From 5b3d78e058ec2991979480b1660f4c4afc986bd3 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Thu, 6 Aug 2026 17:12:39 +0800 Subject: [PATCH 01/23] refactor: rename agentpool to wolfharness across repository, package, and CLI --- .agents/skills/sync-acp-spec/SKILL.md | 8 +- .agents/skills/sync-skills-spec/SKILL.md | 14 +- .copier-answers.yml | 8 +- .github/workflows/build.yml | 12 +- .github/workflows/publish_docker.yml | 2 +- .github/workflows/pytest.yml | 2 +- .opencode/agents/review-code.md | 4 +- .opencode/agents/review-docs.md | 8 +- .opencode/agents/review-lead.md | 4 +- .pre-commit-config.yaml | 2 +- AGENTS.md | 38 +- Dockerfile | 2 +- README.md | 44 +- .../docs/ops/acp-handler.md | 4 +- .../docs/ops/opencode-handler.md | 6 +- .../docs/ops/session-pool.md | 44 +- alembic.ini | 2 +- benchmarks/capability_overhead.py | 12 +- compose.yml | 2 +- diagnostic_pool_skills.py | 4 +- distribution/zed/extension.toml | 44 +- docs/adr/eventbus-replay.md | 2 +- docs/adr/lifecycle-analysis.md | 34 +- docs/adr/message-history-api.md | 6 +- docs/adr/message_node_adapter.md | 6 +- docs/adr/permission-question-api.md | 4 +- docs/adr/signal_streaming_adapters.md | 8 +- ...-provider-lifecycle-architecture-design.md | 4 +- ...7-10-agent-pool-backdoor-cleanup-design.md | 6 +- docs/explanation/architectural-patterns.md | 2 +- docs/explanation/capabilities.md | 8 +- docs/explanation/common-patterns.md | 2 +- docs/explanation/extending-agentpool.md | 14 +- docs/explanation/hooks-events.md | 8 +- docs/explanation/import-patterns.md | 6 +- docs/explanation/lifecycle-dimensions.md | 4 +- docs/explanation/mcp-architecture.md | 6 +- docs/explanation/module-structure.md | 16 +- docs/explanation/session-orchestration.md | 2 +- docs/explanation/skills-system.md | 12 +- docs/explanation/special-considerations.md | 2 +- docs/explanation/storage-observability.md | 2 +- docs/explanation/tool-display-capability.md | 10 +- docs/explanation/usage-examples.md | 8 +- docs/explanation/user_interaction.md | 8 +- docs/how-to/advanced/acp-integration.md | 24 +- docs/how-to/advanced/agui-integration.md | 24 +- docs/how-to/advanced/agui_example.yml | 2 +- docs/how-to/advanced/async.md | 2 +- docs/how-to/advanced/db.md | 8 +- docs/how-to/advanced/db_example.yml | 2 +- docs/how-to/advanced/models_example.yml | 2 +- docs/how-to/advanced/system-prompts.md | 10 +- docs/how-to/configuration/builtin-tools.md | 4 +- docs/how-to/configuration/commands.md | 4 +- docs/how-to/configuration/conditions.md | 4 +- docs/how-to/configuration/connections.md | 4 +- docs/how-to/configuration/event-sources.md | 6 +- docs/how-to/configuration/events.md | 4 +- docs/how-to/configuration/hooks.md | 8 +- docs/how-to/configuration/index.md | 26 +- docs/how-to/configuration/knowledge.md | 2 +- docs/how-to/configuration/mcp.md | 4 +- .../configuration/node-types/acp-agents.md | 4 +- docs/how-to/configuration/node-types/agent.md | 4 +- .../configuration/node-types/agui-agents.md | 2 +- .../node-types/claude-code-agents.md | 2 +- .../configuration/node-types/codex-agents.md | 8 +- docs/how-to/configuration/node-types/team.md | 2 +- docs/how-to/configuration/observability.md | 4 +- docs/how-to/configuration/path-resolution.md | 12 +- docs/how-to/configuration/prompt-hubs.md | 4 +- docs/how-to/configuration/prompts.md | 4 +- docs/how-to/configuration/prompts_example.yml | 2 +- docs/how-to/configuration/resources.md | 10 +- docs/how-to/configuration/responses.yml | 2 +- docs/how-to/configuration/session.md | 2 +- docs/how-to/configuration/skill-uri-usage.md | 2 +- docs/how-to/configuration/skills.md | 4 +- docs/how-to/configuration/storage.md | 4 +- docs/how-to/configuration/storage_example.yml | 2 +- docs/how-to/configuration/tasks.md | 2 +- .../configuration/toolsets/code-mode.md | 2 +- docs/how-to/configuration/toolsets/code.md | 6 +- .../how-to/configuration/toolsets/composio.md | 2 +- .../configuration/toolsets/config-creation.md | 6 +- docs/how-to/configuration/toolsets/custom.md | 4 +- .../configuration/toolsets/entry-points.md | 2 +- .../configuration/toolsets/execution.md | 6 +- .../configuration/toolsets/file-access.md | 6 +- .../configuration/toolsets/import-tools.md | 2 +- .../configuration/toolsets/notifications.md | 6 +- docs/how-to/configuration/toolsets/openapi.md | 2 +- .../toolsets/remote-code-mode.md | 2 +- docs/how-to/configuration/toolsets/search.md | 6 +- docs/how-to/configuration/toolsets/skills.md | 4 +- .../how-to/configuration/toolsets/subagent.md | 6 +- .../toolsets/user-interaction.md | 2 +- docs/how-to/configuration/toolsets/vfs.md | 6 +- docs/how-to/configuration/toolsets/workers.md | 4 +- docs/how-to/programmatic-usage/injection.md | 2 +- .../programmatic-usage/run-interface.md | 2 +- .../programmatic-usage/task-monitoring.md | 2 +- docs/how-to/programmatic-usage/team.md | 4 +- docs/how-to/programmatic-usage/tools.md | 2 +- docs/how-to/servers/acp-server.md | 8 +- docs/how-to/servers/agui-server.md | 8 +- docs/how-to/servers/index.md | 20 +- docs/how-to/servers/mcp-server.md | 18 +- docs/how-to/servers/openai-api-server.md | 12 +- docs/how-to/servers/opencode-server.md | 8 +- docs/index.md | 4 +- docs/records/acp-protocol-gap-analysis.md | 46 +- .../bugs/BUG-001-run-stream-break-error.md | 20 +- docs/reference/acp-meta-field-reference.md | 2 +- docs/reference/api/agent.md | 2 +- docs/reference/api/agentpool.md | 2 +- docs/reference/api/eventbus.md | 4 +- docs/reference/api/messagenode.md | 2 +- docs/reference/cli/add.md | 4 +- docs/reference/cli/config.md | 54 +- docs/reference/cli/history.md | 4 +- docs/reference/cli/index.md | 50 +- docs/reference/cli/list.md | 4 +- docs/reference/cli/run.md | 12 +- docs/reference/cli/serve-acp.md | 8 +- docs/reference/cli/serve-agui.md | 4 +- docs/reference/cli/serve-api.md | 4 +- docs/reference/cli/serve-mcp.md | 4 +- docs/reference/cli/serve-opencode.md | 4 +- docs/reference/cli/set.md | 4 +- docs/reference/cli/task.md | 4 +- docs/reference/cli/watch.md | 4 +- docs/reference/core-concepts/agent-pool.md | 2 +- docs/reference/core-concepts/basic-agent.md | 2 +- docs/reference/core-concepts/events.md | 4 +- docs/reference/core-concepts/mcp.md | 2 +- docs/reference/js/run_code_config.js | 2 +- .../rfcs/RFC-0034-background-task-redesign.md | 128 +- .../accepted/RFC-0001-unified-run-tracking.md | 2 +- ...022-opencode-v144-global-event-protocol.md | 16 +- ...RFC-0015-multiple-questions-elicitation.md | 26 +- .../draft/RFC-0016-skill-slash-commands.md | 82 +- ...RFC-0017-opencode-command-skill-support.md | 6 +- .../draft/RFC-0018-simulation-framework.md | 18 +- ...0019-mcp-server-display-name-separation.md | 12 +- .../RFC-0024-agent-stateless-refactor.md | 16 +- .../RFC-0025-shared-agent-architecture.md | 12 +- ...RFC-0027-acp-subagent-zed-compatibility.md | 16 +- ...-delegation-provider-session-adaptation.md | 96 +- ...agent-reactivation-pending-prompt-queue.md | 10 +- ...acp-streamable-http-websocket-transport.md | 16 +- ...-0032-acp-slash-commands-session-update.md | 12 +- ...0034-acp-session-config-options-unified.md | 30 +- .../RFC-0034-background-task-redesign.md | 130 +- ...0034-per-session-todo-tracker-isolation.md | 26 +- ...-mcp-over-acp-complete-connection-chain.md | 28 +- .../RFC-0036-mcp-over-acp-test-coverage.md | 14 +- .../RFC-0038-eliminate-pool-level-agents.md | 4 +- ...-0039-acp-subagent-zed-protocol-upgrade.md | 22 +- .../draft/RFC-0041-loop-run-separation.md | 8 +- ...RFC-0042-unified-lifecycle-architecture.md | 10 +- ...50-agentwolf-v1-foundation-architecture.md | 20 +- .../RFC-0052-restore-skill-capabilities.md | 2 +- .../RFC-0053-pre-m4-protocol-debt-cleanup.md | 12 +- docs/rfcs/draft/RFC-0055-dynamic-team-mode.md | 20 +- ...C-0001-workers-teams-session-management.md | 12 +- .../RFC-0002-extended-tool-definition.md | 4 +- ...antic-ai-history-processors-integration.md | 32 +- .../RFC-0008-dynamic-skills-injection.md | 58 +- .../RFC-0014-spawn-session-events.md | 10 +- .../RFC-0020-mcp-skills-resources-provider.md | 68 +- ...-0021-agent-concurrent-execution-safety.md | 34 +- ...C-0023-session-scoped-interrupt-routing.md | 14 +- .../RFC-0026-per-session-agent-isolation.md | 12 +- ...FC-0031-acp-per-session-agent-isolation.md | 18 +- .../RFC-0033-mcp-over-acp-transport.md | 78 +- .../RFC-0054-v2-message-id-infrastructure.md | 6 +- docs/tool-display-capability.example.yaml | 2 +- docs/tutorials/agent-usage.md | 2 +- docs/tutorials/basic-concepts.md | 2 +- docs/tutorials/dependencies.md | 4 +- docs/tutorials/example-configurations.md | 4 +- docs/tutorials/example_simple.yml | 2 +- docs/tutorials/example_structured.yml | 2 +- docs/tutorials/example_tools.yml | 2 +- .../tutorials/examples/create_docs/config.yml | 2 +- docs/tutorials/examples/create_docs/index.md | 12 +- docs/tutorials/examples/create_docs/main.py | 10 +- .../tutorials/examples/crewai_flow/config.yml | 2 +- docs/tutorials/examples/crewai_flow/index.md | 10 +- docs/tutorials/examples/crewai_flow/main.py | 8 +- .../examples/download_agents/config.yml | 4 +- .../examples/download_agents/index.md | 16 +- .../examples/download_agents/main.py | 12 +- .../examples/download_workers/config.yml | 2 +- .../examples/download_workers/index.md | 8 +- .../examples/download_workers/main.py | 6 +- .../examples/human_interaction/config.yml | 2 +- .../examples/human_interaction/index.md | 8 +- .../examples/human_interaction/main.py | 6 +- docs/tutorials/examples/index.md | 2 +- .../examples/mcp_sampling_elicitation/demo.py | 6 +- .../mcp_sampling_elicitation/index.md | 6 +- .../examples/mcp_servers_yaml/config.yml | 2 +- .../examples/mcp_servers_yaml/index.md | 14 +- .../examples/mcp_servers_yaml/main_py.py | 6 +- .../examples/mcp_servers_yaml/main_yaml.py | 6 +- docs/tutorials/examples/mcp_skills/config.yml | 2 +- docs/tutorials/examples/mcp_skills/index.md | 8 +- .../examples/model_comparison/config.yml | 2 +- .../examples/model_comparison/index.md | 8 +- .../examples/model_comparison/main.py | 6 +- .../examples/pytest_style/config.yml | 2 +- docs/tutorials/examples/pytest_style/index.md | 10 +- docs/tutorials/examples/pytest_style/main.py | 8 +- .../tutorials/examples/round_robin/config.yml | 2 +- docs/tutorials/examples/round_robin/index.md | 8 +- docs/tutorials/examples/round_robin/main.py | 6 +- .../examples/skill_uri_loading/config.yml | 2 +- .../examples/skill_uri_loading/index.md | 10 +- .../examples/skill_with_references/config.yml | 2 +- .../examples/skill_with_references/index.md | 8 +- .../examples/structured_response/config.yml | 2 +- .../examples/structured_response/index.md | 8 +- .../examples/structured_response/main.py | 6 +- docs/tutorials/index.md | 8 +- docs/tutorials/quickstart.md | 16 +- duties.py | 16 +- examples/__init__.py | 2 +- examples/a2a_server_example.py | 4 +- examples/aggregating_server_with_agui.py | 4 +- examples/agui_server_example.py | 4 +- examples/codex_with_mcp_injection.py | 8 +- examples/ctx_zip_style/http_approach.py | 8 +- examples/ctx_zip_style/mcp_approach.py | 4 +- examples/opencode_question_demo.py | 6 +- inspect_mcp.py | 4 +- migrations/README_USAGE.md | 8 +- migrations/__init__.py | 2 +- migrations/env.py | 4 +- .../2f915b1f62bd_add_title_to_conversation.py | 2 +- ...1_initial_migration_with_renamed_token_.py | 2 +- migrations/versions/__init__.py | 2 +- mkdocs.yml | 12 +- .../.openspec.yaml | 2 + .../rename-agentpool-to-wolfharness/README.md | 3 + .../rename-agentpool-to-wolfharness/design.md | 70 + .../proposal.md | 38 + .../specs/wolfharness-rename/spec.md | 34 + .../rename-agentpool-to-wolfharness/tasks.md | 48 + .../specs/acp-single-execution-path/spec.md | 2 +- openspec/specs/graph-visualization/spec.md | 2 +- openspec/specs/ws-server-integration/spec.md | 8 +- pyproject.toml | 254 +- reproduce_skill_bug.py | 8 +- ruff.toml | 16 +- schema/config-schema.json | 94 +- scripts/add_frontmatter.py | 2 +- scripts/build_mcp_registry_index.py | 2 +- scripts/check_union_docs.py | 6 +- scripts/diagnose_skills.py | 20 +- scripts/generate_mcp_registry_models.py | 4 +- scripts/generate_openresponses_models.py | 4 +- scripts/generate_schema.py | 4 +- scripts/qa_test_server.py | 16 +- ..._agentwolf.py => rename_to_wolfharness.py} | 75 +- scripts/restart_opencode_server.sh | 6 +- scripts/tts_performance.py | 8 +- src/acp/AGENTS.md | 4 +- src/acp/agent/acp_agent_api.py | 2 +- src/acp/agent/notifications.py | 2 +- src/acp/bridge/__main__.py | 4 +- src/acp/bridge/ws_server.py | 2 +- src/acp/bridge/ws_server_cli.py | 8 +- src/acp/filesystem.py | 2 +- src/agentpool/__main__.py | 7 - src/agentpool/agents/__init__.py | 31 - src/agentpool/delegation/__init__.py | 6 - src/agentpool/hooks/__init__.py | 20 - src/agentpool/mcp_server/__init__.py | 6 - src/agentpool/models/__init__.py | 20 - src/agentpool/running/__init__.py | 20 - src/agentpool/skills/__init__.py | 6 - src/agentpool/talk/__init__.py | 13 - src/agentpool_bot/__init__.py | 3 - src/agentpool_commands/base.py | 12 - src/agentpool_server/__init__.py | 9 - src/agentpool_server/acp_server/__init__.py | 24 - .../opencode_server/routes/__init__.py | 29 - .../file_provider/__init__.py | 7 - .../memory_provider/__init__.py | 7 - src/agentpool_toolsets/__init__.py | 13 - src/agentpool_toolsets/builtin/__init__.py | 23 - src/{agentpool => wolfharness}/AGENTS.md | 12 +- src/{agentpool => wolfharness}/__init__.py | 32 +- src/wolfharness/__main__.py | 7 + src/wolfharness/agents/__init__.py | 31 + .../agents/acp_agent/__init__.py | 0 .../agents/acp_agent/acp_agent.py | 80 +- .../agents/acp_agent/acp_converters.py | 24 +- .../agents/acp_agent/client_handler.py | 20 +- .../agents/acp_agent/helpers.py | 2 +- .../agents/acp_agent/modes.py | 6 +- .../agents/acp_agent/session_state.py | 2 +- .../agents/acp_agent/turn.py | 36 +- .../agents/base_agent.py | 102 +- .../agents/context.py | 42 +- .../agents/events/__init__.py | 0 .../agents/events/builtin_handlers.py | 10 +- .../agents/events/event_emitter.py | 16 +- .../agents/events/events.py | 10 +- .../agents/events/infer_info.py | 6 +- .../agents/events/processors.py | 4 +- .../agents/events/tts_handlers.py | 8 +- .../agents/exceptions.py | 0 .../agents/interactions.py | 22 +- .../agents/modes.py | 2 +- .../agents/native_agent/__init__.py | 0 .../agents/native_agent/agent.py | 172 +- .../agents/native_agent/approval_bridge.py | 6 +- .../agents/native_agent/checkpoint.py | 12 +- .../agents/native_agent/deferred_bridge.py | 6 +- .../agents/native_agent/elicitation_bridge.py | 12 +- .../native_agent/elicitation_strategy.py | 4 +- .../agents/native_agent/helpers.py | 14 +- .../agents/native_agent/hook_manager.py | 8 +- .../process_history_capability.py | 3 +- .../agents/native_agent/tool_intercept.py | 14 +- .../agents/native_agent/tool_wrapping.py | 12 +- .../agents/native_agent/turn.py | 34 +- .../agents/prompt_injection.py | 2 +- .../agents/staged_content.py | 2 +- .../agents/sys_prompts.py | 10 +- .../agents/tool_call_accumulator.py | 0 .../auth/__init__.py | 0 .../auth/anthropic_auth.py | 0 .../capabilities/AGENTS.md | 2 +- .../capabilities/__init__.py | 0 .../capabilities/agent_context.py | 18 +- .../capabilities/background_task/__init__.py | 8 +- .../background_task/capability.py | 28 +- .../capabilities/background_task/manager.py | 2 +- .../background_task/notification.py | 2 +- .../capabilities/background_task/types.py | 4 +- .../capabilities/change_event.py | 0 .../capabilities/code_mode_capability.py | 6 +- .../capabilities/codemode/__init__.py | 0 .../capabilities/codemode/default_prompt.py | 0 .../capabilities/codemode/helpers.py | 6 +- .../capabilities/combined_toolset.py | 6 +- .../capabilities/dcp/__init__.py | 6 +- .../capabilities/dcp/block_store.py | 2 +- .../capabilities/dcp/capability.py | 20 +- .../capabilities/dcp/config.py | 2 +- .../capabilities/dcp/nudge.py | 10 +- .../capabilities/dcp/prunable_list.py | 8 +- .../capabilities/dcp/state.py | 2 +- .../capabilities/dcp/strategies.py | 6 +- .../capabilities/dcp/token_utils.py | 0 .../capabilities/dcp/tools.py | 6 +- .../capabilities/dcp/watermark.py | 4 +- .../capabilities/delegation.py | 0 .../capabilities/extension_registry.py | 26 +- .../capabilities/file_team_state.py | 0 .../capabilities/filtered_toolset.py | 6 +- .../capabilities/function_toolset.py | 32 +- .../capabilities/loop_detection.py | 2 +- .../capabilities/mcp_server_cap.py | 14 +- .../capabilities/memory.py | 0 .../capabilities/modality_filter.py | 8 +- .../capabilities/modality_utils.py | 0 .../capabilities/question.py | 18 +- .../capabilities/registry.py | 8 +- .../capabilities/resource_capability.py | 14 +- .../capabilities/resource_protocols.py | 6 +- .../capabilities/resource_resolver.py | 8 +- .../capabilities/runloop_delegation.py | 12 +- .../capabilities/skill_manager_cap.py | 45 +- .../capabilities/skill_watcher.py | 6 +- .../capabilities/subagent_capability.py | 10 +- .../capabilities/team_comm_capability.py | 52 +- .../capabilities/token_budget.py | 0 .../capabilities/tool_display_capability.py | 12 +- .../capabilities/tool_output_budget.py | 0 .../capabilities/viking/__init__.py | 38 +- .../capabilities/viking/compaction.py | 0 .../capabilities/viking/identity.py | 0 .../capabilities/viking/ingest.py | 2 +- .../capabilities/viking/instructions.py | 0 .../capabilities/viking/profile.py | 2 +- .../capabilities/viking/recall.py | 2 +- .../capabilities/viking/tools.py | 4 +- .../capabilities/viking/utils.py | 0 .../commands/__init__.py | 4 +- .../commands/base.py | 4 +- .../common_types.py | 8 +- .../config_resources/__init__.py | 2 +- .../config_resources/acp_assistant.yml | 2 +- .../config_resources/agents.yml | 2 +- .../config_resources/agents_template.yml | 2 +- .../config_resources/claude_style_subagent.md | 0 .../config_resources/external_acp_agents.yml | 2 +- .../opencode_style_subagent.md | 0 .../config_resources/tts_test_agents.yml | 4 +- src/wolfharness/delegation/__init__.py | 6 + .../delegation/base_team.py | 80 +- .../delegation/graph_team.py | 10 +- .../delegation/message_flow_tracker.py | 4 +- .../delegation/pool.py | 84 +- .../diagnostics/__init__.py | 6 +- .../diagnostics/lsp_manager.py | 8 +- .../diagnostics/lsp_proxy.py | 0 .../diagnostics/lsp_proxy_script.py | 0 .../diagnostics/models.py | 0 .../docs/__init__.py | 2 +- .../docs/gen_examples.py | 2 +- src/{agentpool => wolfharness}/docs/utils.py | 14 +- .../functional/__init__.py | 4 +- .../functional/py.typed | 0 .../functional/run.py | 6 +- .../functional/structure.py | 6 +- .../hooks/AGENTS.md | 0 src/wolfharness/hooks/__init__.py | 20 + .../hooks/agent_hooks.py | 6 +- src/{agentpool => wolfharness}/hooks/base.py | 2 +- .../hooks/callable.py | 8 +- .../hooks/command.py | 6 +- .../hooks/prompt.py | 8 +- .../host/__init__.py | 0 .../host/context.py | 24 +- .../host/factory.py | 90 +- .../host/registry.py | 2 +- src/{agentpool => wolfharness}/host/stubs.py | 2 +- .../jinja_filters.py | 2 +- .../lifecycle/AGENTS.md | 2 +- .../lifecycle/__init__.py | 16 +- .../lifecycle/comm_channel.py | 10 +- .../lifecycle/event_transport.py | 2 +- .../lifecycle/factory.py | 18 +- .../lifecycle/journal.py | 8 +- .../lifecycle/protocols.py | 2 +- .../lifecycle/snapshot_store.py | 4 +- .../lifecycle/triggers.py | 6 +- .../lifecycle/types.py | 0 src/{agentpool => wolfharness}/log.py | 8 +- src/wolfharness/mcp_server/__init__.py | 6 + .../mcp_server/client.py | 28 +- .../mcp_server/config_snapshot.py | 2 +- .../mcp_server/constants.py | 0 .../mcp_server/conversions.py | 4 +- .../mcp_server/global_pool.py | 4 +- .../mcp_server/helpers.py | 2 +- .../mcp_server/manager.py | 32 +- .../mcp_server/message_handler.py | 8 +- .../mcp_server/registries/__init__.py | 0 .../registries/official_registry_client.py | 0 .../mcp_server/registries/pulsemcp_client.py | 0 .../mcp_server/session_pool.py | 14 +- .../mcp_server/session_stream_pair.py | 0 .../mcp_server/tool_bridge.py | 30 +- .../messaging/__init__.py | 14 +- .../messaging/chat_filesystem.py | 2 +- .../messaging/compaction.py | 8 +- .../messaging/connection_manager.py | 14 +- .../messaging/context.py | 16 +- .../messaging/event_manager.py | 10 +- .../messaging/events.py | 6 +- .../messaging/graph_adapter.py | 4 +- .../messaging/message_container.py | 16 +- .../messaging/message_history.py | 28 +- .../messaging/message_utils.py | 4 +- .../messaging/messagenode.py | 58 +- .../messaging/messages.py | 16 +- .../messaging/processing.py | 10 +- .../messaging/signal_adapter.py | 10 +- .../messaging/streaming_adapter.py | 6 +- src/{agentpool => wolfharness}/mime_utils.py | 2 +- src/wolfharness/models/__init__.py | 20 + .../models/acp_agents/__init__.py | 2 +- .../models/acp_agents/base.py | 22 +- .../models/agents.py | 80 +- .../models/fields.py | 6 +- .../models/file_agents.py | 0 .../models/file_parsing.py | 10 +- .../models/fixed_args_test_model.py | 0 .../models/manifest.py | 88 +- .../models/model_configs.py | 18 +- .../models/pending_interaction.py | 0 .../observability/__init__.py | 2 +- .../observability/observability_registry.py | 4 +- .../observability/spans.py | 2 +- .../observability/trace.py | 0 .../orchestrator/AGENTS.md | 0 .../orchestrator/__init__.py | 12 +- .../orchestrator/core.py | 8 +- .../orchestrator/event_bus.py | 6 +- .../orchestrator/event_mapper.py | 6 +- .../orchestrator/metrics.py | 34 +- .../orchestrator/run.py | 42 +- .../orchestrator/runtime_registry.py | 2 +- .../orchestrator/session_controller.py | 34 +- .../orchestrator/session_controller_agent.py | 24 +- .../orchestrator/session_controller_close.py | 16 +- .../orchestrator/session_controller_runs.py | 32 +- .../orchestrator/session_pool.py | 50 +- .../orchestrator/session_pool_config.py | 0 .../orchestrator/session_pool_messaging.py | 30 +- .../orchestrator/session_pool_runs.py | 24 +- .../orchestrator/session_pool_teams.py | 12 +- .../orchestrator/turn.py | 12 +- .../prompts/__init__.py | 0 .../prompts/base.py | 0 .../prompts/builtin_provider.py | 6 +- .../prompts/conversion_manager.py | 6 +- .../prompts/convert.py | 8 +- .../prompts/instructions.py | 2 +- .../prompts/manager.py | 10 +- .../prompts/parts/zed.md | 4 +- .../prompts/prompts.py | 14 +- src/{agentpool => wolfharness}/py.typed | 0 .../repomap/__init__.py | 14 +- .../repomap/context.py | 12 +- .../repomap/core.py | 12 +- .../repomap/languages.py | 4 +- .../repomap/outline.py | 4 +- .../tree-sitter-language-pack/README.md | 0 .../arduino-tags.scm | 0 .../tree-sitter-language-pack/c-tags.scm | 0 .../chatito-tags.scm | 0 .../clojure-tags.scm | 0 .../commonlisp-tags.scm | 0 .../tree-sitter-language-pack/cpp-tags.scm | 0 .../tree-sitter-language-pack/csharp-tags.scm | 0 .../tree-sitter-language-pack/d-tags.scm | 0 .../tree-sitter-language-pack/dart-tags.scm | 0 .../tree-sitter-language-pack/elisp-tags.scm | 0 .../tree-sitter-language-pack/elixir-tags.scm | 0 .../tree-sitter-language-pack/elm-tags.scm | 0 .../tree-sitter-language-pack/gleam-tags.scm | 0 .../tree-sitter-language-pack/go-tags.scm | 0 .../tree-sitter-language-pack/java-tags.scm | 0 .../javascript-tags.scm | 0 .../tree-sitter-language-pack/lua-tags.scm | 0 .../tree-sitter-language-pack/matlab-tags.scm | 0 .../tree-sitter-language-pack/ocaml-tags.scm | 0 .../ocaml_interface-tags.scm | 0 .../tree-sitter-language-pack/pony-tags.scm | 0 .../properties-tags.scm | 0 .../tree-sitter-language-pack/python-tags.scm | 0 .../tree-sitter-language-pack/r-tags.scm | 0 .../tree-sitter-language-pack/racket-tags.scm | 0 .../tree-sitter-language-pack/ruby-tags.scm | 0 .../tree-sitter-language-pack/rust-tags.scm | 0 .../solidity-tags.scm | 0 .../tree-sitter-language-pack/swift-tags.scm | 0 .../tree-sitter-language-pack/udev-tags.scm | 0 .../queries/tree-sitter-languages/README.md | 0 .../queries/tree-sitter-languages/c-tags.scm | 0 .../tree-sitter-languages/c_sharp-tags.scm | 0 .../tree-sitter-languages/cpp-tags.scm | 0 .../tree-sitter-languages/dart-tags.scm | 0 .../tree-sitter-languages/elisp-tags.scm | 0 .../tree-sitter-languages/elixir-tags.scm | 0 .../tree-sitter-languages/elm-tags.scm | 0 .../tree-sitter-languages/fortran-tags.scm | 0 .../queries/tree-sitter-languages/go-tags.scm | 0 .../tree-sitter-languages/haskell-tags.scm | 0 .../tree-sitter-languages/hcl-tags.scm | 0 .../tree-sitter-languages/java-tags.scm | 0 .../tree-sitter-languages/javascript-tags.scm | 0 .../tree-sitter-languages/julia-tags.scm | 0 .../tree-sitter-languages/kotlin-tags.scm | 0 .../tree-sitter-languages/matlab-tags.scm | 0 .../tree-sitter-languages/ocaml-tags.scm | 0 .../ocaml_interface-tags.scm | 0 .../tree-sitter-languages/php-tags.scm | 0 .../tree-sitter-languages/python-tags.scm | 0 .../queries/tree-sitter-languages/ql-tags.scm | 0 .../tree-sitter-languages/ruby-tags.scm | 0 .../tree-sitter-languages/rust-tags.scm | 0 .../tree-sitter-languages/scala-tags.scm | 0 .../tree-sitter-languages/typescript-tags.scm | 0 .../tree-sitter-languages/zig-tags.scm | 0 .../repomap/tags.py | 2 +- .../repomap/types.py | 0 .../repomap/utils.py | 0 src/wolfharness/running/__init__.py | 20 + .../running/decorators.py | 4 +- .../running/discovery.py | 6 +- .../running/executor.py | 8 +- .../running/injection.py | 8 +- .../running/py.typed | 0 .../running/run_nodes.py | 6 +- src/{agentpool => wolfharness}/server.py | 4 +- .../sessions/__init__.py | 6 +- .../sessions/models.py | 4 +- .../sessions/state_mapper.py | 6 +- .../skills/AGENTS.md | 2 +- src/wolfharness/skills/__init__.py | 6 + .../skills/command.py | 2 +- .../skills/exceptions.py | 2 +- .../skills/manager.py | 10 +- .../skills/registry.py | 10 +- .../skills/skill.py | 2 +- .../skills/skill_tool_manager.py | 4 +- .../skills/uri_resolver.py | 10 +- .../storage/__init__.py | 4 +- .../storage/manager.py | 26 +- .../storage/serialization.py | 4 +- src/wolfharness/talk/__init__.py | 13 + .../talk/graph_edges.py | 22 +- .../talk/registry.py | 14 +- src/{agentpool => wolfharness}/talk/stats.py | 6 +- src/{agentpool => wolfharness}/talk/talk.py | 38 +- .../tasks/__init__.py | 4 +- .../tasks/exceptions.py | 2 +- .../tasks/registry.py | 8 +- src/{agentpool => wolfharness}/testing.py | 14 +- .../text_templates/__init__.py | 2 +- .../text_templates/system_prompt.jinja | 0 .../text_templates/tool_call_default.jinja | 0 .../text_templates/tool_call_markdown.jinja | 0 .../text_templates/tool_call_simple.jinja | 0 .../tool_impls/__init__.py | 0 .../tool_impls/agent_cli/__init__.py | 4 +- .../tool_impls/agent_cli/tool.py | 4 +- .../tool_impls/bash/__init__.py | 4 +- .../tool_impls/bash/helpers.py | 0 .../tool_impls/bash/tool.py | 8 +- .../tool_impls/delete_path/__init__.py | 4 +- .../tool_impls/delete_path/tool.py | 6 +- .../tool_impls/download_file/__init__.py | 4 +- .../tool_impls/download_file/tool.py | 6 +- .../tool_impls/execute_code/__init__.py | 4 +- .../tool_impls/execute_code/tool.py | 6 +- .../tool_impls/grep/__init__.py | 4 +- .../tool_impls/grep/tool.py | 12 +- .../tool_impls/list_directory/__init__.py | 4 +- .../tool_impls/list_directory/tool.py | 10 +- .../tool_impls/question/__init__.py | 4 +- .../tool_impls/question/tool.py | 6 +- .../tool_impls/read/__init__.py | 8 +- .../tool_impls/read/tool.py | 20 +- .../tools/__init__.py | 8 +- src/{agentpool => wolfharness}/tools/base.py | 20 +- .../tools/exceptions.py | 2 +- .../tools/tool_call_info.py | 6 +- .../tools/tool_wrapping.py | 6 +- src/{agentpool => wolfharness}/ui/__init__.py | 2 +- src/{agentpool => wolfharness}/ui/base.py | 4 +- .../ui/elicitation.py | 4 +- .../ui/mock_provider.py | 6 +- .../ui/stdlib_provider.py | 10 +- .../utils/__init__.py | 6 +- .../utils/baseregistry.py | 0 .../utils/context_wrapping.py | 8 +- .../utils/count_tokens.py | 0 src/{agentpool => wolfharness}/utils/dag.py | 0 src/{agentpool => wolfharness}/utils/diffs.py | 0 .../utils/file_watcher.py | 2 +- .../utils/identifiers.py | 2 +- .../utils/importing.py | 0 .../utils/inspection.py | 6 +- .../utils/model_capabilities.py | 0 .../utils/model_helpers.py | 4 +- .../utils/network.py | 0 .../utils/parse_time.py | 0 .../utils/pydantic_ai_helpers.py | 2 +- .../utils/result_utils.py | 2 +- .../utils/signatures.py | 4 +- .../utils/streams.py | 2 +- .../utils/subprocess_utils.py | 2 +- .../utils/task_group.py | 2 +- src/{agentpool => wolfharness}/utils/tasks.py | 6 +- .../utils/thread_helpers.py | 0 .../utils/time_utils.py | 0 src/{agentpool => wolfharness}/utils/todos.py | 0 .../utils/token_breakdown.py | 4 +- .../utils/tool_schema.py | 0 src/{agentpool => wolfharness}/utils/usage.py | 0 .../utils/worktree.py | 2 +- .../vfs_registry.py | 6 +- src/wolfharness_bot/__init__.py | 3 + src/{agentpool_bot => wolfharness_bot}/bus.py | 2 +- .../channels/__init__.py | 4 +- .../channels/base.py | 8 +- .../channels/discord.py | 16 +- .../channels/email.py | 12 +- .../channels/manager.py | 8 +- .../channels/slack.py | 10 +- .../channels/telegram.py | 14 +- .../channels/whatsapp.py | 8 +- .../config.py | 24 +- .../cron/__init__.py | 4 +- .../cron/cron_types.py | 0 .../cron/service.py | 8 +- .../heartbeat.py | 4 +- .../message_tools.py | 10 +- .../transcription.py | 2 +- .../__init__.py | 4 +- .../__main__.py | 36 +- .../agent.py | 16 +- .../cli_types.py | 0 .../common.py | 8 +- .../config_info.py | 12 +- .../history.py | 44 +- src/{agentpool_cli => wolfharness_cli}/log.py | 4 +- .../py.typed | 0 src/{agentpool_cli => wolfharness_cli}/run.py | 10 +- .../serve_acp.py | 32 +- .../serve_agui.py | 10 +- .../serve_api.py | 10 +- .../serve_mcp.py | 18 +- .../serve_opencode.py | 30 +- .../serve_vercel.py | 12 +- .../store.py | 4 +- .../task.py | 16 +- src/{agentpool_cli => wolfharness_cli}/ui.py | 50 +- .../utils.py | 0 .../watch.py | 6 +- .../__init__.py | 28 +- .../agents.py | 14 +- src/wolfharness_commands/base.py | 12 + .../commands.py | 8 +- .../completers.py | 6 +- .../connections.py | 10 +- .../history.py | 6 +- .../markdown_utils.py | 0 .../mcp.py | 20 +- .../models.py | 12 +- .../pool.py | 16 +- .../prompts.py | 6 +- .../py.typed | 0 .../read.py | 10 +- .../resources.py | 10 +- .../session.py | 4 +- .../text_sharing/__init__.py | 12 +- .../text_sharing/base.py | 2 +- .../text_sharing/github_gist.py | 4 +- .../text_sharing/opencode.py | 14 +- .../text_sharing/paste_rs.py | 4 +- .../text_sharing/pastebin.py | 4 +- .../text_sharing/shittycodingagent.py | 4 +- .../tools.py | 26 +- .../utils.py | 10 +- .../workers.py | 12 +- .../__init__.py | 36 +- .../builtin_tools.py | 2 +- .../capabilities.py | 36 +- .../commands.py | 0 .../compaction.py | 30 +- .../conditions.py | 8 +- .../context.py | 4 +- .../converters.py | 0 .../durable.py | 2 +- .../event_handlers.py | 12 +- .../events.py | 2 +- .../forward_targets.py | 18 +- .../graph_config.py | 4 +- .../graph_translation.py | 6 +- .../hook_conditions.py | 0 .../hooks.py | 12 +- .../instructions.py | 0 .../jinja.py | 2 +- .../knowledge.py | 6 +- .../lifecycle.py | 0 .../loaders.py | 6 +- .../mcp_server.py | 0 .../model_capabilities.py | 0 .../nodes.py | 22 +- .../observability.py | 2 +- .../output_types.py | 2 +- .../paths.py | 2 +- .../pool_server.py | 0 .../prompt_hubs.py | 16 +- .../prompts.py | 2 +- .../py.typed | 0 .../resolution.py | 18 +- .../session.py | 4 +- .../session_pool.py | 2 +- .../skill_commands.py | 0 .../skills.py | 6 +- .../storage.py | 20 +- .../system_prompts.py | 6 +- .../task.py | 18 +- .../team_mode.py | 0 .../teams.py | 4 +- .../tools.py | 6 +- .../toolsets.py | 80 +- .../wolfharness_tools.py} | 30 +- .../workers.py | 0 .../__init__.py | 0 .../braintrust_hub.py | 6 +- .../fabric.py | 6 +- .../langfuse_hub.py | 6 +- .../promptlayer_provider.py | 6 +- .../py.typed | 0 .../AGENTS.md | 6 +- src/wolfharness_server/__init__.py | 9 + .../a2a_server/__init__.py | 0 .../a2a_server/a2a_types.py | 0 .../a2a_server/agent_worker.py | 2 +- .../a2a_server/server.py | 14 +- .../a2a_server/storage.py | 6 +- src/wolfharness_server/acp_server/__init__.py | 24 + .../acp_server/acp_agent.py | 56 +- .../acp_server/acp_mcp_manager.py | 4 +- .../acp_server/acp_mcp_transport.py | 4 +- .../acp_server/commands/__init__.py | 0 .../acp_server/commands/debug_commands.py | 12 +- .../commands/docs_commands/__init__.py | 0 .../commands/docs_commands/fetch_repo.py | 8 +- .../commands/docs_commands/get_schema.py | 8 +- .../commands/docs_commands/get_source.py | 12 +- .../commands/docs_commands/git_diff.py | 8 +- .../commands/docs_commands/helpers.py | 0 .../commands/docs_commands/url_to_markdown.py | 8 +- .../acp_server/commands/skill_commands.py | 6 +- .../acp_server/converters.py | 16 +- .../acp_server/event_converter.py | 10 +- .../acp_server/handler.py | 28 +- .../acp_server/input_provider.py | 12 +- .../acp_server/provider_router.py | 6 +- .../acp_server/server.py | 24 +- .../acp_server/session.py | 34 +- .../acp_server/session_agent_mgmt.py | 14 +- .../acp_server/session_events.py | 10 +- .../acp_server/session_lifecycle.py | 26 +- .../acp_server/session_manager.py | 26 +- .../acp_server/syntax_detection.py | 0 .../acp_server/zed_tools.md | 0 .../aggregating_server.py | 6 +- .../agui_server/__init__.py | 6 +- .../agui_server/base_agent_adapter.py | 4 +- .../agui_server/server.py | 16 +- .../agui_server/skill_tools.py | 4 +- .../base.py | 6 +- .../http_server.py | 14 +- .../mcp_server/__init__.py | 2 +- .../mcp_server/server.py | 24 +- .../mcp_server/zed_wrapper.py | 8 +- .../mixins.py | 8 +- .../openai_api_server/__init__.py | 0 .../openai_api_server/completions/__init__.py | 0 .../openai_api_server/completions/helpers.py | 10 +- .../openai_api_server/completions/models.py | 4 +- .../openai_api_server/responses/__init__.py | 0 .../openai_api_server/responses/helpers.py | 6 +- .../openai_api_server/responses/models.py | 0 .../openai_api_server/server.py | 28 +- .../opencode_server/.rules | 6 +- .../opencode_server/ENDPOINTS.md | 0 .../OPENCODE_UI_TOOLS_COMPLETE.md | 0 .../opencode_server/__init__.py | 6 +- .../opencode_server/command_validation.py | 0 .../opencode_server/converters.py | 44 +- .../opencode_server/dependencies.py | 4 +- .../opencode_server/event_adapter.py | 12 +- .../opencode_server/event_bridge.py | 10 +- .../opencode_server/event_processor.py | 44 +- .../event_processor_context.py | 16 +- .../opencode_server/input_provider.py | 32 +- .../opencode_server/models/__init__.py | 34 +- .../opencode_server/models/agent.py | 4 +- .../opencode_server/models/app.py | 2 +- .../opencode_server/models/base.py | 0 .../opencode_server/models/common.py | 6 +- .../opencode_server/models/config.py | 4 +- .../opencode_server/models/diagnostics.py | 0 .../opencode_server/models/events.py | 16 +- .../opencode_server/models/file.py | 4 +- .../opencode_server/models/mcp.py | 2 +- .../opencode_server/models/message.py | 8 +- .../opencode_server/models/parts.py | 6 +- .../opencode_server/models/provider.py | 8 +- .../opencode_server/models/pty.py | 2 +- .../opencode_server/models/question.py | 2 +- .../models/question_permission.py | 0 .../opencode_server/models/session.py | 4 +- .../opencode_server/models/session_info.py | 0 .../opencode_server/models/tool_metadata.py | 2 +- .../opencode_server/opencode_event_bridge.py | 34 +- .../opencode_message_bridge.py | 36 +- .../opencode_session_routes.py | 36 +- .../opencode_server/provider_auth.py | 6 +- .../opencode_server/routes/__init__.py | 29 + .../opencode_server/routes/agent_routes.py | 38 +- .../opencode_server/routes/app_routes.py | 12 +- .../opencode_server/routes/config_routes.py | 16 +- .../opencode_server/routes/file_routes.py | 4 +- .../opencode_server/routes/global_routes.py | 18 +- .../opencode_server/routes/lsp_routes.py | 4 +- .../opencode_server/routes/message_routes.py | 38 +- .../routes/permission_routes.py | 6 +- .../opencode_server/routes/pty_routes.py | 8 +- .../opencode_server/routes/question_routes.py | 6 +- .../opencode_server/routes/routing.py | 4 +- .../opencode_server/routes/session_routes.py | 46 +- .../opencode_server/routes/tui_routes.py | 4 +- .../opencode_server/server.py | 46 +- .../session_pool_integration.py | 18 +- .../opencode_server/skill_bridge.py | 4 +- .../opencode_server/state.py | 48 +- .../opencode_server/stream_adapter.py | 24 +- .../opencode_server/todo_utils.py | 2 +- .../py.typed | 0 .../shared/__init__.py | 2 +- .../shared/constants.py | 0 .../shared/model_utils.py | 20 +- .../__init__.py | 8 +- .../acp_provider/__init__.py | 2 +- .../acp_provider/provider.py | 12 +- .../adapter.py | 14 +- .../base.py | 12 +- .../file_provider/__init__.py | 7 + .../file_provider/provider.py | 24 +- .../formatters.py | 2 +- .../memory_provider/__init__.py | 7 + .../memory_provider/provider.py | 18 +- .../models.py | 2 +- .../opencode_provider/ARCHITECTURE.md | 6 +- .../opencode_provider/__init__.py | 2 +- .../opencode_provider/helpers.py | 18 +- .../opencode_provider/provider.py | 41 +- .../project_store.py | 22 +- .../protocols.py | 12 +- .../py.typed | 0 .../sql_provider/__init__.py | 4 +- .../sql_provider/cli.py | 8 +- .../sql_provider/models.py | 6 +- .../sql_provider/queries.py | 0 .../sql_provider/sql_messages.py | 16 +- .../sql_provider/sql_projects.py | 10 +- .../sql_provider/sql_provider.py | 14 +- .../sql_provider/sql_sessions.py | 32 +- .../sql_provider/utils.py | 20 +- .../zed_provider/__init__.py | 2 +- .../zed_provider/helpers.py | 12 +- .../zed_provider/models.py | 0 .../zed_provider/provider.py | 22 +- .../__init__.py | 16 +- .../config.py | 4 +- .../git.py | 0 .../handlers.py | 4 +- .../manager.py | 24 +- .../models.py | 0 .../packages.py | 0 .../parsers/__init__.py | 0 .../parsers/helpers.py | 0 .../parsers/markdown_parser.py | 6 +- .../parsers/protocol.py | 2 +- .../parsers/python_parser.py | 4 +- .../resources.py | 0 src/wolfharness_toolsets/__init__.py | 13 + src/wolfharness_toolsets/builtin/__init__.py | 23 + .../builtin/code.py | 12 +- .../builtin/debug.py | 20 +- .../builtin/execution_environment.py | 12 +- .../builtin/file_edit/__init__.py | 0 .../builtin/file_edit/file_edit.py | 6 +- .../builtin/question_tools.py | 8 +- .../builtin/skills.py | 6 +- .../builtin/subagent_tools.py | 18 +- .../builtin/workers.py | 36 +- .../composio_toolset.py | 8 +- .../config_creation.py | 2 +- .../cron.py | 14 +- .../entry_points.py | 8 +- .../fsspec_toolset/__init__.py | 6 +- .../fsspec_toolset/diagnostics.py | 2 +- .../fsspec_toolset/grep.py | 4 +- .../fsspec_toolset/helpers.py | 2 +- .../fsspec_toolset/image_utils.py | 0 .../fsspec_toolset/streaming_diff_parser.py | 0 .../fsspec_toolset/toolset.py | 70 +- .../mcp_discovery/__init__.py | 2 +- .../mcp_discovery/data/mcp_servers.parquet | Bin .../mcp_discovery/toolset.py | 20 +- .../mcp_run_toolset.py | 6 +- .../notifications.py | 6 +- .../openapi.py | 6 +- .../py.typed | 0 .../search_toolset.py | 12 +- .../streaming_tools.py | 8 +- .../vfs_toolset.py | 4 +- test_graph_config_loading.py | 8 +- test_pool_skills.py | 6 +- tests/AGENTS.md | 8 +- tests/_controller_helpers.py | 4 +- .../acp/test_client_handler_session_update.py | 8 +- tests/acp/test_event_converter_errors.py | 6 +- tests/acp/test_event_converter_snapshots.py | 12 +- tests/acp/test_meta_guardrails.py | 4 +- tests/acp/test_qwen_display_mode.py | 4 +- .../acp/test_subagent_display_mode_coerce.py | 4 +- tests/acp/test_toast_notification.py | 36 +- tests/acp/test_zed_none_child.py | 8 +- tests/acp/test_zed_subagent_spawn.py | 12 +- tests/acp_server/test_acp_skill_commands.py | 8 +- tests/acp_server/test_acp_skill_e2e.py | 18 +- tests/acp_server/test_acp_skill_lifecycle.py | 16 +- .../acp_server/test_acp_mcp_end_to_end.py | 4 +- .../test_acp_mcp_fastmcp_integration.py | 14 +- .../acp_server/test_acp_mcp_manager.py | 2 +- .../acp_server/test_acp_mcp_red_flags.py | 12 +- .../test_acp_mcp_session_cleanup.py | 12 +- .../acp_server/test_acp_mcp_transport.py | 4 +- .../acp_server/test_e2e_session_lifecycle.py | 6 +- .../acp_server/test_resume_active_run.py | 6 +- .../acp_server/test_resume_reconnect.py | 4 +- .../test_resume_session_lifecycle.py | 8 +- .../acp_server/test_websocket_disconnect.py | 2 +- .../test_websocket_disconnect_during_run.py | 8 +- .../shared/test_model_utils.py | 46 +- .../acp_agent/test_acp_agent_load_session.py | 6 +- tests/agents/acp_agent/test_acp_converters.py | 2 +- tests/agents/acp_agent/test_acp_turn_hooks.py | 8 +- tests/agents/acp_agent/test_create_turn.py | 10 +- tests/agents/acp_agent/test_filter_servers.py | 2 +- .../acp_agent/test_load_session_manual.py | 16 +- tests/agents/acp_agent/test_step_usage.py | 10 +- tests/agents/acp_agent/test_turn.py | 6 +- .../agents/acp_agent/test_turn_integration.py | 16 +- tests/agents/events/test_deferred_events.py | 2 +- tests/agents/events/test_event_bus_scopes.py | 12 +- .../agents/events/test_file_ops_message_id.py | 8 +- .../test_user_message_inserted_event.py | 2 +- .../native_agent/test_agent_instructions.py | 6 +- .../native_agent/test_approval_bridge.py | 8 +- .../native_agent/test_capability_chain.py | 28 +- tests/agents/native_agent/test_checkpoint.py | 18 +- .../agents/native_agent/test_confirmation.py | 14 +- .../native_agent/test_deferred_bridge.py | 58 +- .../native_agent/test_deferred_wrap_tool.py | 34 +- .../test_get_agentlet_capabilities.py | 84 +- .../test_inject_prompt_cross_task.py | 14 +- .../native_agent/test_instructions_format.py | 12 +- tests/agents/native_agent/test_interrupt.py | 18 +- .../native_agent/test_native_turn_hooks.py | 10 +- .../test_process_history_capability.py | 2 +- .../native_agent/test_step_error_metadata.py | 2 +- tests/agents/native_agent/test_step_usage.py | 8 +- .../native_agent/test_terminal_events.py | 8 +- .../test_tool_intercept_capability.py | 12 +- tests/agents/native_agent/test_turn.py | 14 +- tests/agents/run_concurrent_tests.py | 6 +- tests/agents/test_agent_basics.py | 4 +- tests/agents/test_base_agent_api.py | 20 +- tests/agents/test_base_agent_run_v2.py | 30 +- tests/agents/test_capability_hooks_graph.py | 4 +- .../test_capability_hooks_standalone.py | 4 +- tests/agents/test_contextvar_concurrency.py | 24 +- tests/agents/test_create_child_session.py | 8 +- tests/agents/test_create_turn.py | 26 +- tests/agents/test_delegation_depth_error.py | 4 +- tests/agents/test_deprecation_warnings.py | 2 +- .../test_external_agent_event_sequence.py | 12 +- tests/agents/test_import_corrections.py | 8 +- ...est_native_agent_streaming_cancellation.py | 10 +- .../test_native_agent_streaming_ordering.py | 4 +- .../test_native_agent_streaming_realtime.py | 4 +- tests/agents/test_run_stream_depth.py | 8 +- tests/agents/test_run_stream_direct_gating.py | 8 +- tests/agents/test_runusage_corrections.py | 2 +- tests/agents/test_session_id_deprecation.py | 2 +- .../agents/test_signatures_context_binding.py | 10 +- tests/benchmark/test_graph_performance.py | 8 +- ...test_background_task_concurrency_stress.py | 18 +- .../integration/test_background_task_e2e.py | 20 +- ...background_task_cancellation_regression.py | 34 +- ...background_task_completion_notification.py | 34 +- .../test_background_task_history_isolation.py | 24 +- .../test_background_task_provider.py | 12 +- ...t_background_task_provider_async_events.py | 32 +- ...test_background_task_provider_execution.py | 33 +- ..._background_task_provider_output_cancel.py | 10 +- .../test_background_output_edge_cases.py | 10 +- .../test_background_task_capability.py | 12 +- .../background_task/test_error_propagation.py | 12 +- .../background_task/test_manager.py | 2 +- .../test_manager_state_and_cleanup.py | 2 +- .../test_notification_batcher.py | 4 +- .../test_run_and_stream_errors.py | 20 +- .../test_session_task_state.py | 6 +- .../background_task/test_types.py | 4 +- tests/capabilities/dcp/test_edge_cases.py | 20 +- tests/capabilities/dcp/test_integration.py | 14 +- tests/capabilities/dcp/test_unit.py | 20 +- tests/capabilities/test_agent_context.py | 24 +- .../test_agent_context_resources_unused.py | 2 +- .../capabilities/test_code_mode_capability.py | 4 +- tests/capabilities/test_combined_toolset.py | 10 +- tests/capabilities/test_extension_registry.py | 22 +- .../test_extension_registry_errors.py | 10 +- .../test_extension_registry_integration.py | 6 +- tests/capabilities/test_filtered_toolset.py | 14 +- tests/capabilities/test_function_toolset.py | 4 +- tests/capabilities/test_mcp_server_cap.py | 20 +- .../capabilities/test_question_capability.py | 4 +- tests/capabilities/test_registry.py | 46 +- .../capabilities/test_resource_capability.py | 14 +- .../test_resource_registration.py | 8 +- tests/capabilities/test_review_fixes.py | 22 +- .../test_runloop_delegation_review.py | 12 +- tests/capabilities/test_runloop_injection.py | 16 +- .../test_session_pool_get_client.py | 16 +- .../test_skill_manager_cap_integration.py | 8 +- .../test_skill_manager_cap_tools.py | 10 +- .../test_skill_tool_registration.py | 14 +- tests/capabilities/test_skill_watcher.py | 2 +- .../capabilities/test_subagent_capability.py | 12 +- .../test_tool_display_capability.py | 4 +- .../test_tool_display_integration.py | 10 +- .../test_tool_display_semantics.py | 4 +- tests/capabilities/test_transport_reuse.py | 14 +- tests/capabilities/viking/test_viking.py | 100 +- tests/capabilities/viking/test_viking_e2e.py | 8 +- .../viking/test_viking_integration.py | 18 +- tests/cli/test_cli.py | 12 +- tests/cli/test_cli_config_path_resolution.py | 10 +- tests/cli/test_commands.py | 4 +- tests/cli/test_serve_acp_streamable_http.py | 38 +- tests/commands/test_commands_integration.py | 6 +- tests/compat/test_compat.py | 10 +- tests/compat/test_no_deprecation_warnings.py | 12 +- tests/config/__init__.py | 2 +- tests/config/test_capabilities_yaml.py | 4 +- tests/config/test_config_resources.py | 4 +- tests/config/test_deferred_wiring.py | 50 +- tests/config/test_durable_config.py | 2 +- tests/config/test_entrypoint_capabilities.py | 8 +- tests/config/test_graph_translation.py | 10 +- tests/config/test_instructions_config.py | 10 +- tests/config/test_mcp_server_config.py | 2 +- tests/config/test_path_resolution.py | 4 +- tests/config/test_skill_commands.py | 2 +- tests/conftest.py | 5 +- tests/data/test_skills/hello-world/SKILL.md | 2 +- .../data/test_skills/test-with-args/SKILL.md | 4 +- tests/delegation/test_base_team_stop.py | 2 +- tests/delegation/test_break_behavior.py | 8 +- .../test_cross_provider_session_lifecycle.py | 32 +- tests/delegation/test_graph_adapter.py | 12 +- tests/delegation/test_graph_edges.py | 10 +- tests/delegation/test_graph_teams.py | 20 +- tests/delegation/test_pool_get_context.py | 6 +- tests/delegation/test_pool_runs.py | 4 +- .../test_pool_session_integration.py | 6 +- tests/delegation/test_pool_skills.py | 10 +- tests/delegation/test_team_member_skills.py | 4 +- tests/e2e/conftest.py | 28 +- tests/e2e/test_acp_concurrent_prompts.py | 10 +- tests/e2e/test_acp_config.py | 4 +- tests/e2e/test_acp_elicitation.py | 2 +- tests/e2e/test_acp_error_paths.py | 6 +- tests/e2e/test_acp_mcp.py | 4 +- tests/e2e/test_acp_permission_flow.py | 2 +- tests/e2e/test_acp_providers.py | 4 +- tests/e2e/test_acp_session_lifecycle.py | 4 +- tests/e2e/test_acp_session_updates.py | 10 +- tests/e2e/test_acp_stop_reasons.py | 6 +- tests/e2e/test_acp_subprocess.py | 8 +- tests/e2e/test_acp_v2_alignment.py | 2 +- tests/e2e/test_acp_ws_transport.py | 2 +- tests/e2e/test_agui_subprocess.py | 4 +- tests/e2e/test_openai_api_subprocess.py | 4 +- .../e2e/test_opencode_agent_skill_command.py | 2 +- tests/e2e/test_opencode_error_paths.py | 2 +- tests/e2e/test_opencode_lock_concurrency.py | 2 +- tests/e2e/test_opencode_mcp.py | 2 +- tests/e2e/test_opencode_message_ops.py | 2 +- tests/e2e/test_opencode_misc.py | 2 +- tests/e2e/test_opencode_multiturn_redflag.py | 4 +- .../e2e/test_opencode_permission_question.py | 2 +- tests/e2e/test_opencode_prompt_delivery.py | 2 +- .../e2e/test_opencode_provider_mode_config.py | 2 +- tests/e2e/test_opencode_session_lifecycle.py | 2 +- tests/e2e/test_opencode_sse_streaming.py | 2 +- tests/e2e/test_opencode_subprocess.py | 4 +- tests/e2e/test_team_mode_smoke.py | 8 +- tests/e2e/test_turn_split_e2e.py | 4 +- tests/e2e/test_user_message_inserted_e2e.py | 8 +- .../e2e/test_user_message_no_duplicate_e2e.py | 4 +- tests/elicitation/test_e2e_crash_recovery.py | 20 +- .../test_integration_elicitation.py | 38 +- .../elicitation/test_protocol_integration.py | 28 +- tests/elicitation/test_resume_lifecycle.py | 24 +- tests/elicitation/test_unit_elicitation.py | 74 +- tests/fixtures/minimal_pool.py | 2 +- tests/fixtures/subagent_events.py | 4 +- tests/fixtures/team_mode_pool.py | 2 +- tests/fixtures/test_resource_cap.py | 2 +- tests/functional/test_functional.py | 2 +- tests/hooks/test_hook_aware_turn.py | 6 +- tests/hooks/test_hook_smoke_matrix.py | 16 +- tests/hooks/test_hooks.py | 6 +- tests/host/test_context.py | 4 +- tests/host/test_factory.py | 27 +- tests/host/test_registry.py | 2 +- tests/integration/test_acp_agent_close.py | 2 +- tests/integration/test_acp_streaming.py | 12 +- .../test_base_server_start_background.py | 2 +- tests/integration/test_cross_protocol.py | 12 +- .../test_event_manager_lifecycle.py | 2 +- .../test_permission_denial_sync.py | 2 +- tests/integration/test_skill_e2e.py | 24 +- tests/integration/test_skill_resolution.py | 16 +- tests/integration/test_step_usage_e2e.py | 12 +- .../test_v2_message_id_integration.py | 34 +- tests/lifecycle/test_comm_channel.py | 30 +- .../lifecycle/test_config_and_integration.py | 14 +- tests/lifecycle/test_crash_recovery.py | 16 +- tests/lifecycle/test_deprecation.py | 2 +- tests/lifecycle/test_event_transport.py | 2 +- tests/lifecycle/test_journal.py | 2 +- .../lifecycle/test_recovery_event_delivery.py | 10 +- tests/lifecycle/test_run_loop.py | 12 +- tests/lifecycle/test_session_migration.py | 24 +- tests/lifecycle/test_snapshot_store.py | 2 +- tests/lifecycle/test_trigger_source.py | 2 +- tests/lifecycle/test_types.py | 4 +- tests/manifest/test_loader.py | 2 +- tests/manifest/test_metadata_fields.py | 6 +- tests/manifest/test_models.py | 2 +- tests/manifest/test_schema.py | 2 +- tests/manifest/test_schema_override.py | 2 +- tests/manifest/test_yaml_inheritance.py | 2 +- tests/mcp_client/test_client_conversion.py | 4 +- tests/mcp_client/test_contextual_progress.py | 8 +- tests/mcp_client/test_mcp_features.py | 6 +- .../mcp_server/test_child_session_acp_fix.py | 6 +- tests/mcp_server/test_config_snapshot.py | 4 +- ...test_e2e_acp_inheritance_function_model.py | 26 +- .../test_e2e_connection_tracking.py | 16 +- tests/mcp_server/test_e2e_mcp_capability.py | 30 +- .../mcp_server/test_e2e_session_controller.py | 34 +- tests/mcp_server/test_global_pool.py | 4 +- .../test_global_pool_review_fixes.py | 4 +- tests/mcp_server/test_global_pool_sharing.py | 8 +- tests/mcp_server/test_manager_capability.py | 4 +- tests/mcp_server/test_manager_status.py | 22 +- tests/mcp_server/test_mcp_persistence.py | 8 +- .../mcp_server/test_mcp_provider_lifecycle.py | 10 +- tests/mcp_server/test_review_fixes.py | 20 +- tests/mcp_server/test_review_fixes_r3.py | 18 +- .../test_session_close_integration.py | 4 +- tests/mcp_server/test_session_lifecycle.py | 4 +- tests/mcp_server/test_session_pool.py | 24 +- .../test_session_wiring_integration.py | 34 +- .../test_session_wiring_integration_part2.py | 24 +- tests/mcp_server/test_stale_mcp_connection.py | 8 +- .../mcp_server/test_tool_bridge_conversion.py | 2 +- tests/messaging/test_adapters.py | 14 +- tests/messaging/test_agent_piping.py | 6 +- tests/messaging/test_agent_signals.py | 4 +- tests/messaging/test_compaction.py | 4 +- tests/messaging/test_connection_registry.py | 6 +- tests/messaging/test_conversation.py | 2 +- tests/messaging/test_debug_taskgroup.py | 2 +- tests/messaging/test_event_converter.py | 4 +- tests/messaging/test_event_manager.py | 4 +- .../messaging/test_event_routing_scenarios.py | 4 +- tests/messaging/test_message_tracker.py | 12 +- tests/messaging/test_messagenode_bind_pool.py | 8 +- tests/messaging/test_messagenode_cleanup.py | 4 +- tests/messaging/test_messagenode_events.py | 6 +- tests/messaging/test_minimal_piping.py | 2 +- tests/messaging/test_parent_id.py | 2 +- tests/messaging/test_runners.py | 2 +- tests/messaging/test_signal_forwarding.py | 2 +- tests/messaging/test_source_type.py | 30 +- tests/messaging/test_talks.py | 6 +- tests/messaging/test_thread_hypothesis.py | 2 +- tests/messaging/test_tool_display_acp_diff.py | 4 +- tests/observability/test_observability.py | 4 +- .../test_observability_integration.py | 4 +- tests/observability/test_span_hierarchy.py | 2 +- .../test_tracing_optimization.py | 46 +- tests/orchestrator/test_aexit_hang.py | 10 +- .../orchestrator/test_agent_type_detection.py | 4 +- tests/orchestrator/test_auto_compaction.py | 4 +- tests/orchestrator/test_cancellation.py | 18 +- tests/orchestrator/test_config_hash.py | 6 +- tests/orchestrator/test_consume_run_event.py | 14 +- tests/orchestrator/test_deferred_timeout.py | 4 +- tests/orchestrator/test_deprecation.py | 14 +- tests/orchestrator/test_e2e.py | 14 +- .../test_enqueued_messages_mapping.py | 4 +- .../orchestrator/test_envelope_integration.py | 8 +- tests/orchestrator/test_event_bus.py | 6 +- tests/orchestrator/test_event_mapper.py | 4 +- ...est_event_mapper_thinking_normalization.py | 4 +- ...est_event_mapper_tool_call_sanitization.py | 2 +- .../test_event_mapper_tool_progress.py | 4 +- tests/orchestrator/test_execute_turn.py | 8 +- .../orchestrator/test_integration_redflags.py | 8 +- tests/orchestrator/test_multimodal_prompts.py | 22 +- tests/orchestrator/test_multimodal_storage.py | 16 +- .../test_native_turn_integration.py | 28 +- tests/orchestrator/test_performance.py | 12 +- tests/orchestrator/test_phase6_cleanup.py | 14 +- tests/orchestrator/test_receive_request.py | 12 +- .../orchestrator/test_receive_request_acp.py | 12 +- .../test_receive_request_input_provider.py | 6 +- .../orchestrator/test_recovery_integration.py | 4 +- .../test_replay_buffer_turn_isolation.py | 6 +- tests/orchestrator/test_resume_integration.py | 48 +- .../orchestrator/test_route_message_event.py | 6 +- .../orchestrator/test_run_event_agent_name.py | 14 +- .../test_run_failure_context_redflag.py | 6 +- tests/orchestrator/test_run_handle.py | 58 +- tests/orchestrator/test_run_lifecycle.py | 14 +- tests/orchestrator/test_run_status.py | 4 +- tests/orchestrator/test_runhandle_emission.py | 12 +- tests/orchestrator/test_session_controller.py | 20 +- tests/orchestrator/test_session_lifecycle.py | 22 +- tests/orchestrator/test_session_pool.py | 10 +- .../test_session_pool_input_provider.py | 12 +- .../test_session_pool_public_api.py | 12 +- .../test_sessionpool_integration.py | 44 +- .../test_sessionpool_reasoning_redflag.py | 6 +- .../test_staged_content_integration.py | 18 +- .../test_steer_background_acp_integration.py | 10 +- tests/orchestrator/test_steer_message_loss.py | 18 +- tests/orchestrator/test_steer_queue_events.py | 14 +- tests/orchestrator/test_steer_user_message.py | 12 +- .../test_streaming_redflag_tool_calls.py | 12 +- tests/orchestrator/test_subagent_events.py | 14 +- .../test_user_message_display_integration.py | 8 +- .../test_user_message_inserted_integration.py | 14 +- tests/phase8_merge_queue_removal_test.py | 4 +- tests/phase8_shutdown_race_condition_test.py | 2 +- tests/phase8_subagent_cascade_test.py | 2 +- tests/prompts/test_instructions_types.py | 4 +- tests/prompts/test_prompt_manager.py | 4 +- tests/prompts/test_sys_prompts.py | 2 +- tests/providers/test_multimodal.py | 2 +- tests/providers/test_structured.py | 2 +- tests/repomap/test_repomap_imports.py | 14 +- tests/resources/test_loading_resources.py | 4 +- tests/running/test_delegation.py | 8 +- tests/running/test_discovery.py | 2 +- tests/running/test_executor.py | 6 +- tests/security/test_skill_security.py | 78 +- tests/server/agui/test_skill_tools.py | 6 +- tests/servers/acp_server/conftest.py | 10 +- .../acp_server/test_acp_agent_integration.py | 22 +- .../acp_server/test_acp_available_commands.py | 10 +- .../acp_server/test_acp_cancel_then_prompt.py | 14 +- .../acp_server/test_acp_elicitation_resume.py | 14 +- .../acp_server/test_acp_integration.py | 16 +- tests/servers/acp_server/test_acp_load.py | 20 +- .../test_acp_mcp_transport_reuse.py | 4 +- .../test_acp_protocol_handler_cancel.py | 8 +- ...est_acp_protocol_handler_input_provider.py | 20 +- tests/servers/acp_server/test_acp_resume.py | 24 +- .../acp_server/test_acp_resume_integration.py | 18 +- .../acp_server/test_acp_session_load.py | 14 +- .../test_acp_session_manager_child_session.py | 46 +- ...cp_session_process_prompt_turn_complete.py | 10 +- .../acp_server/test_acp_session_resume.py | 58 +- .../acp_server/test_acp_skills_red_flags.py | 42 +- .../acp_server/test_acp_v2_extensions.py | 10 +- tests/servers/acp_server/test_agent_role.py | 4 +- .../test_claude_acp_toolset_integration.py | 10 +- .../test_client_filesystem_tools.py | 6 +- .../test_command_bridge_streaming.py | 6 +- tests/servers/acp_server/test_dedup.py | 4 +- tests/servers/acp_server/test_deferred_acp.py | 4 +- .../test_elicitation_task_isolation.py | 2 +- .../acp_server/test_enum_elicitation.py | 2 +- .../test_event_converter_tool_call_fix.py | 4 +- .../test_event_converter_user_message.py | 4 +- ...vent_converter_user_message_integration.py | 4 +- .../acp_server/test_get_session_red_flag.py | 6 +- .../acp_server/test_handle_prompt_delivery.py | 18 +- .../test_input_provider_elicitation.py | 2 +- .../acp_server/test_mcp_integration.py | 18 +- tests/servers/acp_server/test_model_state.py | 6 +- .../test_process_tools_integration.py | 8 +- .../acp_server/test_prompt_meta_delivery.py | 2 +- .../acp_server/test_provider_router.py | 4 +- .../servers/acp_server/test_raw_input_mode.py | 4 +- .../acp_server/test_steer_event_pipeline.py | 14 +- .../acp_server/test_step_usage_converter.py | 6 +- .../acp_server/test_subagent_events.py | 42 +- .../acp_server/test_tool_call_snapshots.py | 6 +- .../acp_server/test_turn_completion.py | 2 +- tests/servers/acp_server/tool_call_harness.py | 14 +- tests/servers/opencode_server/conftest.py | 56 +- .../test_agent_model_propagation.py | 54 +- .../test_auto_resume_message_redflag.py | 16 +- .../opencode_server/test_cancelled_message.py | 20 +- .../opencode_server/test_command_execution.py | 10 +- .../test_command_model_and_hints.py | 4 +- .../opencode_server/test_commit_revert.py | 14 +- .../test_commit_revert_integration.py | 20 +- .../test_concurrent_messages.py | 28 +- .../opencode_server/test_config_routes.py | 4 +- .../test_converter_thinking.py | 8 +- .../opencode_server/test_d14_separation.py | 2 +- tests/servers/opencode_server/test_dedup.py | 14 +- .../opencode_server/test_deferred_opencode.py | 22 +- .../opencode_server/test_diagnostic.py | 10 +- .../test_duplicate_message_id.py | 8 +- .../test_duplication_reproduction.py | 8 +- .../test_elicitation_bug_coverage.py | 38 +- .../opencode_server/test_ensure_session.py | 48 +- .../test_ensure_session_idle.py | 6 +- .../opencode_server/test_event_adaptation.py | 36 +- .../opencode_server/test_event_bridge.py | 54 +- .../test_event_pipeline_e2e.py | 26 +- .../opencode_server/test_event_processor.py | 30 +- .../test_event_processor_diff.py | 14 +- .../test_event_processor_user_message.py | 12 +- ...vent_processor_user_message_integration.py | 12 +- .../opencode_server/test_file_diff_models.py | 4 +- .../test_global_compat_routes.py | 18 +- .../opencode_server/test_global_event.py | 32 +- .../opencode_server/test_init_session.py | 2 +- .../test_list_sessions_restore.py | 22 +- .../opencode_server/test_mcp_routes.py | 4 +- .../test_mcp_status_behavior.py | 12 +- .../test_mcp_status_converter.py | 4 +- .../test_message_id_alignment.py | 112 +- .../opencode_server/test_message_models.py | 6 +- .../test_message_routes_dedup.py | 12 +- .../test_message_routes_regression.py | 4 +- .../opencode_server/test_mode_consistency.py | 20 +- .../test_model_default_resolution.py | 12 +- .../test_model_variant_resolution.py | 10 +- .../test_opencode_model_switching.py | 28 +- .../opencode_server/test_opencode_resume.py | 24 +- .../test_p1_part_updated_event.py | 12 +- .../test_p2_protocol_channel_routing.py | 14 +- .../test_p3_context_serialization.py | 28 +- .../test_p4_message_registered_reset.py | 18 +- .../opencode_server/test_part_id_dedup.py | 20 +- .../test_permission_session_state.py | 13 +- .../test_project_id_mismatch.py | 12 +- .../opencode_server/test_prompt_async.py | 4 +- .../opencode_server/test_question_handling.py | 59 +- .../servers/opencode_server/test_reasoning.py | 12 +- .../test_replay_buffer_sync.py | 10 +- .../test_resource_resolution.py | 100 +- .../opencode_server/test_restart_recovery.py | 10 +- .../opencode_server/test_revert_cycle.py | 10 +- .../test_revert_stage_clear.py | 20 +- .../opencode_server/test_route_discovery.py | 36 +- .../test_run_error_session_status.py | 14 +- .../opencode_server/test_server_lifecycle.py | 6 +- .../opencode_server/test_session_cache_404.py | 8 +- .../test_session_cwd_consistency.py | 4 +- .../test_session_history_loading.py | 10 +- .../test_session_integration.py | 68 +- .../opencode_server/test_session_lifecycle.py | 16 +- .../test_session_pool_integration.py | 12 +- .../test_session_provisioning.py | 32 +- .../test_session_scoped_consumer.py | 18 +- .../test_session_storage_load.py | 10 +- .../test_session_switch_input_provider.py | 8 +- .../test_session_title_fixes.py | 38 +- .../test_session_title_integration.py | 12 +- .../opencode_server/test_share_revert.py | 16 +- tests/servers/opencode_server/test_shell.py | 4 +- .../test_skill_autocomplete.py | 8 +- .../test_skill_command_execution.py | 2 +- .../test_skill_command_path.py | 8 +- .../opencode_server/test_soft_hide_revert.py | 18 +- .../opencode_server/test_sse_compliance.py | 24 +- .../opencode_server/test_sse_headers.py | 2 +- .../opencode_server/test_step_usage.py | 26 +- .../test_stream_adapter_event_feed.py | 28 +- .../test_subagent_integration.py | 32 +- .../opencode_server/test_summarize_session.py | 26 +- .../opencode_server/test_title_management.py | 32 +- .../opencode_server/test_todo_utils.py | 6 +- .../opencode_server/test_tui_routing.py | 4 +- .../test_turn_lifecycle_state_machine.py | 18 +- .../test_turn_split_integration.py | 20 +- tests/servers/test_a2a_server.py | 10 +- tests/servers/test_aggregating_server.py | 8 +- tests/servers/test_agui_server.py | 10 +- tests/servers/test_openai_api_server.py | 8 +- tests/servers/test_openai_api_thinking.py | 14 +- tests/servers/test_step_usage_agui_openai.py | 12 +- tests/servers/test_subagent_event_mixin.py | 6 +- tests/sessions/conftest.py | 8 +- tests/sessions/test_checkpoint_round_trip.py | 4 +- tests/sessions/test_creation_unification.py | 32 +- tests/sessions/test_e2e_lifecycle.py | 14 +- tests/sessions/test_elicitation_cancel.py | 4 +- tests/sessions/test_history.py | 12 +- tests/sessions/test_history_processors.py | 12 +- tests/sessions/test_list_sessions_batch.py | 8 +- .../test_list_sessions_cross_agent.py | 6 +- tests/sessions/test_message_ordering.py | 12 +- tests/sessions/test_opencode_helpers.py | 2 +- tests/sessions/test_opencode_integration.py | 18 +- .../sessions/test_opencode_project_methods.py | 10 +- .../sessions/test_opencode_session_methods.py | 8 +- .../test_opencode_thinking_roundtrip.py | 8 +- tests/sessions/test_pending_deferred_call.py | 2 +- .../test_project_provider_selection.py | 8 +- tests/sessions/test_round_trip_migration.py | 6 +- tests/sessions/test_session_controller.py | 14 +- .../test_session_from_db_edge_cases.py | 6 +- tests/sessions/test_session_hierarchy.py | 14 +- tests/sessions/test_session_id_opaque.py | 36 +- tests/sessions/test_session_persistence.py | 12 +- .../sessions/test_sql_provider_no_dispose.py | 6 +- tests/sessions/test_state_mapper.py | 4 +- .../sessions/test_state_mapper_integration.py | 8 +- tests/sessions/test_storage_provider_fixes.py | 36 +- tests/sessions/test_title_generation.py | 20 +- tests/skills/test_command.py | 4 +- tests/skills/test_exceptions.py | 4 +- tests/skills/test_flat_uri.py | 12 +- tests/skills/test_load_skill_propagation.py | 4 +- tests/skills/test_manager_config.py | 18 +- tests/skills/test_mcp_skills_integration.py | 10 +- tests/skills/test_registry_events.py | 4 +- ...test_scratchpad_skill_reference_redflag.py | 12 +- tests/skills/test_skill_parsing.py | 2 +- tests/skills/test_skill_tool_manager.py | 18 +- tests/skills/test_skills.py | 4 +- tests/skills/test_skills_config.py | 2 +- tests/skills/test_skills_integration.py | 4 +- tests/skills/test_uri_resolver.py | 6 +- tests/storage/test_checkpoint_storage.py | 10 +- tests/storage/test_deferred_serialization.py | 4 +- tests/storage/test_memory_checkpoint.py | 4 +- tests/storage/test_protocols.py | 12 +- tests/storage/test_sql_checkpoint.py | 6 +- tests/storage/test_sql_truncate_messages.py | 6 +- tests/team_mode/conftest.py | 20 +- tests/team_mode/test_display_e2e.py | 26 +- tests/team_mode/test_display_enrichment.py | 2 +- tests/team_mode/test_flow_budget.py | 8 +- .../team_mode/test_flow_error_containment.py | 8 +- tests/team_mode/test_flow_lifecycle.py | 8 +- tests/team_mode/test_flow_role_aware.py | 6 +- .../team_mode/test_integration_collab_flow.py | 8 +- tests/team_mode/test_integration_defaults.py | 10 +- tests/team_mode/test_integration_mode.py | 14 +- tests/team_mode/test_integration_parallel.py | 6 +- tests/team_mode/test_integration_session.py | 24 +- tests/team_mode/test_live.py | 10 +- tests/team_mode/test_unit_batch_progress.py | 4 +- tests/team_mode/test_unit_capability.py | 62 +- tests/team_mode/test_unit_collab_flow.py | 16 +- tests/team_mode/test_unit_config.py | 2 +- tests/team_mode/test_unit_file_state.py | 2 +- tests/teams/test_parallel_agents.py | 4 +- tests/teams/test_team.py | 4 +- tests/teams/test_team_run.py | 6 +- tests/teams/test_team_streaming.py | 34 +- tests/test_acp_modality_capabilities.py | 4 +- tests/test_agent_factory_modality.py | 20 +- tests/test_capability_cache.py | 4 +- .../test_config/test_package_skill_scopes.py | 4 +- tests/test_event_bus_conditional_replay.py | 4 +- tests/test_event_bus_mechanics.py | 10 +- tests/test_modality_e2e.py | 12 +- tests/test_modality_filter.py | 8 +- tests/test_modality_filter_config.py | 4 +- tests/test_modality_utils.py | 2 +- tests/test_model_capabilities.py | 4 +- tests/test_opencode_display_propagation.py | 6 +- tests/test_opencode_provider_modality.py | 4 +- ...test_sse_conditional_replay_integration.py | 6 +- .../question/test_question_for_user.py | 4 +- .../tool_impls/question/test_question_tool.py | 6 +- tests/tool_impls/test_tool_impls_imports.py | 18 +- tests/tools/test_deferred_exports.py | 10 +- tests/tools/test_deferred_tool.py | 2 +- tests/tools/test_deferred_validation.py | 4 +- .../tools/test_execution_environment_tools.py | 10 +- tests/tools/test_file_editor.py | 2 +- tests/tools/test_mcp_tools.py | 2 +- tests/tools/test_openapi_toolsets.py | 2 +- tests/tools/test_pick.py | 2 +- tests/tools/test_pydantic_ai_schema.py | 2 +- tests/tools/test_runcontext.py | 10 +- tests/tools/test_tool_confirmation.py | 4 +- tests/tools/test_tool_schema.py | 8 +- tests/tools/test_workers.py | 12 +- tests/toolsets/builtin/test_as_capability.py | 8 +- tests/toolsets/test_agentic_edit.py | 4 +- tests/toolsets/test_builtin_handlers.py | 4 +- tests/toolsets/test_custom_toolset.py | 4 +- tests/toolsets/test_diff_application.py | 2 +- .../test_input_provider_propagation.py | 16 +- tests/toolsets/test_mcp_discovery.py | 10 +- tests/toolsets/test_notifications.py | 4 +- tests/toolsets/test_package_scoped_skills.py | 10 +- tests/toolsets/test_process_integration.py | 8 +- tests/toolsets/test_subagent_async.py | 2 +- tests/toolsets/test_subagent_child_session.py | 16 +- tests/toolsets/test_tool_filtering.py | 8 +- tests/unit/test_open_code_config_cleanup.py | 2 +- tests/utils/test_context_wrapping.py | 22 +- tests/utils/test_dag.py | 2 +- tests/utils/test_safe_args_as_dict.py | 2 +- tests/utils/test_task_group.py | 2 +- tests/utils/test_usage.py | 2 +- tests/vcr/_acp_helpers.py | 4 +- tests/vcr/conftest.py | 2 +- tests/vcr/test_acp_protocol.py | 2 +- tests/vcr/test_acp_subagent.py | 2 +- tests/vcr/test_acp_tool_call.py | 2 +- tests/vcr/test_agui_protocol.py | 4 +- tests/vcr/test_delegation.py | 6 +- tests/vcr/test_event_bus_sequences.py | 4 +- tests/vcr/test_lifecycle_recovery.py | 4 +- tests/vcr/test_native_basic.py | 4 +- tests/vcr/test_native_streaming.py | 6 +- tests/vcr/test_native_tool_call.py | 6 +- tests/vcr/test_openai_api_protocol.py | 4 +- tests/vcr/test_opencode_protocol.py | 12 +- tests/vcr/test_runloop_streaming.py | 2 +- tests/vcr/test_session_controller.py | 2 +- tests/vcr/test_skill_command_vcr.py | 8 +- tests/vcr/test_team_mode.py | 4 +- tests/vcr/test_turn_tool_calls.py | 6 +- tests/verification/test_acp_display_config.py | 28 +- tests/verification/test_rfc0011_lineage.py | 14 +- ty.toml | 26 +- uv.lock | 4642 +++++++++-------- agentpool.spec => wolfharness.spec | 22 +- 1631 files changed, 11459 insertions(+), 11127 deletions(-) create mode 100644 openspec/changes/rename-agentpool-to-wolfharness/.openspec.yaml create mode 100644 openspec/changes/rename-agentpool-to-wolfharness/README.md create mode 100644 openspec/changes/rename-agentpool-to-wolfharness/design.md create mode 100644 openspec/changes/rename-agentpool-to-wolfharness/proposal.md create mode 100644 openspec/changes/rename-agentpool-to-wolfharness/specs/wolfharness-rename/spec.md create mode 100644 openspec/changes/rename-agentpool-to-wolfharness/tasks.md rename scripts/{rename_to_agentwolf.py => rename_to_wolfharness.py} (67%) delete mode 100644 src/agentpool/__main__.py delete mode 100644 src/agentpool/agents/__init__.py delete mode 100644 src/agentpool/delegation/__init__.py delete mode 100644 src/agentpool/hooks/__init__.py delete mode 100644 src/agentpool/mcp_server/__init__.py delete mode 100644 src/agentpool/models/__init__.py delete mode 100644 src/agentpool/running/__init__.py delete mode 100644 src/agentpool/skills/__init__.py delete mode 100644 src/agentpool/talk/__init__.py delete mode 100644 src/agentpool_bot/__init__.py delete mode 100644 src/agentpool_commands/base.py delete mode 100644 src/agentpool_server/__init__.py delete mode 100644 src/agentpool_server/acp_server/__init__.py delete mode 100644 src/agentpool_server/opencode_server/routes/__init__.py delete mode 100644 src/agentpool_storage/file_provider/__init__.py delete mode 100644 src/agentpool_storage/memory_provider/__init__.py delete mode 100644 src/agentpool_toolsets/__init__.py delete mode 100644 src/agentpool_toolsets/builtin/__init__.py rename src/{agentpool => wolfharness}/AGENTS.md (96%) rename src/{agentpool => wolfharness}/__init__.py (63%) create mode 100644 src/wolfharness/__main__.py create mode 100644 src/wolfharness/agents/__init__.py rename src/{agentpool => wolfharness}/agents/acp_agent/__init__.py (100%) rename src/{agentpool => wolfharness}/agents/acp_agent/acp_agent.py (92%) rename src/{agentpool => wolfharness}/agents/acp_agent/acp_converters.py (97%) rename src/{agentpool => wolfharness}/agents/acp_agent/client_handler.py (97%) rename src/{agentpool => wolfharness}/agents/acp_agent/helpers.py (97%) rename src/{agentpool => wolfharness}/agents/acp_agent/modes.py (98%) rename src/{agentpool => wolfharness}/agents/acp_agent/session_state.py (98%) rename src/{agentpool => wolfharness}/agents/acp_agent/turn.py (90%) rename src/{agentpool => wolfharness}/agents/base_agent.py (96%) rename src/{agentpool => wolfharness}/agents/context.py (96%) rename src/{agentpool => wolfharness}/agents/events/__init__.py (100%) rename src/{agentpool => wolfharness}/agents/events/builtin_handlers.py (95%) rename src/{agentpool => wolfharness}/agents/events/event_emitter.py (96%) rename src/{agentpool => wolfharness}/agents/events/events.py (99%) rename src/{agentpool => wolfharness}/agents/events/infer_info.py (97%) rename src/{agentpool => wolfharness}/agents/events/processors.py (97%) rename src/{agentpool => wolfharness}/agents/events/tts_handlers.py (95%) rename src/{agentpool => wolfharness}/agents/exceptions.py (100%) rename src/{agentpool => wolfharness}/agents/interactions.py (96%) rename src/{agentpool => wolfharness}/agents/modes.py (98%) rename src/{agentpool => wolfharness}/agents/native_agent/__init__.py (100%) rename src/{agentpool => wolfharness}/agents/native_agent/agent.py (93%) rename src/{agentpool => wolfharness}/agents/native_agent/approval_bridge.py (96%) rename src/{agentpool => wolfharness}/agents/native_agent/checkpoint.py (96%) rename src/{agentpool => wolfharness}/agents/native_agent/deferred_bridge.py (97%) rename src/{agentpool => wolfharness}/agents/native_agent/elicitation_bridge.py (97%) rename src/{agentpool => wolfharness}/agents/native_agent/elicitation_strategy.py (96%) rename src/{agentpool => wolfharness}/agents/native_agent/helpers.py (92%) rename src/{agentpool => wolfharness}/agents/native_agent/hook_manager.py (96%) rename src/{agentpool => wolfharness}/agents/native_agent/process_history_capability.py (98%) rename src/{agentpool => wolfharness}/agents/native_agent/tool_intercept.py (95%) rename src/{agentpool => wolfharness}/agents/native_agent/tool_wrapping.py (96%) rename src/{agentpool => wolfharness}/agents/native_agent/turn.py (97%) rename src/{agentpool => wolfharness}/agents/prompt_injection.py (98%) rename src/{agentpool => wolfharness}/agents/staged_content.py (97%) rename src/{agentpool => wolfharness}/agents/sys_prompts.py (96%) rename src/{agentpool => wolfharness}/agents/tool_call_accumulator.py (100%) rename src/{agentpool => wolfharness}/auth/__init__.py (100%) rename src/{agentpool => wolfharness}/auth/anthropic_auth.py (100%) rename src/{agentpool => wolfharness}/capabilities/AGENTS.md (98%) rename src/{agentpool => wolfharness}/capabilities/__init__.py (100%) rename src/{agentpool => wolfharness}/capabilities/agent_context.py (86%) rename src/{agentpool => wolfharness}/capabilities/background_task/__init__.py (73%) rename src/{agentpool => wolfharness}/capabilities/background_task/capability.py (98%) rename src/{agentpool => wolfharness}/capabilities/background_task/manager.py (99%) rename src/{agentpool => wolfharness}/capabilities/background_task/notification.py (99%) rename src/{agentpool => wolfharness}/capabilities/background_task/types.py (93%) rename src/{agentpool => wolfharness}/capabilities/change_event.py (100%) rename src/{agentpool => wolfharness}/capabilities/code_mode_capability.py (97%) rename src/{agentpool => wolfharness}/capabilities/codemode/__init__.py (100%) rename src/{agentpool => wolfharness}/capabilities/codemode/default_prompt.py (100%) rename src/{agentpool => wolfharness}/capabilities/codemode/helpers.py (94%) rename src/{agentpool => wolfharness}/capabilities/combined_toolset.py (98%) rename src/{agentpool => wolfharness}/capabilities/dcp/__init__.py (71%) rename src/{agentpool => wolfharness}/capabilities/dcp/block_store.py (98%) rename src/{agentpool => wolfharness}/capabilities/dcp/capability.py (99%) rename src/{agentpool => wolfharness}/capabilities/dcp/config.py (98%) rename src/{agentpool => wolfharness}/capabilities/dcp/nudge.py (96%) rename src/{agentpool => wolfharness}/capabilities/dcp/prunable_list.py (96%) rename src/{agentpool => wolfharness}/capabilities/dcp/state.py (99%) rename src/{agentpool => wolfharness}/capabilities/dcp/strategies.py (98%) rename src/{agentpool => wolfharness}/capabilities/dcp/token_utils.py (100%) rename src/{agentpool => wolfharness}/capabilities/dcp/tools.py (99%) rename src/{agentpool => wolfharness}/capabilities/dcp/watermark.py (96%) rename src/{agentpool => wolfharness}/capabilities/delegation.py (100%) rename src/{agentpool => wolfharness}/capabilities/extension_registry.py (96%) rename src/{agentpool => wolfharness}/capabilities/file_team_state.py (100%) rename src/{agentpool => wolfharness}/capabilities/filtered_toolset.py (97%) rename src/{agentpool => wolfharness}/capabilities/function_toolset.py (90%) rename src/{agentpool => wolfharness}/capabilities/loop_detection.py (98%) rename src/{agentpool => wolfharness}/capabilities/mcp_server_cap.py (97%) rename src/{agentpool => wolfharness}/capabilities/memory.py (100%) rename src/{agentpool => wolfharness}/capabilities/modality_filter.py (98%) rename src/{agentpool => wolfharness}/capabilities/modality_utils.py (100%) rename src/{agentpool => wolfharness}/capabilities/question.py (90%) rename src/{agentpool => wolfharness}/capabilities/registry.py (93%) rename src/{agentpool => wolfharness}/capabilities/resource_capability.py (97%) rename src/{agentpool => wolfharness}/capabilities/resource_protocols.py (98%) rename src/{agentpool => wolfharness}/capabilities/resource_resolver.py (96%) rename src/{agentpool => wolfharness}/capabilities/runloop_delegation.py (93%) rename src/{agentpool => wolfharness}/capabilities/skill_manager_cap.py (96%) rename src/{agentpool => wolfharness}/capabilities/skill_watcher.py (97%) rename src/{agentpool => wolfharness}/capabilities/subagent_capability.py (94%) rename src/{agentpool => wolfharness}/capabilities/team_comm_capability.py (98%) rename src/{agentpool => wolfharness}/capabilities/token_budget.py (100%) rename src/{agentpool => wolfharness}/capabilities/tool_display_capability.py (95%) rename src/{agentpool => wolfharness}/capabilities/tool_output_budget.py (100%) rename src/{agentpool => wolfharness}/capabilities/viking/__init__.py (97%) rename src/{agentpool => wolfharness}/capabilities/viking/compaction.py (100%) rename src/{agentpool => wolfharness}/capabilities/viking/identity.py (100%) rename src/{agentpool => wolfharness}/capabilities/viking/ingest.py (99%) rename src/{agentpool => wolfharness}/capabilities/viking/instructions.py (100%) rename src/{agentpool => wolfharness}/capabilities/viking/profile.py (99%) rename src/{agentpool => wolfharness}/capabilities/viking/recall.py (99%) rename src/{agentpool => wolfharness}/capabilities/viking/tools.py (99%) rename src/{agentpool => wolfharness}/capabilities/viking/utils.py (100%) rename src/{agentpool => wolfharness}/commands/__init__.py (69%) rename src/{agentpool => wolfharness}/commands/base.py (91%) rename src/{agentpool => wolfharness}/common_types.py (97%) rename src/{agentpool => wolfharness}/config_resources/__init__.py (94%) rename src/{agentpool => wolfharness}/config_resources/acp_assistant.yml (92%) rename src/{agentpool => wolfharness}/config_resources/agents.yml (98%) rename src/{agentpool => wolfharness}/config_resources/agents_template.yml (90%) rename src/{agentpool => wolfharness}/config_resources/claude_style_subagent.md (100%) rename src/{agentpool => wolfharness}/config_resources/external_acp_agents.yml (96%) rename src/{agentpool => wolfharness}/config_resources/opencode_style_subagent.md (100%) rename src/{agentpool => wolfharness}/config_resources/tts_test_agents.yml (92%) create mode 100644 src/wolfharness/delegation/__init__.py rename src/{agentpool => wolfharness}/delegation/base_team.py (95%) rename src/{agentpool => wolfharness}/delegation/graph_team.py (97%) rename src/{agentpool => wolfharness}/delegation/message_flow_tracker.py (92%) rename src/{agentpool => wolfharness}/delegation/pool.py (93%) rename src/{agentpool => wolfharness}/diagnostics/__init__.py (86%) rename src/{agentpool => wolfharness}/diagnostics/lsp_manager.py (99%) rename src/{agentpool => wolfharness}/diagnostics/lsp_proxy.py (100%) rename src/{agentpool => wolfharness}/diagnostics/lsp_proxy_script.py (100%) rename src/{agentpool => wolfharness}/diagnostics/models.py (100%) rename src/{agentpool => wolfharness}/docs/__init__.py (54%) rename src/{agentpool => wolfharness}/docs/gen_examples.py (96%) rename src/{agentpool => wolfharness}/docs/utils.py (95%) rename src/{agentpool => wolfharness}/functional/__init__.py (77%) rename src/{agentpool => wolfharness}/functional/py.typed (100%) rename src/{agentpool => wolfharness}/functional/run.py (92%) rename src/{agentpool => wolfharness}/functional/structure.py (96%) rename src/{agentpool => wolfharness}/hooks/AGENTS.md (100%) create mode 100644 src/wolfharness/hooks/__init__.py rename src/{agentpool => wolfharness}/hooks/agent_hooks.py (98%) rename src/{agentpool => wolfharness}/hooks/base.py (99%) rename src/{agentpool => wolfharness}/hooks/callable.py (95%) rename src/{agentpool => wolfharness}/hooks/command.py (97%) rename src/{agentpool => wolfharness}/hooks/prompt.py (95%) rename src/{agentpool => wolfharness}/host/__init__.py (100%) rename src/{agentpool => wolfharness}/host/context.py (75%) rename src/{agentpool => wolfharness}/host/factory.py (91%) rename src/{agentpool => wolfharness}/host/registry.py (96%) rename src/{agentpool => wolfharness}/host/stubs.py (99%) rename src/{agentpool => wolfharness}/jinja_filters.py (98%) rename src/{agentpool => wolfharness}/lifecycle/AGENTS.md (92%) rename src/{agentpool => wolfharness}/lifecycle/__init__.py (75%) rename src/{agentpool => wolfharness}/lifecycle/comm_channel.py (98%) rename src/{agentpool => wolfharness}/lifecycle/event_transport.py (99%) rename src/{agentpool => wolfharness}/lifecycle/factory.py (92%) rename src/{agentpool => wolfharness}/lifecycle/journal.py (98%) rename src/{agentpool => wolfharness}/lifecycle/protocols.py (99%) rename src/{agentpool => wolfharness}/lifecycle/snapshot_store.py (99%) rename src/{agentpool => wolfharness}/lifecycle/triggers.py (97%) rename src/{agentpool => wolfharness}/lifecycle/types.py (100%) rename src/{agentpool => wolfharness}/log.py (97%) create mode 100644 src/wolfharness/mcp_server/__init__.py rename src/{agentpool => wolfharness}/mcp_server/client.py (97%) rename src/{agentpool => wolfharness}/mcp_server/config_snapshot.py (98%) rename src/{agentpool => wolfharness}/mcp_server/constants.py (100%) rename src/{agentpool => wolfharness}/mcp_server/conversions.py (98%) rename src/{agentpool => wolfharness}/mcp_server/global_pool.py (98%) rename src/{agentpool => wolfharness}/mcp_server/helpers.py (96%) rename src/{agentpool => wolfharness}/mcp_server/manager.py (97%) rename src/{agentpool => wolfharness}/mcp_server/message_handler.py (96%) rename src/{agentpool => wolfharness}/mcp_server/registries/__init__.py (100%) rename src/{agentpool => wolfharness}/mcp_server/registries/official_registry_client.py (100%) rename src/{agentpool => wolfharness}/mcp_server/registries/pulsemcp_client.py (100%) rename src/{agentpool => wolfharness}/mcp_server/session_pool.py (96%) rename src/{agentpool => wolfharness}/mcp_server/session_stream_pair.py (100%) rename src/{agentpool => wolfharness}/mcp_server/tool_bridge.py (96%) rename src/{agentpool => wolfharness}/messaging/__init__.py (69%) rename src/{agentpool => wolfharness}/messaging/chat_filesystem.py (99%) rename src/{agentpool => wolfharness}/messaging/compaction.py (98%) rename src/{agentpool => wolfharness}/messaging/connection_manager.py (96%) rename src/{agentpool => wolfharness}/messaging/context.py (85%) rename src/{agentpool => wolfharness}/messaging/event_manager.py (98%) rename src/{agentpool => wolfharness}/messaging/events.py (86%) rename src/{agentpool => wolfharness}/messaging/graph_adapter.py (96%) rename src/{agentpool => wolfharness}/messaging/message_container.py (92%) rename src/{agentpool => wolfharness}/messaging/message_history.py (93%) rename src/{agentpool => wolfharness}/messaging/message_utils.py (94%) rename src/{agentpool => wolfharness}/messaging/messagenode.py (94%) rename src/{agentpool => wolfharness}/messaging/messages.py (97%) rename src/{agentpool => wolfharness}/messaging/processing.py (86%) rename src/{agentpool => wolfharness}/messaging/signal_adapter.py (97%) rename src/{agentpool => wolfharness}/messaging/streaming_adapter.py (98%) rename src/{agentpool => wolfharness}/mime_utils.py (98%) create mode 100644 src/wolfharness/models/__init__.py rename src/{agentpool => wolfharness}/models/acp_agents/__init__.py (91%) rename src/{agentpool => wolfharness}/models/acp_agents/base.py (93%) rename src/{agentpool => wolfharness}/models/agents.py (87%) rename src/{agentpool => wolfharness}/models/fields.py (86%) rename src/{agentpool => wolfharness}/models/file_agents.py (100%) rename src/{agentpool => wolfharness}/models/file_parsing.py (96%) rename src/{agentpool => wolfharness}/models/fixed_args_test_model.py (100%) rename src/{agentpool => wolfharness}/models/manifest.py (89%) rename src/{agentpool => wolfharness}/models/model_configs.py (98%) rename src/{agentpool => wolfharness}/models/pending_interaction.py (100%) rename src/{agentpool => wolfharness}/observability/__init__.py (64%) rename src/{agentpool => wolfharness}/observability/observability_registry.py (96%) rename src/{agentpool => wolfharness}/observability/spans.py (99%) rename src/{agentpool => wolfharness}/observability/trace.py (100%) rename src/{agentpool => wolfharness}/orchestrator/AGENTS.md (100%) rename src/{agentpool => wolfharness}/orchestrator/__init__.py (75%) rename src/{agentpool => wolfharness}/orchestrator/core.py (87%) rename src/{agentpool => wolfharness}/orchestrator/event_bus.py (99%) rename src/{agentpool => wolfharness}/orchestrator/event_mapper.py (99%) rename src/{agentpool => wolfharness}/orchestrator/metrics.py (78%) rename src/{agentpool => wolfharness}/orchestrator/run.py (97%) rename src/{agentpool => wolfharness}/orchestrator/runtime_registry.py (96%) rename src/{agentpool => wolfharness}/orchestrator/session_controller.py (96%) rename src/{agentpool => wolfharness}/orchestrator/session_controller_agent.py (96%) rename src/{agentpool => wolfharness}/orchestrator/session_controller_close.py (96%) rename src/{agentpool => wolfharness}/orchestrator/session_controller_runs.py (97%) rename src/{agentpool => wolfharness}/orchestrator/session_pool.py (96%) rename src/{agentpool => wolfharness}/orchestrator/session_pool_config.py (100%) rename src/{agentpool => wolfharness}/orchestrator/session_pool_messaging.py (96%) rename src/{agentpool => wolfharness}/orchestrator/session_pool_runs.py (95%) rename src/{agentpool => wolfharness}/orchestrator/session_pool_teams.py (91%) rename src/{agentpool => wolfharness}/orchestrator/turn.py (97%) rename src/{agentpool => wolfharness}/prompts/__init__.py (100%) rename src/{agentpool => wolfharness}/prompts/base.py (100%) rename src/{agentpool => wolfharness}/prompts/builtin_provider.py (91%) rename src/{agentpool => wolfharness}/prompts/conversion_manager.py (95%) rename src/{agentpool => wolfharness}/prompts/convert.py (94%) rename src/{agentpool => wolfharness}/prompts/instructions.py (98%) rename src/{agentpool => wolfharness}/prompts/manager.py (95%) rename src/{agentpool => wolfharness}/prompts/parts/zed.md (87%) rename src/{agentpool => wolfharness}/prompts/prompts.py (98%) rename src/{agentpool => wolfharness}/py.typed (100%) rename src/{agentpool => wolfharness}/repomap/__init__.py (81%) rename src/{agentpool => wolfharness}/repomap/context.py (95%) rename src/{agentpool => wolfharness}/repomap/core.py (98%) rename src/{agentpool => wolfharness}/repomap/languages.py (96%) rename src/{agentpool => wolfharness}/repomap/outline.py (97%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/README.md (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/arduino-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/c-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/chatito-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/clojure-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/commonlisp-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/cpp-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/csharp-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/d-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/dart-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/elisp-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/elixir-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/elm-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/gleam-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/go-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/java-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/javascript-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/lua-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/matlab-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/ocaml-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/ocaml_interface-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/pony-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/properties-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/python-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/r-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/racket-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/ruby-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/rust-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/solidity-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/swift-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-language-pack/udev-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/README.md (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/c-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/c_sharp-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/cpp-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/dart-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/elisp-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/elixir-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/elm-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/fortran-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/go-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/haskell-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/hcl-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/java-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/javascript-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/julia-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/kotlin-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/matlab-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/ocaml-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/ocaml_interface-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/php-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/python-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/ql-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/ruby-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/rust-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/scala-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/typescript-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/queries/tree-sitter-languages/zig-tags.scm (100%) rename src/{agentpool => wolfharness}/repomap/tags.py (98%) rename src/{agentpool => wolfharness}/repomap/types.py (100%) rename src/{agentpool => wolfharness}/repomap/utils.py (100%) create mode 100644 src/wolfharness/running/__init__.py rename src/{agentpool => wolfharness}/running/decorators.py (94%) rename src/{agentpool => wolfharness}/running/discovery.py (95%) rename src/{agentpool => wolfharness}/running/executor.py (97%) rename src/{agentpool => wolfharness}/running/injection.py (95%) rename src/{agentpool => wolfharness}/running/py.typed (100%) rename src/{agentpool => wolfharness}/running/run_nodes.py (93%) rename src/{agentpool => wolfharness}/server.py (97%) rename src/{agentpool => wolfharness}/sessions/__init__.py (60%) rename src/{agentpool => wolfharness}/sessions/models.py (98%) rename src/{agentpool => wolfharness}/sessions/state_mapper.py (98%) rename src/{agentpool => wolfharness}/skills/AGENTS.md (97%) create mode 100644 src/wolfharness/skills/__init__.py rename src/{agentpool => wolfharness}/skills/command.py (97%) rename src/{agentpool => wolfharness}/skills/exceptions.py (97%) rename src/{agentpool => wolfharness}/skills/manager.py (95%) rename src/{agentpool => wolfharness}/skills/registry.py (97%) rename src/{agentpool => wolfharness}/skills/skill.py (99%) rename src/{agentpool => wolfharness}/skills/skill_tool_manager.py (96%) rename src/{agentpool => wolfharness}/skills/uri_resolver.py (97%) rename src/{agentpool => wolfharness}/storage/__init__.py (71%) rename src/{agentpool => wolfharness}/storage/manager.py (98%) rename src/{agentpool => wolfharness}/storage/serialization.py (98%) create mode 100644 src/wolfharness/talk/__init__.py rename src/{agentpool => wolfharness}/talk/graph_edges.py (96%) rename src/{agentpool => wolfharness}/talk/registry.py (91%) rename src/{agentpool => wolfharness}/talk/stats.py (96%) rename src/{agentpool => wolfharness}/talk/talk.py (95%) rename src/{agentpool => wolfharness}/tasks/__init__.py (74%) rename src/{agentpool => wolfharness}/tasks/exceptions.py (88%) rename src/{agentpool => wolfharness}/tasks/registry.py (79%) rename src/{agentpool => wolfharness}/testing.py (98%) rename src/{agentpool => wolfharness}/text_templates/__init__.py (93%) rename src/{agentpool => wolfharness}/text_templates/system_prompt.jinja (100%) rename src/{agentpool => wolfharness}/text_templates/tool_call_default.jinja (100%) rename src/{agentpool => wolfharness}/text_templates/tool_call_markdown.jinja (100%) rename src/{agentpool => wolfharness}/text_templates/tool_call_simple.jinja (100%) rename src/{agentpool => wolfharness}/tool_impls/__init__.py (100%) rename src/{agentpool => wolfharness}/tool_impls/agent_cli/__init__.py (89%) rename src/{agentpool => wolfharness}/tool_impls/agent_cli/tool.py (96%) rename src/{agentpool => wolfharness}/tool_impls/bash/__init__.py (94%) rename src/{agentpool => wolfharness}/tool_impls/bash/helpers.py (100%) rename src/{agentpool => wolfharness}/tool_impls/bash/tool.py (97%) rename src/{agentpool => wolfharness}/tool_impls/delete_path/__init__.py (93%) rename src/{agentpool => wolfharness}/tool_impls/delete_path/tool.py (96%) rename src/{agentpool => wolfharness}/tool_impls/download_file/__init__.py (94%) rename src/{agentpool => wolfharness}/tool_impls/download_file/tool.py (97%) rename src/{agentpool => wolfharness}/tool_impls/execute_code/__init__.py (92%) rename src/{agentpool => wolfharness}/tool_impls/execute_code/tool.py (97%) rename src/{agentpool => wolfharness}/tool_impls/grep/__init__.py (95%) rename src/{agentpool => wolfharness}/tool_impls/grep/tool.py (94%) rename src/{agentpool => wolfharness}/tool_impls/list_directory/__init__.py (94%) rename src/{agentpool => wolfharness}/tool_impls/list_directory/tool.py (95%) rename src/{agentpool => wolfharness}/tool_impls/question/__init__.py (90%) rename src/{agentpool => wolfharness}/tool_impls/question/tool.py (95%) rename src/{agentpool => wolfharness}/tool_impls/read/__init__.py (92%) rename src/{agentpool => wolfharness}/tool_impls/read/tool.py (94%) rename src/{agentpool => wolfharness}/tools/__init__.py (70%) rename src/{agentpool => wolfharness}/tools/base.py (98%) rename src/{agentpool => wolfharness}/tools/exceptions.py (69%) rename src/{agentpool => wolfharness}/tools/tool_call_info.py (93%) rename src/{agentpool => wolfharness}/tools/tool_wrapping.py (96%) rename src/{agentpool => wolfharness}/ui/__init__.py (51%) rename src/{agentpool => wolfharness}/ui/base.py (96%) rename src/{agentpool => wolfharness}/ui/elicitation.py (99%) rename src/{agentpool => wolfharness}/ui/mock_provider.py (92%) rename src/{agentpool => wolfharness}/ui/stdlib_provider.py (94%) rename src/{agentpool => wolfharness}/utils/__init__.py (88%) rename src/{agentpool => wolfharness}/utils/baseregistry.py (100%) rename src/{agentpool => wolfharness}/utils/context_wrapping.py (94%) rename src/{agentpool => wolfharness}/utils/count_tokens.py (100%) rename src/{agentpool => wolfharness}/utils/dag.py (100%) rename src/{agentpool => wolfharness}/utils/diffs.py (100%) rename src/{agentpool => wolfharness}/utils/file_watcher.py (99%) rename src/{agentpool => wolfharness}/utils/identifiers.py (99%) rename src/{agentpool => wolfharness}/utils/importing.py (100%) rename src/{agentpool => wolfharness}/utils/inspection.py (98%) rename src/{agentpool => wolfharness}/utils/model_capabilities.py (100%) rename src/{agentpool => wolfharness}/utils/model_helpers.py (98%) rename src/{agentpool => wolfharness}/utils/network.py (100%) rename src/{agentpool => wolfharness}/utils/parse_time.py (100%) rename src/{agentpool => wolfharness}/utils/pydantic_ai_helpers.py (99%) rename src/{agentpool => wolfharness}/utils/result_utils.py (95%) rename src/{agentpool => wolfharness}/utils/signatures.py (99%) rename src/{agentpool => wolfharness}/utils/streams.py (99%) rename src/{agentpool => wolfharness}/utils/subprocess_utils.py (99%) rename src/{agentpool => wolfharness}/utils/task_group.py (99%) rename src/{agentpool => wolfharness}/utils/tasks.py (75%) rename src/{agentpool => wolfharness}/utils/thread_helpers.py (100%) rename src/{agentpool => wolfharness}/utils/time_utils.py (100%) rename src/{agentpool => wolfharness}/utils/todos.py (100%) rename src/{agentpool => wolfharness}/utils/token_breakdown.py (99%) rename src/{agentpool => wolfharness}/utils/tool_schema.py (100%) rename src/{agentpool => wolfharness}/utils/usage.py (100%) rename src/{agentpool => wolfharness}/utils/worktree.py (99%) rename src/{agentpool => wolfharness}/vfs_registry.py (98%) create mode 100644 src/wolfharness_bot/__init__.py rename src/{agentpool_bot => wolfharness_bot}/bus.py (99%) rename src/{agentpool_bot => wolfharness_bot}/channels/__init__.py (54%) rename src/{agentpool_bot => wolfharness_bot}/channels/base.py (94%) rename src/{agentpool_bot => wolfharness_bot}/channels/discord.py (95%) rename src/{agentpool_bot => wolfharness_bot}/channels/email.py (97%) rename src/{agentpool_bot => wolfharness_bot}/channels/manager.py (95%) rename src/{agentpool_bot => wolfharness_bot}/channels/slack.py (96%) rename src/{agentpool_bot => wolfharness_bot}/channels/telegram.py (97%) rename src/{agentpool_bot => wolfharness_bot}/channels/whatsapp.py (95%) rename src/{agentpool_bot => wolfharness_bot}/config.py (86%) rename src/{agentpool_bot => wolfharness_bot}/cron/__init__.py (76%) rename src/{agentpool_bot => wolfharness_bot}/cron/cron_types.py (100%) rename src/{agentpool_bot => wolfharness_bot}/cron/service.py (98%) rename src/{agentpool_bot => wolfharness_bot}/heartbeat.py (98%) rename src/{agentpool_bot => wolfharness_bot}/message_tools.py (93%) rename src/{agentpool_bot => wolfharness_bot}/transcription.py (98%) rename src/{agentpool_cli => wolfharness_cli}/__init__.py (90%) rename src/{agentpool_cli => wolfharness_cli}/__main__.py (68%) rename src/{agentpool_cli => wolfharness_cli}/agent.py (92%) rename src/{agentpool_cli => wolfharness_cli}/cli_types.py (100%) rename src/{agentpool_cli => wolfharness_cli}/common.py (95%) rename src/{agentpool_cli => wolfharness_cli}/config_info.py (92%) rename src/{agentpool_cli => wolfharness_cli}/history.py (84%) rename src/{agentpool_cli => wolfharness_cli}/log.py (93%) rename src/{agentpool_cli => wolfharness_cli}/py.typed (100%) rename src/{agentpool_cli => wolfharness_cli}/run.py (92%) rename src/{agentpool_cli => wolfharness_cli}/serve_acp.py (91%) rename src/{agentpool_cli => wolfharness_cli}/serve_agui.py (89%) rename src/{agentpool_cli => wolfharness_cli}/serve_api.py (91%) rename src/{agentpool_cli => wolfharness_cli}/serve_mcp.py (85%) rename src/{agentpool_cli => wolfharness_cli}/serve_opencode.py (86%) rename src/{agentpool_cli => wolfharness_cli}/serve_vercel.py (95%) rename src/{agentpool_cli => wolfharness_cli}/store.py (97%) rename src/{agentpool_cli => wolfharness_cli}/task.py (87%) rename src/{agentpool_cli => wolfharness_cli}/ui.py (93%) rename src/{agentpool_cli => wolfharness_cli}/utils.py (100%) rename src/{agentpool_cli => wolfharness_cli}/watch.py (89%) rename src/{agentpool_commands => wolfharness_commands}/__init__.py (89%) rename src/{agentpool_commands => wolfharness_commands}/agents.py (94%) create mode 100644 src/wolfharness_commands/base.py rename src/{agentpool_commands => wolfharness_commands}/commands.py (83%) rename src/{agentpool_commands => wolfharness_commands}/completers.py (94%) rename src/{agentpool_commands => wolfharness_commands}/connections.py (95%) rename src/{agentpool_commands => wolfharness_commands}/history.py (89%) rename src/{agentpool_commands => wolfharness_commands}/markdown_utils.py (100%) rename src/{agentpool_commands => wolfharness_commands}/mcp.py (89%) rename src/{agentpool_commands => wolfharness_commands}/models.py (90%) rename src/{agentpool_commands => wolfharness_commands}/pool.py (94%) rename src/{agentpool_commands => wolfharness_commands}/prompts.py (93%) rename src/{agentpool_commands => wolfharness_commands}/py.typed (100%) rename src/{agentpool_commands => wolfharness_commands}/read.py (89%) rename src/{agentpool_commands => wolfharness_commands}/resources.py (96%) rename src/{agentpool_commands => wolfharness_commands}/session.py (90%) rename src/{agentpool_commands => wolfharness_commands}/text_sharing/__init__.py (85%) rename src/{agentpool_commands => wolfharness_commands}/text_sharing/base.py (98%) rename src/{agentpool_commands => wolfharness_commands}/text_sharing/github_gist.py (94%) rename src/{agentpool_commands => wolfharness_commands}/text_sharing/opencode.py (97%) rename src/{agentpool_commands => wolfharness_commands}/text_sharing/paste_rs.py (91%) rename src/{agentpool_commands => wolfharness_commands}/text_sharing/pastebin.py (96%) rename src/{agentpool_commands => wolfharness_commands}/text_sharing/shittycodingagent.py (95%) rename src/{agentpool_commands => wolfharness_commands}/tools.py (94%) rename src/{agentpool_commands => wolfharness_commands}/utils.py (95%) rename src/{agentpool_commands => wolfharness_commands}/workers.py (93%) rename src/{agentpool_config => wolfharness_config}/__init__.py (71%) rename src/{agentpool_config => wolfharness_config}/builtin_tools.py (99%) rename src/{agentpool_config => wolfharness_config}/capabilities.py (94%) rename src/{agentpool_config => wolfharness_config}/commands.py (100%) rename src/{agentpool_config => wolfharness_config}/compaction.py (86%) rename src/{agentpool_config => wolfharness_config}/conditions.py (97%) rename src/{agentpool_config => wolfharness_config}/context.py (97%) rename src/{agentpool_config => wolfharness_config}/converters.py (100%) rename src/{agentpool_config => wolfharness_config}/durable.py (99%) rename src/{agentpool_config => wolfharness_config}/event_handlers.py (97%) rename src/{agentpool_config => wolfharness_config}/events.py (98%) rename src/{agentpool_config => wolfharness_config}/forward_targets.py (95%) rename src/{agentpool_config => wolfharness_config}/graph_config.py (98%) rename src/{agentpool_config => wolfharness_config}/graph_translation.py (97%) rename src/{agentpool_config => wolfharness_config}/hook_conditions.py (100%) rename src/{agentpool_config => wolfharness_config}/hooks.py (96%) rename src/{agentpool_config => wolfharness_config}/instructions.py (100%) rename src/{agentpool_config => wolfharness_config}/jinja.py (99%) rename src/{agentpool_config => wolfharness_config}/knowledge.py (87%) rename src/{agentpool_config => wolfharness_config}/lifecycle.py (100%) rename src/{agentpool_config => wolfharness_config}/loaders.py (98%) rename src/{agentpool_config => wolfharness_config}/mcp_server.py (100%) rename src/{agentpool_config => wolfharness_config}/model_capabilities.py (100%) rename src/{agentpool_config => wolfharness_config}/nodes.py (94%) rename src/{agentpool_config => wolfharness_config}/observability.py (99%) rename src/{agentpool_config => wolfharness_config}/output_types.py (94%) rename src/{agentpool_config => wolfharness_config}/paths.py (97%) rename src/{agentpool_config => wolfharness_config}/pool_server.py (100%) rename src/{agentpool_config => wolfharness_config}/prompt_hubs.py (86%) rename src/{agentpool_config => wolfharness_config}/prompts.py (99%) rename src/{agentpool_config => wolfharness_config}/py.typed (100%) rename src/{agentpool_config => wolfharness_config}/resolution.py (97%) rename src/{agentpool_config => wolfharness_config}/session.py (98%) rename src/{agentpool_config => wolfharness_config}/session_pool.py (97%) rename src/{agentpool_config => wolfharness_config}/skill_commands.py (100%) rename src/{agentpool_config => wolfharness_config}/skills.py (98%) rename src/{agentpool_config => wolfharness_config}/storage.py (94%) rename src/{agentpool_config => wolfharness_config}/system_prompts.py (97%) rename src/{agentpool_config => wolfharness_config}/task.py (89%) rename src/{agentpool_config => wolfharness_config}/team_mode.py (100%) rename src/{agentpool_config => wolfharness_config}/teams.py (95%) rename src/{agentpool_config => wolfharness_config}/tools.py (97%) rename src/{agentpool_config => wolfharness_config}/toolsets.py (91%) rename src/{agentpool_config/agentpool_tools.py => wolfharness_config/wolfharness_tools.py} (93%) rename src/{agentpool_config => wolfharness_config}/workers.py (100%) rename src/{agentpool_prompts => wolfharness_prompts}/__init__.py (100%) rename src/{agentpool_prompts => wolfharness_prompts}/braintrust_hub.py (97%) rename src/{agentpool_prompts => wolfharness_prompts}/fabric.py (93%) rename src/{agentpool_prompts => wolfharness_prompts}/langfuse_hub.py (92%) rename src/{agentpool_prompts => wolfharness_prompts}/promptlayer_provider.py (89%) rename src/{agentpool_prompts => wolfharness_prompts}/py.typed (100%) rename src/{agentpool_server => wolfharness_server}/AGENTS.md (97%) create mode 100644 src/wolfharness_server/__init__.py rename src/{agentpool_server => wolfharness_server}/a2a_server/__init__.py (100%) rename src/{agentpool_server => wolfharness_server}/a2a_server/a2a_types.py (100%) rename src/{agentpool_server => wolfharness_server}/a2a_server/agent_worker.py (99%) rename src/{agentpool_server => wolfharness_server}/a2a_server/server.py (94%) rename src/{agentpool_server => wolfharness_server}/a2a_server/storage.py (92%) create mode 100644 src/wolfharness_server/acp_server/__init__.py rename src/{agentpool_server => wolfharness_server}/acp_server/acp_agent.py (96%) rename src/{agentpool_server => wolfharness_server}/acp_server/acp_mcp_manager.py (99%) rename src/{agentpool_server => wolfharness_server}/acp_server/acp_mcp_transport.py (96%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/__init__.py (100%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/debug_commands.py (97%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/docs_commands/__init__.py (100%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/docs_commands/fetch_repo.py (96%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/docs_commands/get_schema.py (96%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/docs_commands/get_source.py (91%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/docs_commands/git_diff.py (94%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/docs_commands/helpers.py (100%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/docs_commands/url_to_markdown.py (92%) rename src/{agentpool_server => wolfharness_server}/acp_server/commands/skill_commands.py (93%) rename src/{agentpool_server => wolfharness_server}/acp_server/converters.py (94%) rename src/{agentpool_server => wolfharness_server}/acp_server/event_converter.py (99%) rename src/{agentpool_server => wolfharness_server}/acp_server/handler.py (97%) rename src/{agentpool_server => wolfharness_server}/acp_server/input_provider.py (98%) rename src/{agentpool_server => wolfharness_server}/acp_server/provider_router.py (97%) rename src/{agentpool_server => wolfharness_server}/acp_server/server.py (95%) rename src/{agentpool_server => wolfharness_server}/acp_server/session.py (91%) rename src/{agentpool_server => wolfharness_server}/acp_server/session_agent_mgmt.py (97%) rename src/{agentpool_server => wolfharness_server}/acp_server/session_events.py (92%) rename src/{agentpool_server => wolfharness_server}/acp_server/session_lifecycle.py (95%) rename src/{agentpool_server => wolfharness_server}/acp_server/session_manager.py (96%) rename src/{agentpool_server => wolfharness_server}/acp_server/syntax_detection.py (100%) rename src/{agentpool_server => wolfharness_server}/acp_server/zed_tools.md (100%) rename src/{agentpool_server => wolfharness_server}/aggregating_server.py (98%) rename src/{agentpool_server => wolfharness_server}/agui_server/__init__.py (63%) rename src/{agentpool_server => wolfharness_server}/agui_server/base_agent_adapter.py (98%) rename src/{agentpool_server => wolfharness_server}/agui_server/server.py (92%) rename src/{agentpool_server => wolfharness_server}/agui_server/skill_tools.py (97%) rename src/{agentpool_server => wolfharness_server}/base.py (98%) rename src/{agentpool_server => wolfharness_server}/http_server.py (92%) rename src/{agentpool_server => wolfharness_server}/mcp_server/__init__.py (58%) rename src/{agentpool_server => wolfharness_server}/mcp_server/server.py (93%) rename src/{agentpool_server => wolfharness_server}/mcp_server/zed_wrapper.py (94%) rename src/{agentpool_server => wolfharness_server}/mixins.py (97%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/__init__.py (100%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/completions/__init__.py (100%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/completions/helpers.py (93%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/completions/models.py (96%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/responses/__init__.py (100%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/responses/helpers.py (93%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/responses/models.py (100%) rename src/{agentpool_server => wolfharness_server}/openai_api_server/server.py (94%) rename src/{agentpool_server => wolfharness_server}/opencode_server/.rules (93%) rename src/{agentpool_server => wolfharness_server}/opencode_server/ENDPOINTS.md (100%) rename src/{agentpool_server => wolfharness_server}/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md (100%) rename src/{agentpool_server => wolfharness_server}/opencode_server/__init__.py (80%) rename src/{agentpool_server => wolfharness_server}/opencode_server/command_validation.py (100%) rename src/{agentpool_server => wolfharness_server}/opencode_server/converters.py (94%) rename src/{agentpool_server => wolfharness_server}/opencode_server/dependencies.py (77%) rename src/{agentpool_server => wolfharness_server}/opencode_server/event_adapter.py (93%) rename src/{agentpool_server => wolfharness_server}/opencode_server/event_bridge.py (94%) rename src/{agentpool_server => wolfharness_server}/opencode_server/event_processor.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/event_processor_context.py (96%) rename src/{agentpool_server => wolfharness_server}/opencode_server/input_provider.py (96%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/__init__.py (88%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/agent.py (96%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/app.py (98%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/base.py (100%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/common.py (95%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/config.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/diagnostics.py (100%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/events.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/file.py (91%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/mcp.py (94%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/message.py (98%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/parts.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/provider.py (96%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/pty.py (94%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/question.py (94%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/question_permission.py (100%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/session.py (95%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/session_info.py (100%) rename src/{agentpool_server => wolfharness_server}/opencode_server/models/tool_metadata.py (99%) rename src/{agentpool_server => wolfharness_server}/opencode_server/opencode_event_bridge.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/opencode_message_bridge.py (96%) rename src/{agentpool_server => wolfharness_server}/opencode_server/opencode_session_routes.py (95%) rename src/{agentpool_server => wolfharness_server}/opencode_server/provider_auth.py (98%) create mode 100644 src/wolfharness_server/opencode_server/routes/__init__.py rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/agent_routes.py (95%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/app_routes.py (95%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/config_routes.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/file_routes.py (98%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/global_routes.py (96%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/lsp_routes.py (98%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/message_routes.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/permission_routes.py (93%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/pty_routes.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/question_routes.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/routing.py (93%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/session_routes.py (98%) rename src/{agentpool_server => wolfharness_server}/opencode_server/routes/tui_routes.py (97%) rename src/{agentpool_server => wolfharness_server}/opencode_server/server.py (93%) rename src/{agentpool_server => wolfharness_server}/opencode_server/session_pool_integration.py (84%) rename src/{agentpool_server => wolfharness_server}/opencode_server/skill_bridge.py (98%) rename src/{agentpool_server => wolfharness_server}/opencode_server/state.py (89%) rename src/{agentpool_server => wolfharness_server}/opencode_server/stream_adapter.py (93%) rename src/{agentpool_server => wolfharness_server}/opencode_server/todo_utils.py (91%) rename src/{agentpool_server => wolfharness_server}/py.typed (100%) rename src/{agentpool_server => wolfharness_server}/shared/__init__.py (87%) rename src/{agentpool_server => wolfharness_server}/shared/constants.py (100%) rename src/{agentpool_server => wolfharness_server}/shared/model_utils.py (95%) rename src/{agentpool_storage => wolfharness_storage}/__init__.py (76%) rename src/{agentpool_storage => wolfharness_storage}/acp_provider/__init__.py (59%) rename src/{agentpool_storage => wolfharness_storage}/acp_provider/provider.py (91%) rename src/{agentpool_storage => wolfharness_storage}/adapter.py (95%) rename src/{agentpool_storage => wolfharness_storage}/base.py (98%) create mode 100644 src/wolfharness_storage/file_provider/__init__.py rename src/{agentpool_storage => wolfharness_storage}/file_provider/provider.py (96%) rename src/{agentpool_storage => wolfharness_storage}/formatters.py (98%) create mode 100644 src/wolfharness_storage/memory_provider/__init__.py rename src/{agentpool_storage => wolfharness_storage}/memory_provider/provider.py (97%) rename src/{agentpool_storage => wolfharness_storage}/models.py (97%) rename src/{agentpool_storage => wolfharness_storage}/opencode_provider/ARCHITECTURE.md (98%) rename src/{agentpool_storage => wolfharness_storage}/opencode_provider/__init__.py (85%) rename src/{agentpool_storage => wolfharness_storage}/opencode_provider/helpers.py (96%) rename src/{agentpool_storage => wolfharness_storage}/opencode_provider/provider.py (97%) rename src/{agentpool_storage => wolfharness_storage}/project_store.py (93%) rename src/{agentpool_storage => wolfharness_storage}/protocols.py (96%) rename src/{agentpool_storage => wolfharness_storage}/py.typed (100%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/__init__.py (71%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/cli.py (94%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/models.py (98%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/queries.py (100%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/sql_messages.py (96%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/sql_projects.py (94%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/sql_provider.py (87%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/sql_sessions.py (96%) rename src/{agentpool_storage => wolfharness_storage}/sql_provider/utils.py (92%) rename src/{agentpool_storage => wolfharness_storage}/zed_provider/__init__.py (84%) rename src/{agentpool_storage => wolfharness_storage}/zed_provider/helpers.py (96%) rename src/{agentpool_storage => wolfharness_storage}/zed_provider/models.py (100%) rename src/{agentpool_storage => wolfharness_storage}/zed_provider/provider.py (96%) rename src/{agentpool_sync => wolfharness_sync}/__init__.py (74%) rename src/{agentpool_sync => wolfharness_sync}/config.py (95%) rename src/{agentpool_sync => wolfharness_sync}/git.py (100%) rename src/{agentpool_sync => wolfharness_sync}/handlers.py (98%) rename src/{agentpool_sync => wolfharness_sync}/manager.py (96%) rename src/{agentpool_sync => wolfharness_sync}/models.py (100%) rename src/{agentpool_sync => wolfharness_sync}/packages.py (100%) rename src/{agentpool_sync => wolfharness_sync}/parsers/__init__.py (100%) rename src/{agentpool_sync => wolfharness_sync}/parsers/helpers.py (100%) rename src/{agentpool_sync => wolfharness_sync}/parsers/markdown_parser.py (93%) rename src/{agentpool_sync => wolfharness_sync}/parsers/protocol.py (91%) rename src/{agentpool_sync => wolfharness_sync}/parsers/python_parser.py (96%) rename src/{agentpool_sync => wolfharness_sync}/resources.py (100%) create mode 100644 src/wolfharness_toolsets/__init__.py create mode 100644 src/wolfharness_toolsets/builtin/__init__.py rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/code.py (97%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/debug.py (95%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/execution_environment.py (96%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/file_edit/__init__.py (100%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/file_edit/file_edit.py (95%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/question_tools.py (97%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/skills.py (95%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/subagent_tools.py (95%) rename src/{agentpool_toolsets => wolfharness_toolsets}/builtin/workers.py (89%) rename src/{agentpool_toolsets => wolfharness_toolsets}/composio_toolset.py (93%) rename src/{agentpool_toolsets => wolfharness_toolsets}/config_creation.py (98%) rename src/{agentpool_toolsets => wolfharness_toolsets}/cron.py (92%) rename src/{agentpool_toolsets => wolfharness_toolsets}/entry_points.py (87%) rename src/{agentpool_toolsets => wolfharness_toolsets}/fsspec_toolset/__init__.py (55%) rename src/{agentpool_toolsets => wolfharness_toolsets}/fsspec_toolset/diagnostics.py (99%) rename src/{agentpool_toolsets => wolfharness_toolsets}/fsspec_toolset/grep.py (99%) rename src/{agentpool_toolsets => wolfharness_toolsets}/fsspec_toolset/helpers.py (99%) rename src/{agentpool_toolsets => wolfharness_toolsets}/fsspec_toolset/image_utils.py (100%) rename src/{agentpool_toolsets => wolfharness_toolsets}/fsspec_toolset/streaming_diff_parser.py (100%) rename src/{agentpool_toolsets => wolfharness_toolsets}/fsspec_toolset/toolset.py (96%) rename src/{agentpool_toolsets => wolfharness_toolsets}/mcp_discovery/__init__.py (60%) rename src/{agentpool_toolsets => wolfharness_toolsets}/mcp_discovery/data/mcp_servers.parquet (100%) rename src/{agentpool_toolsets => wolfharness_toolsets}/mcp_discovery/toolset.py (96%) rename src/{agentpool_toolsets => wolfharness_toolsets}/mcp_run_toolset.py (96%) rename src/{agentpool_toolsets => wolfharness_toolsets}/notifications.py (96%) rename src/{agentpool_toolsets => wolfharness_toolsets}/openapi.py (96%) rename src/{agentpool_toolsets => wolfharness_toolsets}/py.typed (100%) rename src/{agentpool_toolsets => wolfharness_toolsets}/search_toolset.py (94%) rename src/{agentpool_toolsets => wolfharness_toolsets}/streaming_tools.py (97%) rename src/{agentpool_toolsets => wolfharness_toolsets}/vfs_toolset.py (97%) rename agentpool.spec => wolfharness.spec (86%) diff --git a/.agents/skills/sync-acp-spec/SKILL.md b/.agents/skills/sync-acp-spec/SKILL.md index 72c5bef43..e074c31a7 100644 --- a/.agents/skills/sync-acp-spec/SKILL.md +++ b/.agents/skills/sync-acp-spec/SKILL.md @@ -74,9 +74,9 @@ Keep `src/acp/schema/` aligned with the official Agent Client Protocol reference - `agent/implementations/debug_server/mock_agent.py` — add stubs to `MockAgent` 7. **Wire up in the ACP server** (if needed): - - `src/agentpool_server/acp_server/acp_agent.py` — implement new methods, advertise new capabilities - - `src/agentpool_server/acp_server/event_converter.py` — emit new session update types - - `src/agentpool_server/acp_server/session.py` — store new state + - `src/wolfharness_server/acp_server/acp_agent.py` — implement new methods, advertise new capabilities + - `src/wolfharness_server/acp_server/event_converter.py` — emit new session update types + - `src/wolfharness_server/acp_server/session.py` — store new state 8. **Update the synced commit hash** in `src/acp/__init__.py`: ```bash @@ -106,7 +106,7 @@ Keep `src/acp/schema/` aligned with the official Agent Client Protocol reference - `connection.py` (base JSON-RPC connection) — only if the wire protocol itself changes - `transports.py` — transport layer is our own - `stdio.py` — our process management layer -- `client/` implementations (`DefaultACPClient`, `HeadlessACPClient`, `NoOpClient`) — agentpool-specific +- `client/` implementations (`DefaultACPClient`, `HeadlessACPClient`, `NoOpClient`) — wolfharness-specific - `registry/` — our agent registry, not part of ACP spec - `task/` — our task abstraction - `tool_call_reporter.py`, `tool_call_state.py` — our extensions diff --git a/.agents/skills/sync-skills-spec/SKILL.md b/.agents/skills/sync-skills-spec/SKILL.md index 55d98fc6b..e32b48b66 100644 --- a/.agents/skills/sync-skills-spec/SKILL.md +++ b/.agents/skills/sync-skills-spec/SKILL.md @@ -1,11 +1,11 @@ --- name: sync-skills-spec -description: Sync the agentpool skills implementation with the official Agent Skills Spec by comparing against the reference repository. +description: Sync the wolfharness skills implementation with the official Agent Skills Spec by comparing against the reference repository. --- # Sync Skills Spec -Keep `src/agentpool/skills/` aligned with the official Agent Skills Spec. +Keep `src/wolfharness/skills/` aligned with the official Agent Skills Spec. ## Steps @@ -21,18 +21,18 @@ Keep `src/agentpool/skills/` aligned with the official Agent Skills Spec. - Focus on the `SkillProperties` model, frontmatter fields, validation rules, and prompt generation 3. **Compare with our implementation**: - - `src/agentpool/skills/skill.py` — our `Skill` Pydantic model, validators, `parse_frontmatter()`, `to_prompt()` - - `src/agentpool/skills/registry.py` — discovery and registration (our extension, not part of the spec) - - `src/agentpool/skills/manager.py` — pool-wide management (our extension) + - `src/wolfharness/skills/skill.py` — our `Skill` Pydantic model, validators, `parse_frontmatter()`, `to_prompt()` + - `src/wolfharness/skills/registry.py` — discovery and registration (our extension, not part of the spec) + - `src/wolfharness/skills/manager.py` — pool-wide management (our extension) - Check for missing fields, changed validation rules, new spec requirements 4. **Apply updates** to our code: - Add any new frontmatter fields to the `Skill` model - Update validators to match spec changes (name format, length limits, allowed fields) - Update `to_prompt()` if the recommended XML format changed - - Do NOT replace our registry/manager — those are agentpool-specific extensions + - Do NOT replace our registry/manager — those are wolfharness-specific extensions -5. **Update the synced commit hash** in `src/agentpool/skills/skill.py`: +5. **Update the synced commit hash** in `src/wolfharness/skills/skill.py`: - Get the current HEAD of the cloned repo: `git -C "$tmp/agentskills" rev-parse HEAD` - Update the `SPEC_SYNCED_COMMIT` constant to the new hash - This tracks which version of the spec we last synced against diff --git a/.copier-answers.yml b/.copier-answers.yml index 821d79946..3fba1c8c4 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -13,9 +13,9 @@ project_description: Pydantic-AI based Multi-Agent Framework with YAML-based Age Teams, Workflows & Extended ACP / AGUI integration project_name: AgentPool python_minimum_version: "3.13" -python_package_command_line_name: agentpool -python_package_distribution_name: agentpool -python_package_import_name: agentpool -repository_name: agentpool +python_package_command_line_name: wolfharness +python_package_distribution_name: wolfharness +python_package_import_name: wolfharness +repository_name: wolfharness repository_namespace: phil65 repository_provider: github.com diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f52ec6e10..b7d03d44f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,7 +54,7 @@ jobs: env: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: uv run --no-group docs pytest --cov-report=xml --cov=src/agentpool/ --cov-report=term-missing + run: uv run --no-group docs pytest --cov-report=xml --cov=src/wolfharness/ --cov-report=term-missing - name: VCR tests run: uv run pytest tests/vcr/ --strict-vcr-cassette-usage --tb=short --no-cov -q @@ -146,7 +146,7 @@ jobs: run: uv sync --all-extras - name: Build with PyInstaller - run: uv run pyinstaller agentpool.spec + run: uv run pyinstaller wolfharness.spec - name: Get package version (Unix) id: get_version_unix @@ -167,23 +167,23 @@ jobs: if: ${{ matrix.os != 'windows' }} run: | cd dist - zip agentpool-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_unix.outputs.version }}.zip agentpool + zip wolfharness-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_unix.outputs.version }}.zip wolfharness - name: Zip binary (Windows) if: ${{ matrix.os == 'windows' }} shell: pwsh run: | cd dist - Compress-Archive -Path agentpool.exe -DestinationPath agentpool-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_windows.outputs.version }}.zip + Compress-Archive -Path wolfharness.exe -DestinationPath wolfharness-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_windows.outputs.version }}.zip - name: Upload binary to release (Unix) if: ${{ matrix.os != 'windows' }} uses: softprops/action-gh-release@v2 with: - files: dist/agentpool-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_unix.outputs.version }}.zip + files: dist/wolfharness-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_unix.outputs.version }}.zip - name: Upload binary to release (Windows) if: ${{ matrix.os == 'windows' }} uses: softprops/action-gh-release@v2 with: - files: dist/agentpool-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_windows.outputs.version }}.zip + files: dist/wolfharness-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.get_version_windows.outputs.version }}.zip diff --git a/.github/workflows/publish_docker.yml b/.github/workflows/publish_docker.yml index 638003471..b4e3797e7 100644 --- a/.github/workflows/publish_docker.yml +++ b/.github/workflows/publish_docker.yml @@ -33,7 +33,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ghcr.io/${{ github.repository }}/agentpool + images: ghcr.io/${{ github.repository }}/wolfharness tags: | type=semver,pattern={{version}} type=ref,event=branch diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 0f9d87135..7af358b19 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -131,7 +131,7 @@ jobs: run: uv sync --all-extras --no-group lint - name: Import smoke test run: | - uv run python -c "mods=['agentpool','agentpool_config','agentpool_cli','agentpool_commands','agentpool_storage','agentpool_prompts','agentpool_server','agentpool_toolsets','acp'];[(__import__(m),print(f' OK {m}')) for m in mods];print(f'All {len(mods)} modules imported successfully')" + uv run python -c "mods=['wolfharness','wolfharness_config','wolfharness_cli','wolfharness_commands','wolfharness_storage','wolfharness_prompts','wolfharness_server','wolfharness_toolsets','acp'];[(__import__(m),print(f' OK {m}')) for m in mods];print(f'All {len(mods)} modules imported successfully')" - name: Test collection check run: uv run pytest --co -q 2>&1 | tail -3 diff --git a/.opencode/agents/review-code.md b/.opencode/agents/review-code.md index 312e0cc0d..f07aec8b5 100644 --- a/.opencode/agents/review-code.md +++ b/.opencode/agents/review-code.md @@ -44,8 +44,8 @@ code changes in a pull request and report findings. You do NOT edit files. - No `asyncio.create_task()` without an active span 5. **Architecture** - - Config models import from `agentpool_config.*`, not `agentpool.models` - - New modules follow existing patterns in `src/agentpool/` + - Config models import from `wolfharness_config.*`, not `wolfharness.models` + - New modules follow existing patterns in `src/wolfharness/` - New capabilities registered properly in the capability system ## Output Format diff --git a/.opencode/agents/review-docs.md b/.opencode/agents/review-docs.md index 7d6a58936..24907434b 100644 --- a/.opencode/agents/review-docs.md +++ b/.opencode/agents/review-docs.md @@ -34,15 +34,15 @@ documentation. You do NOT edit files. ## What to Check 1. **Context Loading Table** - - New `src/agentpool/**/AGENTS.md` files MUST be registered in the root + - New `src/wolfharness/**/AGENTS.md` files MUST be registered in the root `AGENTS.md` Context Loading table. - Removed subsystem AGENTS.md files MUST be removed from the table. 2. **Subsystem Documentation** - - New modules under `src/agentpool/` that introduce a new directory SHOULD have + - New modules under `src/wolfharness/` that introduce a new directory SHOULD have a corresponding `AGENTS.md` in that directory. - - Changes to `src/agentpool/lifecycle/`, `src/agentpool/capabilities/`, - `src/agentpool/skills/`, `src/agentpool/hooks/` MUST check whether the + - Changes to `src/wolfharness/lifecycle/`, `src/wolfharness/capabilities/`, + `src/wolfharness/skills/`, `src/wolfharness/hooks/` MUST check whether the subsystem AGENTS.md needs updating. 3. **Explanation Docs** diff --git a/.opencode/agents/review-lead.md b/.opencode/agents/review-lead.md index 29e6b6c21..17728f288 100644 --- a/.opencode/agents/review-lead.md +++ b/.opencode/agents/review-lead.md @@ -55,10 +55,10 @@ single review comment. You do NOT edit files or run commands. ## When to Invoke review-docs Always invoke `@review-docs` when the PR: -- Adds or removes files under `src/agentpool/` +- Adds or removes files under `src/wolfharness/` - Modifies any `AGENTS.md` - Modifies anything under `docs/` -- Adds a new directory under `src/agentpool/` (potential new subsystem) +- Adds a new directory under `src/wolfharness/` (potential new subsystem) - Changes code style conventions, testing rules, or telemetry rules - Adds or removes lifecycle dimensions, capabilities, protocols, or tools diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5247c6dd1..3d6268b6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -61,7 +61,7 @@ repos: name: check-jsonschema entry: uv run check-jsonschema --schemafile schema/config-schema.json language: system - files: ^src/agentpool/config_resources/.*\.yml$|^docs/examples/.*/config\.yml$ + files: ^src/wolfharness/config_resources/.*\.yml$|^docs/examples/.*/config\.yml$ require_serial: true # Phase 4: pre-push — heavier checks, only when pushing (~30-60s) diff --git a/AGENTS.md b/AGENTS.md index 071c29056..70ba158c7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,8 +22,8 @@ uv run ruff check src/ # Lint uv run ruff format src/ # Format uv run --no-group docs mypy src/ # Type check duty lint # All checks -agentpool run "prompt" # Run agent -agentpool serve-acp config.yml # Start ACP server +wolfharness run "prompt" # Run agent +wolfharness serve-acp config.yml # Start ACP server ``` ## Code Style — Red Lines @@ -32,7 +32,7 @@ agentpool serve-acp config.yml # Start ACP server - PEP 8 via Ruff. Google-style docstrings (no types in Args section). - Type hints required (`mypy --strict`). **Never** use `as any`, `@ts-ignore`, or type suppressions. - `from __future__ import annotations` for forward references. -- Use `TYPE_CHECKING` blocks to avoid circular imports. Config models import from `agentpool_config.*`, not `agentpool.models`. +- Use `TYPE_CHECKING` blocks to avoid circular imports. Config models import from `wolfharness_config.*`, not `wolfharness.models`. - No `getattr`/`hasattr` — provide full type safety. - NEVER use shortcuts or leave TODOs unless explicitly asked. @@ -50,28 +50,28 @@ Instrument critical-path code (RunLoop, Turn, delegation, protocol entry points) ## Key Files -- `src/agentpool/delegation/pool.py` — AgentPool orchestration -- `src/agentpool/agents/agent.py` — Native agent implementation -- `src/agentpool/messaging/messagenode.py` — MessageNode base abstraction -- `src/agentpool/orchestrator/core.py` — EventBus, SessionController -- `src/agentpool/orchestrator/run.py` — RunHandle (RunLoop) lifecycle -- `src/agentpool/models/manifest.py` — Configuration schema -- `src/agentpool/capabilities/` — Capability system (M3) -- `src/agentpool/lifecycle/` — Lifecycle dimensions (M2) +- `src/wolfharness/delegation/pool.py` — AgentPool orchestration +- `src/wolfharness/agents/agent.py` — Native agent implementation +- `src/wolfharness/messaging/messagenode.py` — MessageNode base abstraction +- `src/wolfharness/orchestrator/core.py` — EventBus, SessionController +- `src/wolfharness/orchestrator/run.py` — RunHandle (RunLoop) lifecycle +- `src/wolfharness/models/manifest.py` — Configuration schema +- `src/wolfharness/capabilities/` — Capability system (M3) +- `src/wolfharness/lifecycle/` — Lifecycle dimensions (M2) ## Context Loading | Working on | Read this | |---|---| -| RunLoop / Turn / EventBus | `src/agentpool/orchestrator/AGENTS.md` | -| Lifecycle dimensions | `src/agentpool/lifecycle/AGENTS.md` | -| Capabilities / tools | `src/agentpool/capabilities/AGENTS.md` | -| Skills system | `src/agentpool/skills/AGENTS.md` | -| Hooks system | `src/agentpool/hooks/AGENTS.md` | +| RunLoop / Turn / EventBus | `src/wolfharness/orchestrator/AGENTS.md` | +| Lifecycle dimensions | `src/wolfharness/lifecycle/AGENTS.md` | +| Capabilities / tools | `src/wolfharness/capabilities/AGENTS.md` | +| Skills system | `src/wolfharness/skills/AGENTS.md` | +| Hooks system | `src/wolfharness/hooks/AGENTS.md` | | Tests | `tests/AGENTS.md` | -| Protocol servers | `src/agentpool_server/AGENTS.md` | +| Protocol servers | `src/wolfharness_server/AGENTS.md` | | ACP protocol | `src/acp/AGENTS.md` | -| Core framework overview | `src/agentpool/AGENTS.md` | +| Core framework overview | `src/wolfharness/AGENTS.md` | | Architecture deep-dives | `docs/explanation/` | | Module structure | `docs/explanation/module-structure.md` | | Graph architecture | `docs/explanation/graph-architecture.md` | @@ -83,7 +83,7 @@ Instrument critical-path code (RunLoop, Turn, delegation, protocol entry points) | ToolDisplayCapability (tool rename + diff events) | `docs/explanation/tool-display-capability.md` | | Telemetry rules | `docs/explanation/telemetry.md` | | Usage examples | `docs/explanation/usage-examples.md` | -| Extending AgentPool | `docs/explanation/extending-agentpool.md` | +| Extending AgentPool | `docs/explanation/extending-wolfharness.md` | | Where to put new docs | `docs/meta/documentation-guide.md` | ## Tool Compatibility diff --git a/Dockerfile b/Dockerfile index 36466e125..25bafd132 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,5 +9,5 @@ RUN pip install --no-cache-dir "." EXPOSE 8000 # Run the server -ENTRYPOINT ["agentpool", "serve-api"] +ENTRYPOINT ["wolfharness", "serve-api"] CMD ["--auto-discover"] diff --git a/README.md b/README.md index a9a85dab6..06d81ae99 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # AgentPool -[![PyPI License](https://img.shields.io/pypi/l/agentpool.svg)](https://pypi.org/project/agentpool/) -[![Package status](https://img.shields.io/pypi/status/agentpool.svg)](https://pypi.org/project/agentpool/) -[![Monthly downloads](https://img.shields.io/pypi/dm/agentpool.svg)](https://pypi.org/project/agentpool/) -[![Python version](https://img.shields.io/pypi/pyversions/agentpool.svg)](https://pypi.org/project/agentpool/) -[![Github Stars](https://img.shields.io/github/stars/Leoyzen/agentpool)](https://github.com/Leoyzen/agentpool/stars) +[![PyPI License](https://img.shields.io/pypi/l/wolfharness.svg)](https://pypi.org/project/wolfharness/) +[![Package status](https://img.shields.io/pypi/status/wolfharness.svg)](https://pypi.org/project/wolfharness/) +[![Monthly downloads](https://img.shields.io/pypi/dm/wolfharness.svg)](https://pypi.org/project/wolfharness/) +[![Python version](https://img.shields.io/pypi/pyversions/wolfharness.svg)](https://pypi.org/project/wolfharness/) +[![Github Stars](https://img.shields.io/github/stars/Leoyzen/wolfharness)](https://github.com/Leoyzen/wolfharness/stars) **A unified agent orchestration hub that lets you configure and manage heterogeneous AI agents via YAML and expose them through standardized protocols.** -[Documentation](https://leoyzen.github.io/agentpool/) +[Documentation](https://leoyzen.github.io/wolfharness/) ## The Problem @@ -48,7 +48,7 @@ flowchart TB ## Quick Start ```bash -uv tool install agentpool +uv tool install wolfharness ``` @@ -65,10 +65,10 @@ agents: ```bash # Run via CLI -agentpool run assistant "Hello!" +wolfharness run assistant "Hello!" # Or start as ACP server (for Zed, Toad, etc.) -agentpool serve-acp agents.yml +wolfharness serve-acp agents.yml ``` ### Integrating External Agents @@ -154,11 +154,11 @@ AgentPool can expose your agents through multiple server protocols: | Server | Command | Use Case | |--------|---------|----------| -| **ACP** | `agentpool serve-acp` | IDE integration (Zed, Toad) - bidirectional communication with tool confirmations | -| **OpenCode** | `agentpool serve-opencode` | OpenCode TUI/Desktop - supports remote filesystems via fsspec | -| **MCP** | `agentpool serve-mcp` | Expose tools to other agents | -| AG-UI | `agentpool serve-agui` | AG-UI compatible frontends | -| OpenAI API | `agentpool serve-api` | Drop-in OpenAI API replacement | +| **ACP** | `wolfharness serve-acp` | IDE integration (Zed, Toad) - bidirectional communication with tool confirmations | +| **OpenCode** | `wolfharness serve-opencode` | OpenCode TUI/Desktop - supports remote filesystems via fsspec | +| **MCP** | `wolfharness serve-mcp` | Expose tools to other agents | +| AG-UI | `wolfharness serve-agui` | AG-UI compatible frontends | +| OpenAI API | `wolfharness serve-api` | Drop-in OpenAI API replacement | The **ACP server** is ideal for IDE integration - it provides real-time tool confirmations and session management. The **OpenCode server** enables the OpenCode TUI to control AgentPool agents, including agents operating on remote environments (Docker, SSH, cloud sandboxes). @@ -176,18 +176,18 @@ The **ACP server** is ideal for IDE integration - it provides real-time tool con ### CLI ```bash -agentpool run agent_name "prompt" # Single run -agentpool serve-acp config.yml # ACP server for IDEs -agentpool serve-opencode config.yml # OpenCode TUI server -agentpool serve-mcp config.yml # MCP server -agentpool watch --config agents.yml # React to triggers -agentpool history stats --group-by model # View analytics +wolfharness run agent_name "prompt" # Single run +wolfharness serve-acp config.yml # ACP server for IDEs +wolfharness serve-opencode config.yml # OpenCode TUI server +wolfharness serve-mcp config.yml # MCP server +wolfharness watch --config agents.yml # React to triggers +wolfharness history stats --group-by model # View analytics ``` ### Programmatic ```python -from agentpool import AgentPool +from wolfharness import AgentPool async with AgentPool("agents.yml") as pool: agent = pool.get_agent("assistant") @@ -205,6 +205,6 @@ async with AgentPool("agents.yml") as pool: ## Documentation -For complete documentation including advanced configuration, connection patterns, and API reference, visit [leoyzen.github.io/agentpool](https://leoyzen.github.io/agentpool/). +For complete documentation including advanced configuration, connection patterns, and API reference, visit [leoyzen.github.io/wolfharness](https://leoyzen.github.io/wolfharness/). # test # final test diff --git a/agentpool-session-pool/docs/ops/acp-handler.md b/agentpool-session-pool/docs/ops/acp-handler.md index 3460fb83e..6c853d580 100644 --- a/agentpool-session-pool/docs/ops/acp-handler.md +++ b/agentpool-session-pool/docs/ops/acp-handler.md @@ -1,6 +1,6 @@ # ACP Handler Ops Playbook -Operational runbook for the `ACPProtocolHandler` (`agentpool_server.acp_server.handler`). +Operational runbook for the `ACPProtocolHandler` (`wolfharness_server.acp_server.handler`). ## Feature Flag Toggle @@ -27,7 +27,7 @@ agents: ### Check at runtime ```python -from agentpool_server.acp_server.handler import ACPProtocolHandler +from wolfharness_server.acp_server.handler import ACPProtocolHandler handler = ACPProtocolHandler(agent_pool, event_converter, client) enabled = handler._should_use_session_pool() diff --git a/agentpool-session-pool/docs/ops/opencode-handler.md b/agentpool-session-pool/docs/ops/opencode-handler.md index 57a587ce9..9a1417659 100644 --- a/agentpool-session-pool/docs/ops/opencode-handler.md +++ b/agentpool-session-pool/docs/ops/opencode-handler.md @@ -1,6 +1,6 @@ # OpenCode Handler Ops Playbook -Operational runbook for the `OpenCodeProtocolHandler` (`agentpool_server.opencode_server.handler`). +Operational runbook for the `OpenCodeProtocolHandler` (`wolfharness_server.opencode_server.handler`). ## SSE Connection Troubleshooting @@ -22,7 +22,7 @@ Operational runbook for the `OpenCodeProtocolHandler` (`agentpool_server.opencod 3. **Verify events are being published:** ```python - from agentpool.orchestrator.metrics import MetricsCollector + from wolfharness.orchestrator.metrics import MetricsCollector collector = MetricsCollector(session_pool) metrics = await collector.get_metrics() print(metrics.event_bus_queue_depth.get(session_id, 0)) @@ -30,7 +30,7 @@ Operational runbook for the `OpenCodeProtocolHandler` (`agentpool_server.opencod 4. **Verify SSE broadcast is working:** ```python - from agentpool_server.opencode_server.models.events import SessionIdleEvent + from wolfharness_server.opencode_server.models.events import SessionIdleEvent await state.broadcast_event(SessionIdleEvent.create(session_id)) # Client should receive this immediately ``` diff --git a/agentpool-session-pool/docs/ops/session-pool.md b/agentpool-session-pool/docs/ops/session-pool.md index 862a69c58..f7c7db4c9 100644 --- a/agentpool-session-pool/docs/ops/session-pool.md +++ b/agentpool-session-pool/docs/ops/session-pool.md @@ -4,23 +4,23 @@ Operational runbook for the `SessionPool` orchestration layer. ## Dashboard Compatibility -All Prometheus metric names use the `agentpool_` prefix and identical label names to the pre-SessionPool Grafana dashboards. **No dashboard changes are required.** +All Prometheus metric names use the `wolfharness_` prefix and identical label names to the pre-SessionPool Grafana dashboards. **No dashboard changes are required.** | Metric | Type | Description | |--------|------|-------------| -| `agentpool_sessions_total` | gauge | Active sessions | -| `agentpool_active_turns_total` | gauge | Turns in progress | -| `agentpool_auto_resume_total` | counter | Auto-resume iterations | -| `agentpool_event_bus_subscribers` | gauge | Subscribers per session (`session_id` label) | -| `agentpool_session_lifetime_seconds` | gauge | Average closed-session lifetime | -| `agentpool_turn_latency_ms` | summary | Turn latency p99 (`quantile="0.99"`) | +| `wolfharness_sessions_total` | gauge | Active sessions | +| `wolfharness_active_turns_total` | gauge | Turns in progress | +| `wolfharness_auto_resume_total` | counter | Auto-resume iterations | +| `wolfharness_event_bus_subscribers` | gauge | Subscribers per session (`session_id` label) | +| `wolfharness_session_lifetime_seconds` | gauge | Average closed-session lifetime | +| `wolfharness_turn_latency_ms` | summary | Turn latency p99 (`quantile="0.99"`) | Call `metrics.to_prometheus()` on a `SessionPoolMetrics` snapshot to emit these lines for scraping. ## Startup ```python -from agentpool.orchestrator import SessionPool +from wolfharness.orchestrator import SessionPool session_pool = SessionPool(agent_pool) await session_pool.start() @@ -53,7 +53,7 @@ assert session_pool.event_bus is not None ### Readiness (Recommended) ```python -from agentpool.orchestrator.metrics import MetricsCollector +from wolfharness.orchestrator.metrics import MetricsCollector collector = MetricsCollector(session_pool) metrics = await collector.get_metrics() @@ -94,7 +94,7 @@ print(f"Event bus subscribers: {metrics.event_bus_queue_depth}") **Diagnosis:** -- `agentpool_event_bus_subscribers` gauge will show high counts for specific sessions. +- `wolfharness_event_bus_subscribers` gauge will show high counts for specific sessions. - Check consumer task logs — look for "Event consumer cancelled" or uncaught exceptions. **Remediation:** @@ -137,18 +137,18 @@ print(metrics.to_prometheus()) Example output: ```text -# TYPE agentpool_sessions_total gauge -agentpool_sessions_total 42 -# TYPE agentpool_active_turns_total gauge -agentpool_active_turns_total 3 -# TYPE agentpool_auto_resume_total counter -agentpool_auto_resume_total 17 -# TYPE agentpool_event_bus_subscribers gauge -agentpool_event_bus_subscribers{session_id="sess_abc"} 2 -# TYPE agentpool_session_lifetime_seconds gauge -agentpool_session_lifetime_seconds 1245.300 -# TYPE agentpool_turn_latency_ms summary -agentpool_turn_latency_ms{quantile="0.99"} 150.000 +# TYPE wolfharness_sessions_total gauge +wolfharness_sessions_total 42 +# TYPE wolfharness_active_turns_total gauge +wolfharness_active_turns_total 3 +# TYPE wolfharness_auto_resume_total counter +wolfharness_auto_resume_total 17 +# TYPE wolfharness_event_bus_subscribers gauge +wolfharness_event_bus_subscribers{session_id="sess_abc"} 2 +# TYPE wolfharness_session_lifetime_seconds gauge +wolfharness_session_lifetime_seconds 1245.300 +# TYPE wolfharness_turn_latency_ms summary +wolfharness_turn_latency_ms{quantile="0.99"} 150.000 ``` ## Emergency Procedures diff --git a/alembic.ini b/alembic.ini index de22e69d2..a95666a9b 100644 --- a/alembic.ini +++ b/alembic.ini @@ -85,7 +85,7 @@ path_separator = os # other means of configuring database URLs may be customized within the env.py # file. # Default to SQLite database in project root -sqlalchemy.url = sqlite:///./agentpool.db +sqlalchemy.url = sqlite:///./wolfharness.db [post_write_hooks] diff --git a/benchmarks/capability_overhead.py b/benchmarks/capability_overhead.py index 207b0dadd..39565eaee 100644 --- a/benchmarks/capability_overhead.py +++ b/benchmarks/capability_overhead.py @@ -27,14 +27,14 @@ import tracemalloc from typing import Any -from agentpool.resource_providers import StaticResourceProvider from pydantic_ai.models.test import TestModel +from wolfharness.resource_providers import StaticResourceProvider -from agentpool import Agent -from agentpool.hooks import AgentHooks -from agentpool.hooks.base import Hook, HookInput, HookResult -from agentpool.mcp_server.manager import MCPManager -from agentpool.tools import Tool, ToolManager +from wolfharness import Agent +from wolfharness.hooks import AgentHooks +from wolfharness.hooks.base import Hook, HookInput, HookResult +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.tools import Tool, ToolManager # --------------------------------------------------------------------------- diff --git a/compose.yml b/compose.yml index b672431e6..aec913654 100644 --- a/compose.yml +++ b/compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: - agentpool: + wolfharness: build: context: . dockerfile: Dockerfile diff --git a/diagnostic_pool_skills.py b/diagnostic_pool_skills.py index dfabe73a9..bd81e574c 100644 --- a/diagnostic_pool_skills.py +++ b/diagnostic_pool_skills.py @@ -7,9 +7,9 @@ import sys -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool/src") +sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") -from agentpool import AgentPool +from wolfharness import AgentPool async def main(): diff --git a/distribution/zed/extension.toml b/distribution/zed/extension.toml index c2d7d20a2..aa53b4409 100644 --- a/distribution/zed/extension.toml +++ b/distribution/zed/extension.toml @@ -1,41 +1,41 @@ -id = "agentpool" +id = "wolfharness" name = "AgentPool" description = "Pydantic-AI based Multi-Agent Framework with YAML-based Agents, Teams, Workflows & Extended ACP / AGUI integration" version = "1.15.12" schema_version = 1 authors = ["Philipp Temminghoff"] -repository = "https://github.com/phil65/agentpool" +repository = "https://github.com/phil65/wolfharness" -[agent_servers.agentpool] +[agent_servers.wolfharness] name = "AgentPool" -icon = "./icons/agentpool.svg" +icon = "./icons/wolfharness.svg" -[agent_servers.agentpool.targets.darwin-aarch64] -archive = "https://github.com/phil65/agentpool/releases/download/v2.9.5/agentpool-darwin-aarch64-1.15.12.zip" -cmd = "./agentpool" +[agent_servers.wolfharness.targets.darwin-aarch64] +archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-darwin-aarch64-1.15.12.zip" +cmd = "./wolfharness" args = ["serve-acp"] -[agent_servers.agentpool.targets.darwin-x86_64] -archive = "https://github.com/phil65/agentpool/releases/download/v2.9.5/agentpool-darwin-x86_64-1.15.12.zip" -cmd = "./agentpool" +[agent_servers.wolfharness.targets.darwin-x86_64] +archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-darwin-x86_64-1.15.12.zip" +cmd = "./wolfharness" args = ["serve-acp"] -# [agent_servers.agentpool.targets.linux-aarch64] -# archive = "https://github.com/phil65/agentpool/releases/download/v2.9.5/agentpool-linux-aarch64-1.15.12.zip" -# cmd = "./agentpool" +# [agent_servers.wolfharness.targets.linux-aarch64] +# archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-linux-aarch64-1.15.12.zip" +# cmd = "./wolfharness" # args = ["serve-acp"] -[agent_servers.agentpool.targets.linux-x86_64] -archive = "https://github.com/phil65/agentpool/releases/download/v2.9.5/agentpool-linux-x86_64-1.15.12.zip" -cmd = "./agentpool" +[agent_servers.wolfharness.targets.linux-x86_64] +archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-linux-x86_64-1.15.12.zip" +cmd = "./wolfharness" args = ["serve-acp"] -# [agent_servers.agentpool.targets.windows-aarch64] -# archive = "https://github.com/phil65/agentpool/releases/download/v2.9.5/agentpool-windows-aarch64-1.15.12.zip" -# cmd = "./agentpool.exe" +# [agent_servers.wolfharness.targets.windows-aarch64] +# archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-windows-aarch64-1.15.12.zip" +# cmd = "./wolfharness.exe" # args = ["serve-acp"] -[agent_servers.agentpool.targets.windows-x86_64] -archive = "https://github.com/phil65/agentpool/releases/download/v2.9.5/agentpool-windows-x86_64-1.15.12.zip" -cmd = "./agentpool.exe" +[agent_servers.wolfharness.targets.windows-x86_64] +archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-windows-x86_64-1.15.12.zip" +cmd = "./wolfharness.exe" args = ["serve-acp"] diff --git a/docs/adr/eventbus-replay.md b/docs/adr/eventbus-replay.md index e04502a83..b2e2fd53f 100644 --- a/docs/adr/eventbus-replay.md +++ b/docs/adr/eventbus-replay.md @@ -194,7 +194,7 @@ For 1000 active sessions: ~200 MB total (acceptable for most deployments). ## Configuration ```python -# agentpool_config/session_pool.py +# wolfharness_config/session_pool.py @dataclass class OpenCodeConfig: use_session_pool: bool = True diff --git a/docs/adr/lifecycle-analysis.md b/docs/adr/lifecycle-analysis.md index 6ed5e43a3..9fa480549 100644 --- a/docs/adr/lifecycle-analysis.md +++ b/docs/adr/lifecycle-analysis.md @@ -9,7 +9,7 @@ ## Table of Contents -1. [Current AgentPool Lifecycle](#1-current-agentpool-lifecycle) +1. [Current AgentPool Lifecycle](#1-current-wolfharness-lifecycle) 2. [ACP v1 vs v2 Protocol Differences](#2-acp-v1-vs-v2-protocol-differences) 3. [Cross-Framework Comparison](#3-cross-framework-comparison) 4. [Key Design Issues](#4-key-design-issues) @@ -255,7 +255,7 @@ Key bridging difficulties: | Framework | Language | Location | Focus | |---|---|---|---| -| **AgentPool** | Python | `/packages/agentpool/` | Unified agent orchestration, YAML config, multi-protocol | +| **AgentPool** | Python | `/packages/wolfharness/` | Unified agent orchestration, YAML config, multi-protocol | | **pydantic-ai** | Python | `/Users/yuchen.liu/src/pydantic-ai/` | PydanticAI agent framework with graph execution | | **opencode** | TypeScript (Effect-TS) | `/Users/yuchen.liu/src/opencode/` | Code agent with durable event sourcing | | **pi** | TypeScript | `/Users/yuchen.liu/src/pi/` | Minimal event-stream agent loop | @@ -473,21 +473,21 @@ Turn should have unified interface, NativeTurn and ACPTurn through same abstract ## Appendix: Key Files Examined ### AgentPool -- `src/agentpool/delegation/pool.py` — AgentPool entry/exit lifecycle -- `src/agentpool/orchestrator/run.py` — RunHandle idle/wake/turn loop -- `src/agentpool/orchestrator/turn.py` — Turn ABC + HookAwareTurn mixin -- `src/agentpool/orchestrator/session_pool.py` — SessionPool -- `src/agentpool/orchestrator/session_controller.py` — SessionController -- `src/agentpool/orchestrator/event_bus.py` — EventBus -- `src/agentpool/orchestrator/event_mapper.py` — EventMapper -- `src/agentpool/agents/base_agent.py` — BaseAgent -- `src/agentpool/agents/context.py` — AgentRunContext + AgentContext -- `src/agentpool/agents/native_agent/turn.py` — NativeTurn -- `src/agentpool/agents/acp_agent/turn.py` — ACPTurn -- `src/agentpool/agents/native_agent/agent.py` — Native Agent -- `src/agentpool/agents/prompt_injection.py` — PromptInjectionManager -- `src/agentpool_server/mixins.py` — ProtocolEventConsumerMixin -- `src/agentpool/mcp_server/manager.py` — MCPManager +- `src/wolfharness/delegation/pool.py` — AgentPool entry/exit lifecycle +- `src/wolfharness/orchestrator/run.py` — RunHandle idle/wake/turn loop +- `src/wolfharness/orchestrator/turn.py` — Turn ABC + HookAwareTurn mixin +- `src/wolfharness/orchestrator/session_pool.py` — SessionPool +- `src/wolfharness/orchestrator/session_controller.py` — SessionController +- `src/wolfharness/orchestrator/event_bus.py` — EventBus +- `src/wolfharness/orchestrator/event_mapper.py` — EventMapper +- `src/wolfharness/agents/base_agent.py` — BaseAgent +- `src/wolfharness/agents/context.py` — AgentRunContext + AgentContext +- `src/wolfharness/agents/native_agent/turn.py` — NativeTurn +- `src/wolfharness/agents/acp_agent/turn.py` — ACPTurn +- `src/wolfharness/agents/native_agent/agent.py` — Native Agent +- `src/wolfharness/agents/prompt_injection.py` — PromptInjectionManager +- `src/wolfharness_server/mixins.py` — ProtocolEventConsumerMixin +- `src/wolfharness/mcp_server/manager.py` — MCPManager ### ACP Reference - `agent-client-protocol-schema/src/v1/` — ACP v1 schema diff --git a/docs/adr/message-history-api.md b/docs/adr/message-history-api.md index 9d5b13314..4e19c771d 100644 --- a/docs/adr/message-history-api.md +++ b/docs/adr/message-history-api.md @@ -45,7 +45,7 @@ This document specifies **new message history API methods to add to SessionPool* └─────────────────────────────────────────┘ ``` -**Key point**: `SessionPool` lives in `src/agentpool/orchestrator/core.py` and does not have message history methods yet. It has `self.sessions` (SessionController) and `self.sessions.store` (SessionStore for session metadata). Message history operations must be added to SessionPool as new methods. The storage layer is accessed via `AgentPool.storage`, which is a **`StorageManager`** (not `StorageProvider`). `StorageManager` is a proxy that forwards to configured `StorageProvider` instances. +**Key point**: `SessionPool` lives in `src/wolfharness/orchestrator/core.py` and does not have message history methods yet. It has `self.sessions` (SessionController) and `self.sessions.store` (SessionStore for session metadata). Message history operations must be added to SessionPool as new methods. The storage layer is accessed via `AgentPool.storage`, which is a **`StorageManager`** (not `StorageProvider`). `StorageManager` is a proxy that forwards to configured `StorageProvider` instances. ### Type Conversion @@ -199,7 +199,7 @@ The existing `StorageProvider` has methods that map to the new API: 3. Add pagination forwarding to `StorageManager.get_session_messages()` ```python -# In agentpool_storage/base.py (StorageProvider) +# In wolfharness_storage/base.py (StorageProvider) async def truncate_messages( self, session_id: str, @@ -230,7 +230,7 @@ async def get_session_messages( limit: Maximum number of messages to return (new parameter for Migration B). """ -# In agentpool/storage/manager.py (StorageManager) +# In wolfharness/storage/manager.py (StorageManager) async def get_session_messages( self, session_id: str, diff --git a/docs/adr/message_node_adapter.md b/docs/adr/message_node_adapter.md index 4fbd00eeb..416fcb199 100644 --- a/docs/adr/message_node_adapter.md +++ b/docs/adr/message_node_adapter.md @@ -255,7 +255,7 @@ class MessageNode[TDeps, TResult](ABC): from dataclasses import dataclass, field from typing import Any -from agentpool.messaging import ChatMessage +from wolfharness.messaging import ChatMessage @dataclass @@ -718,8 +718,8 @@ from pydantic_graph.graph_builder import Graph, GraphBuilder from pydantic_graph.step import Step, StepContext from typing_extensions import TypeVar -from agentpool.agents.events import RichAgentStreamEvent -from agentpool.messaging import ChatMessage, MessageNode +from wolfharness.agents.events import RichAgentStreamEvent +from wolfharness.messaging import ChatMessage, MessageNode StateT = TypeVar("StateT") DepsT = TypeVar("DepsT") diff --git a/docs/adr/permission-question-api.md b/docs/adr/permission-question-api.md index 2591df89f..1e90e4e16 100644 --- a/docs/adr/permission-question-api.md +++ b/docs/adr/permission-question-api.md @@ -27,7 +27,7 @@ In Migration B: ### Core Types ```python -# agentpool/models/pending_interaction.py +# wolfharness/models/pending_interaction.py from typing import Protocol from datetime import datetime @@ -47,7 +47,7 @@ class PendingPermission(Protocol): content: str created_at: datetime -# agentpool_server/opencode_server/models/question_permission.py +# wolfharness_server/opencode_server/models/question_permission.py from dataclasses import dataclass, field from datetime import datetime diff --git a/docs/adr/signal_streaming_adapters.md b/docs/adr/signal_streaming_adapters.md index 316821e7b..11f0c7bee 100644 --- a/docs/adr/signal_streaming_adapters.md +++ b/docs/adr/signal_streaming_adapters.md @@ -25,7 +25,7 @@ The migration to `pydantic-graph` replaces the ad-hoc node/graph execution with ### 2.1 Current Signal Definitions -In `src/agentpool/messaging/messagenode.py`: +In `src/wolfharness/messaging/messagenode.py`: ```python class MessageNode[TDeps, TResult](ABC): @@ -36,7 +36,7 @@ class MessageNode[TDeps, TResult](ABC): """Signal emitted when node creates a message.""" ``` -In `src/agentpool/talk/talk.py`: +In `src/wolfharness/talk/talk.py`: ```python class Talk[TTransmittedData = Any]: @@ -60,7 +60,7 @@ Inside `_GraphIterator.iter_graph()`, the lifecycle is: ### 2.3 Existing Event Types -From `src/agentpool/agents/events/events.py`: +From `src/wolfharness/agents/events/events.py`: ```python type RichAgentStreamEvent[OutputDataT] = ( @@ -522,7 +522,7 @@ PartDeltaEvent (LLM resumes streaming) ### 8.1 Adapter Layer Location The adapter should live in a new module, e.g.: -- `src/agentpool/delegation/graph_adapter.py` +- `src/wolfharness/delegation/graph_adapter.py` It wraps `GraphRun` without subclassing it, to avoid coupling to pydantic-graph internals. diff --git a/docs/explanation/2026-07-01-mcp-provider-lifecycle-architecture-design.md b/docs/explanation/2026-07-01-mcp-provider-lifecycle-architecture-design.md index c468954bc..623592825 100644 --- a/docs/explanation/2026-07-01-mcp-provider-lifecycle-architecture-design.md +++ b/docs/explanation/2026-07-01-mcp-provider-lifecycle-architecture-design.md @@ -4,7 +4,7 @@ **Status**: Draft **Author**: Sisyphus (via brainstorming session) **Branch**: `refactor/migrate-to-mcptoolset` -**Worktree**: `/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool-fix-mcp` +**Worktree**: `/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness-fix-mcp` ## Problem Statement @@ -108,7 +108,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Literal if TYPE_CHECKING: - from agentpool_config.mcp_server import BaseMCPServerConfig + from wolfharness_config.mcp_server import BaseMCPServerConfig @dataclass(frozen=True) class McpConfigEntry: diff --git a/docs/explanation/2026-07-10-agent-pool-backdoor-cleanup-design.md b/docs/explanation/2026-07-10-agent-pool-backdoor-cleanup-design.md index c31523b3f..e47b329f3 100644 --- a/docs/explanation/2026-07-10-agent-pool-backdoor-cleanup-design.md +++ b/docs/explanation/2026-07-10-agent-pool-backdoor-cleanup-design.md @@ -57,8 +57,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable if TYPE_CHECKING: - from agentpool_toolsets.builtin.combined_toolset import CombinedToolsetCapability - from agentpool.skills.command_registry import SkillCommandRegistry + from wolfharness_toolsets.builtin.combined_toolset import CombinedToolsetCapability + from wolfharness.skills.command_registry import SkillCommandRegistry @runtime_checkable class SkillService(Protocol): @@ -253,7 +253,7 @@ Phase 2 and Phase 3 can run in **parallel** — both depend only on Phase 1. |---|---|---| | `agents/native_agent/agent.py` | 3 | `skill_capabilities` → `skill_service.capabilities`; `is_skill_visible_to_node` → `skill_service.is_visible_to_node` | | `delegation/base_team.py` | 3 | `skill_provider` → `skill_service.provider`; `get_skill_instructions_for_node` → `skill_service.get_instructions_for_node` | -| `agentpool_commands/utils.py` | 2 | `manifest.config_file_path` → `ctx.config_file_path` | +| `wolfharness_commands/utils.py` | 2 | `manifest.config_file_path` → `ctx.config_file_path` | | `shared/model_utils.py` | 1 | `agent.agent_pool` → `agent.host_context` | **Verification**: Core agent code no longer triggers `DeprecationWarning`. Unit tests pass. diff --git a/docs/explanation/architectural-patterns.md b/docs/explanation/architectural-patterns.md index 7adcdffe5..9d1820595 100644 --- a/docs/explanation/architectural-patterns.md +++ b/docs/explanation/architectural-patterns.md @@ -2,7 +2,7 @@ ## ProtocolEventConsumerMixin -`ProtocolEventConsumerMixin` (in `src/agentpool_server/mixins.py`) provides a reusable event consumer lifecycle for protocol servers. It extracts the common pattern of subscribing to the `EventBus`, running an async consumer loop, and cleaning up on shutdown. +`ProtocolEventConsumerMixin` (in `src/wolfharness_server/mixins.py`) provides a reusable event consumer lifecycle for protocol servers. It extracts the common pattern of subscribing to the `EventBus`, running an async consumer loop, and cleaning up on shutdown. **Why it exists**: Before this mixin, OpenCode and ACP each implemented their own event consumer loop independently. The code was duplicated, and ACP's implementation was missing features like `SpawnSessionStart` handling and recursive child subscription. The mixin centralizes the loop mechanics while letting each protocol define its own event conversion. diff --git a/docs/explanation/capabilities.md b/docs/explanation/capabilities.md index bce2b2bc9..876b33d25 100644 --- a/docs/explanation/capabilities.md +++ b/docs/explanation/capabilities.md @@ -1,6 +1,6 @@ # Capabilities (M3 — Replaces Resource Providers) -In M3, the old `ResourceProvider` hierarchy was replaced with native pydantic-ai `AbstractCapability` / `AbstractToolset` implementations. Each `AbstractCapability` produces tools, instructions, change notifications, and optionally implements `ResourceSource` for read-only data access. The old `src/agentpool/resource_providers/` directory (14 files, ~3860 LOC) was physically deleted after migration. +In M3, the old `ResourceProvider` hierarchy was replaced with native pydantic-ai `AbstractCapability` / `AbstractToolset` implementations. Each `AbstractCapability` produces tools, instructions, change notifications, and optionally implements `ResourceSource` for read-only data access. The old `src/wolfharness/resource_providers/` directory (14 files, ~3860 LOC) was physically deleted after migration. ## Capability Registry @@ -21,7 +21,7 @@ In M3, the old `ResourceProvider` hierarchy was replaced with native pydantic-ai - `AgentContext` (`capabilities/agent_context.py`) — Frozen dataclass carrying `agent_registry`, `delegation`, `session`, `scope`, `resources`, `host`. Constructed by RunLoop per-turn. - `DelegationService` (`capabilities/delegation.py`) — Protocol exposing `spawn_subagent(name, prompt)` and `get_available_agents()`. Limits tools to operations they need without exposing `AgentPool`. - `ChangeEvent` (`capabilities/change_event.py`) — Frozen dataclass for capability change notifications (`on_change()` stream). -- Entry-point registry (`capabilities/registry.py`) — Discovers custom capabilities via `agentpool.capabilities` entry-point group. +- Entry-point registry (`capabilities/registry.py`) — Discovers custom capabilities via `wolfharness.capabilities` entry-point group. - `ExtensionRegistry` (`capabilities/extension_registry.py`) — Unified capability registry with 4-level scope storage. See [ExtensionRegistry and Scope Hierarchy](#extensionregistry-and-scope-hierarchy) below. ## ExtensionRegistry and Scope Hierarchy @@ -76,5 +76,5 @@ The `ScopeLevel` enum has been reordered from `POOL > SESSION > AGENT > TURN` to ## Deleted Alongside ResourceProviders -- `src/agentpool/tools/factory.py` (194 LOC, 6 `ToolsetFactory` classes) — became dead code after all providers migrated. -- `src/agentpool/tools/manager.py` (364 LOC, `ToolManager`) — all `agent.tools.X` access migrated to direct capability references. +- `src/wolfharness/tools/factory.py` (194 LOC, 6 `ToolsetFactory` classes) — became dead code after all providers migrated. +- `src/wolfharness/tools/manager.py` (364 LOC, `ToolManager`) — all `agent.tools.X` access migrated to direct capability references. diff --git a/docs/explanation/common-patterns.md b/docs/explanation/common-patterns.md index b855649ac..1e4d48724 100644 --- a/docs/explanation/common-patterns.md +++ b/docs/explanation/common-patterns.md @@ -32,4 +32,4 @@ pytest && mypy src/ && ruff check src/ - Examples in `site/examples/*/config.yml` - Schema reference auto-generated from Pydantic models -- Validate with: `python -m agentpool_config.manifest config.yml` +- Validate with: `python -m wolfharness_config.manifest config.yml` diff --git a/docs/explanation/extending-agentpool.md b/docs/explanation/extending-agentpool.md index 5e4c54d1d..bd023ed46 100644 --- a/docs/explanation/extending-agentpool.md +++ b/docs/explanation/extending-agentpool.md @@ -3,23 +3,23 @@ ## Tool Implementation When adding new tools: -1. Create implementation in `agentpool/tool_impls//` -2. Define config model in `agentpool_config/` if complex -3. Register in appropriate toolset (`agentpool_toolsets/`) +1. Create implementation in `wolfharness/tool_impls//` +2. Define config model in `wolfharness_config/` if complex +3. Register in appropriate toolset (`wolfharness_toolsets/`) 4. Add tests in `tests/tool_impls/` ## Adding Agent Types New agent types require: -1. Config model in `agentpool/models/` (inherit from base, set `type` discriminator) -2. Implementation in `agentpool/agents/` +1. Config model in `wolfharness/models/` (inherit from base, set `type` discriminator) +2. Implementation in `wolfharness/agents/` 3. Add to `AnyAgentConfig` union in `manifest.py` 4. Update manifest loading in `pool.py` ## Server Implementation New protocol servers: -1. Inherit from `BaseServer` in `agentpool_server/base.py` +1. Inherit from `BaseServer` in `wolfharness_server/base.py` 2. Implement protocol-specific handlers 3. Use `AggregatingServer` if wrapping multiple agents -4. Add CLI command in `agentpool_cli/` +4. Add CLI command in `wolfharness_cli/` diff --git a/docs/explanation/hooks-events.md b/docs/explanation/hooks-events.md index c976859d9..4aaf87d86 100644 --- a/docs/explanation/hooks-events.md +++ b/docs/explanation/hooks-events.md @@ -2,11 +2,11 @@ ## Hooks -Hooks (`src/agentpool/hooks/`) intercept agent turns at 4 points: `pre_turn`, `post_turn`, `pre_tool_use`, `post_tool_use`. Three hook types: `CallableHook` (in-process), `CommandHook` (subprocess), `PromptHook` (LLM evaluation). Hooks run in parallel, results combined with priority: deny > ask > allow. +Hooks (`src/wolfharness/hooks/`) intercept agent turns at 4 points: `pre_turn`, `post_turn`, `pre_tool_use`, `post_tool_use`. Three hook types: `CallableHook` (in-process), `CommandHook` (subprocess), `PromptHook` (LLM evaluation). Hooks run in parallel, results combined with priority: deny > ask > allow. ### HookAwareTurn Architecture -Hooks fire through the `HookAwareTurn` mixin (`src/agentpool/orchestrator/turn.py`), which is inherited by both `NativeTurn` and `ACPTurn`. This provides a single choke point for hook execution inside `Turn.execute()`. +Hooks fire through the `HookAwareTurn` mixin (`src/wolfharness/orchestrator/turn.py`), which is inherited by both `NativeTurn` and `ACPTurn`. This provides a single choke point for hook execution inside `Turn.execute()`. **Firing flow:** @@ -83,8 +83,8 @@ capability = agent_hooks.as_capability(hook_manager) ## Events -**Event Types** (`src/agentpool/agents/events/events.py`): `RichAgentStreamEvent` union type covers streaming deltas, tool calls (start/progress/complete), run lifecycle (started/error/failed), subagent events, session resume, compaction, plan updates, and custom events. +**Event Types** (`src/wolfharness/agents/events/events.py`): `RichAgentStreamEvent` union type covers streaming deltas, tool calls (start/progress/complete), run lifecycle (started/error/failed), subagent events, session resume, compaction, plan updates, and custom events. -**EventBus** (`src/agentpool/orchestrator/core.py`): Cross-turn event streaming for protocol servers. Bounded async queues per session, replay buffers, scoped subscriptions (`"session"`, `"descendants"`, `"subtree"`, `"all"`). +**EventBus** (`src/wolfharness/orchestrator/core.py`): Cross-turn event streaming for protocol servers. Bounded async queues per session, replay buffers, scoped subscriptions (`"session"`, `"descendants"`, `"subtree"`, `"all"`). **Signal Architecture** (`anyenv.signals.Signal`): In-process type-safe pub/sub on `MessageNode` (`message_received`, `message_sent`) and `Talk` (`connection_processed`, `message_forwarded`). `SignalEmittingGraphRun` bridges pydantic-graph steps to signals. diff --git a/docs/explanation/import-patterns.md b/docs/explanation/import-patterns.md index 2091bd890..95f755e58 100644 --- a/docs/explanation/import-patterns.md +++ b/docs/explanation/import-patterns.md @@ -6,8 +6,8 @@ from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: - from agentpool.delegation import AgentPool + from wolfharness.delegation import AgentPool -# Config models are in agentpool_config to avoid circular deps -from agentpool_config.teams import TeamConfig +# Config models are in wolfharness_config to avoid circular deps +from wolfharness_config.teams import TeamConfig ``` diff --git a/docs/explanation/lifecycle-dimensions.md b/docs/explanation/lifecycle-dimensions.md index e541d0808..36f7dd0d3 100644 --- a/docs/explanation/lifecycle-dimensions.md +++ b/docs/explanation/lifecycle-dimensions.md @@ -72,7 +72,7 @@ When the `lifecycle:` section is omitted or all fields are at default, `create_d ## Factory Function ```python -from agentpool.lifecycle.factory import create_dimensions +from wolfharness.lifecycle.factory import create_dimensions trigger, journal, snapshot, comm, transport = create_dimensions( lifecycle_config, session_id="my_session", @@ -92,7 +92,7 @@ run_handle = RunHandle( ## Lifecycle Package Structure ``` -src/agentpool/lifecycle/ +src/wolfharness/lifecycle/ __init__.py — Public exports for all types and implementations types.py — RunState, Prompt, Feedback, ResumeResult, ToolExecutionRecord, EventEnvelope (plain dataclasses; diff --git a/docs/explanation/mcp-architecture.md b/docs/explanation/mcp-architecture.md index 151c17503..8d67835a1 100644 --- a/docs/explanation/mcp-architecture.md +++ b/docs/explanation/mcp-architecture.md @@ -2,18 +2,18 @@ ## Core Components -### MCPManager (`agentpool.mcp_server.manager`) +### MCPManager (`wolfharness.mcp_server.manager`) Manages MCP server **lifecycle** only. Spawns MCP server processes, wraps each in an `MCPResourceProvider`, and aggregates them into an `AggregatingResourceProvider`. Does NOT wire providers into ToolManager — that happens externally. Lives on `MessageNode` (every agent has one) and on `AgentPool` / `BaseTeam`. -### ToolManager (`agentpool.tools.manager`) +### ToolManager (`wolfharness.tools.manager`) Manages local tools + external resource providers. MCP tools become available when someone adds the MCPManager's aggregating provider to the ToolManager. -### ToolManagerBridge (`agentpool.mcp_server.tool_bridge`) +### ToolManagerBridge (`wolfharness.mcp_server.tool_bridge`) The inverse of MCPManager: exposes an agent's internal ToolManager as an HTTP MCP server. Used by wrapped agents (Claude Code, Codex, ACP) so the external process can call back into our tools. diff --git a/docs/explanation/module-structure.md b/docs/explanation/module-structure.md index db75f4f17..a18c12205 100644 --- a/docs/explanation/module-structure.md +++ b/docs/explanation/module-structure.md @@ -2,7 +2,7 @@ The codebase is organized into focused packages under `src/`: -## agentpool/ — Core Agent Framework +## wolfharness/ — Core Agent Framework - `agents/` - Agent implementations (native, ACP) - `capabilities/` - Native pydantic-ai capability implementations (MCPCapability, FunctionToolsetCapability, CombinedToolsetCapability, SubagentCapability, CodeModeCapability, FilteredToolsetCapability, AgentContext, DelegationService, ResourceSource, entry-point registry) @@ -20,11 +20,11 @@ The codebase is organized into focused packages under `src/`: - `hooks/` - Event hooks system - `observability/` - Logging and telemetry (Logfire) -## agentpool_config/ — Configuration Models +## wolfharness_config/ — Configuration Models Separated for clean imports. Contains YAML schema definitions for agents, teams, tools, MCP servers. -## agentpool_server/ — Protocol Servers +## wolfharness_server/ — Protocol Servers - `acp_server/` - Agent Communication Protocol server - `opencode_server/` - OpenCode TUI/Desktop server @@ -32,13 +32,13 @@ Separated for clean imports. Contains YAML schema definitions for agents, teams, - `openai_api_server/` - OpenAI-compatible API server - `mcp_server/` - Model Context Protocol server -## agentpool_toolsets/ — Reusable Toolset Implementations +## wolfharness_toolsets/ — Reusable Toolset Implementations - `builtin/` - Built-in toolsets (code, debug, subagent, file_edit, workers) - `mcp_discovery/` - MCP server discovery with semantic search - Specialized toolsets (composio, search, streaming, etc.) -## agentpool_storage/ — Storage Providers +## wolfharness_storage/ — Storage Providers - `sql_provider/` - SQLAlchemy-based storage - `zed_provider/` - Zed IDE storage integration @@ -47,9 +47,9 @@ Separated for clean imports. Contains YAML schema definitions for agents, teams, ## Other Packages -- `agentpool_cli/` - Command-line interface -- `agentpool_commands/` - Command implementations -- `agentpool_prompts/` - Prompt templates +- `wolfharness_cli/` - Command-line interface +- `wolfharness_commands/` - Command implementations +- `wolfharness_prompts/` - Prompt templates - `acp/` - Agent Communication Protocol implementation - `client/` - ACP client implementations - `agent/` - Agent-side protocol implementation diff --git a/docs/explanation/session-orchestration.md b/docs/explanation/session-orchestration.md index 276b0ce53..af4e6bfd8 100644 --- a/docs/explanation/session-orchestration.md +++ b/docs/explanation/session-orchestration.md @@ -97,7 +97,7 @@ During recovery, events since the last snapshot are replayed through the CommCha ## Session State Machine Mapping -`SessionData.status` (persisted, survives crashes) and `RunState` (transient, in-memory) are separate state machines with a formal mapping defined in `SessionStateMapper` (`src/agentpool/sessions/state_mapper.py`). +`SessionData.status` (persisted, survives crashes) and `RunState` (transient, in-memory) are separate state machines with a formal mapping defined in `SessionStateMapper` (`src/wolfharness/sessions/state_mapper.py`). | Scenario | SessionData.status | RunState | Valid? | |---|---|---|---| diff --git a/docs/explanation/skills-system.md b/docs/explanation/skills-system.md index 6c936662e..1a45a3f1d 100644 --- a/docs/explanation/skills-system.md +++ b/docs/explanation/skills-system.md @@ -11,12 +11,12 @@ Skills are defined as `SKILL.md` files following the [Agent Skills Spec](https:/ ## Key Files -- `src/agentpool/skills/skill.py` — `Skill` model: YAML frontmatter parsing, lazy instruction loading -- `src/agentpool/skills/registry.py` — `SkillsRegistry` auto-discovers SKILL.md files from configured paths -- `src/agentpool/skills/manager.py` — `SkillsManager` pool-level lifecycle -- `src/agentpool/skills/uri_resolver.py` — `skill://` URI scheme resolver -- `src/agentpool/skills/command.py` — `SkillCommand` wraps skills as protocol-agnostic slash commands -- `src/agentpool/skills/instruction_provider.py` — `SkillsInstructionProvider` injects skills as XML into prompts (metadata/full modes) — migrated from `resource_providers/` +- `src/wolfharness/skills/skill.py` — `Skill` model: YAML frontmatter parsing, lazy instruction loading +- `src/wolfharness/skills/registry.py` — `SkillsRegistry` auto-discovers SKILL.md files from configured paths +- `src/wolfharness/skills/manager.py` — `SkillsManager` pool-level lifecycle +- `src/wolfharness/skills/uri_resolver.py` — `skill://` URI scheme resolver +- `src/wolfharness/skills/command.py` — `SkillCommand` wraps skills as protocol-agnostic slash commands +- `src/wolfharness/skills/instruction_provider.py` — `SkillsInstructionProvider` injects skills as XML into prompts (metadata/full modes) — migrated from `resource_providers/` ## Injection Modes diff --git a/docs/explanation/special-considerations.md b/docs/explanation/special-considerations.md index e75972433..adff3efbc 100644 --- a/docs/explanation/special-considerations.md +++ b/docs/explanation/special-considerations.md @@ -26,6 +26,6 @@ Fallback models: `type: fallback, models: [primary, backup]` ## Entry Points The project uses entry points for extensibility: -- `agentpool_toolsets` - Register custom toolsets +- `wolfharness_toolsets` - Register custom toolsets - `fsspec.specs` - Filesystem implementations (ACP) - `universal_pathlib.implementations` - Path implementations diff --git a/docs/explanation/storage-observability.md b/docs/explanation/storage-observability.md index e6e9eeb5f..0c2ea8bcf 100644 --- a/docs/explanation/storage-observability.md +++ b/docs/explanation/storage-observability.md @@ -5,7 +5,7 @@ Track all agent interactions: - SQL-based with SQLModel/SQLAlchemy - Per-agent or shared database -- Analytics via CLI: `agentpool history stats` +- Analytics via CLI: `wolfharness history stats` ## Observability diff --git a/docs/explanation/tool-display-capability.md b/docs/explanation/tool-display-capability.md index c0f04df4c..67ff9b76d 100644 --- a/docs/explanation/tool-display-capability.md +++ b/docs/explanation/tool-display-capability.md @@ -1,8 +1,8 @@ # ToolDisplayCapability -`ToolDisplayCapability` 是 agentpool 的**全局装饰器能力** (Global Decorator Capability):在不修改任何子能力源码的前提下,对 agent 已装配的全部工具统一起作用 —— **改名** (`rename_mode`) 与 **注入 diff 富信息事件** (`emit_diff`)。它解决的核心问题是:第三方 capability(如 viking)的工具名不在协议客户端(OpenCode TUI / Zed)的渲染白名单内、工具返回不含 diff 富信息,导致客户端无法渲染文件变更的 diff 视图。 +`ToolDisplayCapability` 是 wolfharness 的**全局装饰器能力** (Global Decorator Capability):在不修改任何子能力源码的前提下,对 agent 已装配的全部工具统一起作用 —— **改名** (`rename_mode`) 与 **注入 diff 富信息事件** (`emit_diff`)。它解决的核心问题是:第三方 capability(如 viking)的工具名不在协议客户端(OpenCode TUI / Zed)的渲染白名单内、工具返回不含 diff 富信息,导致客户端无法渲染文件变更的 diff 视图。 -模式对齐 `ToolInterceptCapability`(`src/agentpool/agents/native_agent/tool_intercept.py`):独立 `AbstractCapability` 直接覆写 `get_wrapper_toolset()` 与 `wrap_tool_execute()`,作为全局中间件横切 agent 的全部工具 —— 不组合子能力、无 `capabilities` 字段。 +模式对齐 `ToolInterceptCapability`(`src/wolfharness/agents/native_agent/tool_intercept.py`):独立 `AbstractCapability` 直接覆写 `get_wrapper_toolset()` 与 `wrap_tool_execute()`,作为全局中间件横切 agent 的全部工具 —— 不组合子能力、无 `capabilities` 字段。 ## 三个正交开关 @@ -35,8 +35,8 @@ wrap_tool_execute → ctx.deps.events.tool_call_progress(title, items=[DiffConte → 客户端(Zed/OpenCode TUI)渲染 diff ``` -- `ctx.deps` 在 agentpool 中**直接是 `AgentContext`**,携带 `.events` → `StreamEventEmitter`(POC 已验证,同 fsspec 工具集 `agentpool_toolsets/fsspec_toolset/toolset.py:575` 的先例通道) -- `DiffContentItem(path, old_text, new_text)` 定义于 `src/agentpool/agents/events/events.py:203` +- `ctx.deps` 在 wolfharness 中**直接是 `AgentContext`**,携带 `.events` → `StreamEventEmitter`(POC 已验证,同 fsspec 工具集 `wolfharness_toolsets/fsspec_toolset/toolset.py:575` 的先例通道) +- `DiffContentItem(path, old_text, new_text)` 定义于 `src/wolfharness/agents/events/events.py:203` - **不依赖 metadata 通道**:`ToolReturn.metadata` 在 `process_tool_event`/`event_mapper` 构造 `ToolCallCompleteEvent` 时会被丢弃(仅 `is_error`),本能力刻意绕开该断点,改用事件注入 ## 协议区分配置 @@ -66,7 +66,7 @@ capabilities: emit_diff_for: [viking_write, viking_edit] ``` -- 注册:entry-point 组 `agentpool.capabilities`,key `tool_display` → `agentpool.capabilities.tool_display_capability:ToolDisplayCapability`(见 `pyproject.toml`),由 `registry.py` 发现 +- 注册:entry-point 组 `wolfharness.capabilities`,key `tool_display` → `wolfharness.capabilities.tool_display_capability:ToolDisplayCapability`(见 `pyproject.toml`),由 `registry.py` 发现 - 构造:`EntryPointCapabilityConfig(type=..., args={...}).build()` 以 `cls(**args)` 实例化 —— dataclass 字段(`rename_mode`/`name_map`/`emit_diff`/`emit_diff_for`)+ `id` 天然兼容 YAML 装配 ## 已知约束 diff --git a/docs/explanation/usage-examples.md b/docs/explanation/usage-examples.md index de9a4fd76..300f299b1 100644 --- a/docs/explanation/usage-examples.md +++ b/docs/explanation/usage-examples.md @@ -5,7 +5,7 @@ ### Native Agent ```python -from agentpool.agents import Agent +from wolfharness.agents import Agent def greet(name: str) -> str: """Greet someone.""" @@ -24,7 +24,7 @@ async with Agent( ### ACP Agent ```python -from agentpool.agents.acp_agent import ACPAgent +from wolfharness.agents.acp_agent import ACPAgent async with ACPAgent( command="goose", # Required: executable name @@ -56,8 +56,8 @@ agents: ### Python Code ```python -from agentpool.delegation import AgentPool -from agentpool.agents.events import ( +from wolfharness.delegation import AgentPool +from wolfharness.agents.events import ( PartDeltaEvent, ToolCallStartEvent, ToolCallCompleteEvent, diff --git a/docs/explanation/user_interaction.md b/docs/explanation/user_interaction.md index 190932332..ad854361c 100644 --- a/docs/explanation/user_interaction.md +++ b/docs/explanation/user_interaction.md @@ -34,27 +34,27 @@ AgentPool uses **InputProvider** to handle user interactions across different ex ## Providers ### StdlibInputProvider -**Location**: `agentpool/ui/stdlib_provider.py` +**Location**: `wolfharness/ui/stdlib_provider.py` **Usage**: CLI, fallback **Mechanism**: Blocking `input()` calls **Limitations**: No async, no rich UI, no multi-select ### ACPInputProvider -**Location**: `agentpool_server/acp_server/input_provider.py` +**Location**: `wolfharness_server/acp_server/input_provider.py` **Usage**: ACP clients (Goose, Codex) **Mechanism**: Maps elicitation → `request_permission()` **[HACK]** **Why hacky**: ACP lacks native elicitation, shoehorns questions into permission system **Limitations**: Max 4 options, no multi-select, wrong semantics ### OpenCodeInputProvider -**Location**: `agentpool_server/opencode_server/input_provider.py` +**Location**: `wolfharness_server/opencode_server/input_provider.py` **Usage**: OpenCode TUI/Desktop **Mechanism**: SSE events + HTTP response endpoints **Flow**: Create question → broadcast event → await HTTP reply → resolve future **Advantages**: Native questions, multi-select, unlimited options, rich descriptions ### MockInputProvider -**Location**: `agentpool/ui/mock_provider.py` +**Location**: `wolfharness/ui/mock_provider.py` **Usage**: Tests **Mechanism**: Pre-programmed responses diff --git a/docs/how-to/advanced/acp-integration.md b/docs/how-to/advanced/acp-integration.md index 6bb36afe5..82d0cf981 100644 --- a/docs/how-to/advanced/acp-integration.md +++ b/docs/how-to/advanced/acp-integration.md @@ -33,7 +33,7 @@ The key difference is that ACP agents run as **separate processes** communicatin Using uvx for one-off usage: ```bash -uvx --python 3.13 agentpool@latest serve-acp --help +uvx --python 3.13 wolfharness@latest serve-acp --help ``` ## CLI Usage @@ -43,7 +43,7 @@ uvx --python 3.13 agentpool@latest serve-acp --help Start an ACP server from a configuration file: ```bash -agentpool serve-acp agents.yml +wolfharness serve-acp agents.yml ``` ### Available Options @@ -65,9 +65,9 @@ Add this configuration to your Zed `settings.json`: "args": [ "--python", "3.13", - "agentpool[coding]@latest", + "wolfharness[coding]@latest", "serve-acp", - "https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/docs/examples/create_docs/config.yml" + "https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/docs/examples/create_docs/config.yml" ], "env": { "OPENAI_API_KEY": "your-api-key-here" @@ -88,7 +88,7 @@ This configuration: For IDEs that support ACP, the general pattern is: -1. Set the command to `agentpool` (or `uvx agentpool@latest`) +1. Set the command to `wolfharness` (or `uvx wolfharness@latest`) 2. Add `serve-acp` as the first argument 3. Specify your configuration file path 4. Add any desired CLI options @@ -102,11 +102,11 @@ Understanding AgentPool's ACP integration requires grasping a key architectural #### As an ACP Server (IDE Integration) -When you run `agentpool serve-acp`, it becomes an **ACP server** that IDEs can connect to: +When you run `wolfharness serve-acp`, it becomes an **ACP server** that IDEs can connect to: ```mermaid graph LR - A["IDE (Zed)
ACP Client"] <-->|ACP Protocol| B["agentpool
ACP Server
(serve-acp command)"] + A["IDE (Zed)
ACP Client"] <-->|ACP Protocol| B["wolfharness
ACP Server
(serve-acp command)"] ``` In this mode, AgentPool: @@ -123,7 +123,7 @@ At the same time, AgentPool can act as an **ACP client** to integrate external A ```mermaid graph LR - A["agentpool
ACP Client
(external agent)"] <-->|ACP Protocol| B["Claude Code
ACP Server
(subprocess)"] + A["wolfharness
ACP Client
(external agent)"] <-->|ACP Protocol| B["Claude Code
ACP Server
(subprocess)"] ``` In this mode, AgentPool: @@ -141,7 +141,7 @@ The real power comes when **both roles work together**: ```mermaid graph TB IDE["IDE (Zed)
ACP Client"] - LLM["agentpool
Server ↔ Pool ↔ Client"] + LLM["wolfharness
Server ↔ Pool ↔ Client"] Claude["Claude Code
ACP Server"] Pool["Agent Pool
• Internal Agents
• Teams
• Resources
• Tools"] @@ -282,7 +282,7 @@ These agents use ACP protocol but don't support MCP toolset bridging: Once configured, external ACP agents are automatically available in the agent pool: ```python -from agentpool.delegation import AgentPool +from wolfharness.delegation import AgentPool async def main(): async with AgentPool("agents.yml") as pool: @@ -472,7 +472,7 @@ Permissions are enforced at multiple levels: ### Overview -For MCP-capable ACP agents (Claude, Gemini, Auggie, Kimi), agentpool can automatically expose internal toolsets via an in-process MCP server bridge. This allows external agents to use powerful internal capabilities like subagent delegation, agent management, and custom tools. +For MCP-capable ACP agents (Claude, Gemini, Auggie, Kimi), wolfharness can automatically expose internal toolsets via an in-process MCP server bridge. This allows external agents to use powerful internal capabilities like subagent delegation, agent management, and custom tools. ### How It Works @@ -644,7 +644,7 @@ Best practices: You can reference remote configuration files directly: ```bash -agentpool serve-acp https://example.com/config.yml +wolfharness serve-acp https://example.com/config.yml ``` ### Model Provider Selection diff --git a/docs/how-to/advanced/agui-integration.md b/docs/how-to/advanced/agui-integration.md index f3b9fb9ea..d96c529e6 100644 --- a/docs/how-to/advanced/agui-integration.md +++ b/docs/how-to/advanced/agui-integration.md @@ -19,25 +19,25 @@ Unlike ACP (which uses stdio/JSON-RPC for IDE integration), AG-UI is designed fo ## AG-UI Agents: The Client Side -In agentpool, `AGUIAgent` acts as a **client** that connects **to** remote AG-UI servers. This allows you to: +In wolfharness, `AGUIAgent` acts as a **client** that connects **to** remote AG-UI servers. This allows you to: - Integrate external AG-UI agents into your agent pool -- Compose remote agents with native agentpools via connections and teams +- Compose remote agents with native wolfharnesss via connections and teams - Execute tools locally while letting remote agents decide when to use them - Maintain unified messaging and event systems !!! note "Client vs Server" - Currently, agentpool provides the **client side** (consuming AG-UI agents). - Server-side support (exposing agentpools as AG-UI endpoints) is planned for future releases. + Currently, wolfharness provides the **client side** (consuming AG-UI agents). + Server-side support (exposing wolfharnesss as AG-UI endpoints) is planned for future releases. ## Installation -No additional installation required - AG-UI support is included in agentpool: +No additional installation required - AG-UI support is included in wolfharness: ```bash -pip install agentpool +pip install wolfharness # or -uv add agentpool +uv add wolfharness ``` The `ag-ui` Python package is a dependency and provides the protocol types. @@ -47,7 +47,7 @@ The `ag-ui` Python package is a dependency and provides the protocol types. ### Connecting to an AG-UI Agent ```python -from agentpool.agents import AGUIAgent +from wolfharness.agents import AGUIAgent async def main(): # Connect to a remote AG-UI agent @@ -90,7 +90,7 @@ agents: Load and use: ```python -from agentpool import AgentPool +from wolfharness import AgentPool async def main(): async with AgentPool("agents.yml") as pool: @@ -272,7 +272,7 @@ agents: AG-UI agents emit the same events as native agents: ```python -from agentpool.agents.events import ToolCallStartEvent +from wolfharness.agents.events import ToolCallStartEvent async def tool_call_handler(ctx, event): if isinstance(event, ToolCallStartEvent): @@ -322,7 +322,7 @@ async with AGUIAgent( ## Future: AG-UI Server Support -Future versions will support exposing agentpools **as** AG-UI servers: +Future versions will support exposing wolfharnesss **as** AG-UI servers: ```yaml title="Future feature" agui_server: @@ -335,7 +335,7 @@ agui_server: This will enable: -- Frontends directly connecting to agentpools +- Frontends directly connecting to wolfharnesss - Web UI integration - Bidirectional AG-UI workflows diff --git a/docs/how-to/advanced/agui_example.yml b/docs/how-to/advanced/agui_example.yml index 7db8dc7ac..646f6adf7 100644 --- a/docs/how-to/advanced/agui_example.yml +++ b/docs/how-to/advanced/agui_example.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json # AG-UI Integration Example Configuration # This file demonstrates various AG-UI agent configurations diff --git a/docs/how-to/advanced/async.md b/docs/how-to/advanced/async.md index 75a832497..a67183ea9 100644 --- a/docs/how-to/advanced/async.md +++ b/docs/how-to/advanced/async.md @@ -110,7 +110,7 @@ await agent.stop() AgentPool supports event-driven agent operation through file watchers and other triggers. The simplest way to start event-based mode is through the CLI: ```bash -agentpool watch --config agents.yml +wolfharness watch --config agents.yml ``` This command: diff --git a/docs/how-to/advanced/db.md b/docs/how-to/advanced/db.md index 51fca76dc..0d3f6ba87 100644 --- a/docs/how-to/advanced/db.md +++ b/docs/how-to/advanced/db.md @@ -29,9 +29,9 @@ The SQLite database is stored using platformdirs: ```python # Default location: -# Linux: ~/.local/share/agentpool/history.db -# Windows: %LOCALAPPDATA%\agentpool\history.db -# macOS: ~/Library/Application Support/agentpool/history.db +# Linux: ~/.local/share/wolfharness/history.db +# Windows: %LOCALAPPDATA%\wolfharness\history.db +# macOS: ~/Library/Application Support/wolfharness/history.db ``` ## Memory Configuration @@ -45,7 +45,7 @@ Agents can be configured with sophisticated memory management: Or via code: ```python -from agentpool_config.session import MemoryConfig, SessionQuery +from wolfharness_config.session import MemoryConfig, SessionQuery # Configure memory management memory_cfg = MemoryConfig( diff --git a/docs/how-to/advanced/db_example.yml b/docs/how-to/advanced/db_example.yml index 21504f07f..15f705a69 100644 --- a/docs/how-to/advanced/db_example.yml +++ b/docs/how-to/advanced/db_example.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: assistant: model: openai:gpt-5-mini diff --git a/docs/how-to/advanced/models_example.yml b/docs/how-to/advanced/models_example.yml index 10c0ac35f..e5dd76cab 100644 --- a/docs/how-to/advanced/models_example.yml +++ b/docs/how-to/advanced/models_example.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: tuned_agent: model: diff --git a/docs/how-to/advanced/system-prompts.md b/docs/how-to/advanced/system-prompts.md index 3edadfe19..40bdc933b 100644 --- a/docs/how-to/advanced/system-prompts.md +++ b/docs/how-to/advanced/system-prompts.md @@ -159,9 +159,9 @@ agents: Provider implementation: ```python -from agentpool.resource_providers import ResourceProvider -from agentpool.prompts.instructions import InstructionFunc -from agentpool.agents.context import AgentContext +from wolfharness.resource_providers import ResourceProvider +from wolfharness.prompts.instructions import InstructionFunc +from wolfharness.agents.context import AgentContext class UserContextProvider(ResourceProvider): async def get_instructions(self) -> list[InstructionFunc]: @@ -255,7 +255,7 @@ If an instruction function fails, the error is logged and the instruction is ski System prompts can include callables that are evaluated when the agent context starts: ```python -from agentpool import Agent +from wolfharness import Agent async def get_weather_context(): weather = await fetch_weather() @@ -418,7 +418,7 @@ The template has access to: - `inject_tools` - tool injection mode (`"off"` or `"all"`) - `tool_usage_style` - tool usage style (`"suggestive"` or `"strict"`) -See `src/agentpool/agents/sys_prompts.py` for implementation details. +See `src/wolfharness/agents/sys_prompts.py` for implementation details. !!! info "About Prompt Engineering" By default, AgentPool does not engage in prompt engineering or manipulation. The features described diff --git a/docs/how-to/configuration/builtin-tools.md b/docs/how-to/configuration/builtin-tools.md index 7fe4eae9f..a02fa9747 100644 --- a/docs/how-to/configuration/builtin-tools.md +++ b/docs/how-to/configuration/builtin-tools.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/builtin_tools.py + - src/wolfharness_config/builtin_tools.py title: Built-in Tools description: PydanticAI built-in tool configuration icon: material/wrench @@ -26,5 +26,5 @@ These tools integrate seamlessly with the agent's capability system and can be c ## Configuration Reference /// mknodes -{{ "agentpool_config.builtin_tools.BuiltinToolConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.builtin_tools.BuiltinToolConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// diff --git a/docs/how-to/configuration/commands.md b/docs/how-to/configuration/commands.md index 5429def52..2972a00bf 100644 --- a/docs/how-to/configuration/commands.md +++ b/docs/how-to/configuration/commands.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/commands.py + - src/wolfharness_config/commands.py title: Commands description: Slash command configuration icon: material/slash-forward-box @@ -23,7 +23,7 @@ Commands are invoked with `/command-name` syntax and support parameter substitut ## Configuration Reference /// mknodes -{{ "agentpool_config.commands.CommandConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.commands.CommandConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Usage Notes diff --git a/docs/how-to/configuration/conditions.md b/docs/how-to/configuration/conditions.md index 60632b84a..d22813f38 100644 --- a/docs/how-to/configuration/conditions.md +++ b/docs/how-to/configuration/conditions.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/conditions.py + - src/wolfharness_config/conditions.py title: Conditions description: Conditional logic for message flow and lifecycle control icon: material/filter-variant @@ -32,7 +32,7 @@ AgentPool supports various condition types: ## Configuration Reference /// mknodes -{{ "agentpool_config.conditions.Condition" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.conditions.Condition" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Control Levels diff --git a/docs/how-to/configuration/connections.md b/docs/how-to/configuration/connections.md index f2d651026..d4ba9cbf7 100644 --- a/docs/how-to/configuration/connections.md +++ b/docs/how-to/configuration/connections.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/forward_targets.py + - src/wolfharness_config/forward_targets.py title: Connections description: Message routing and forwarding configuration icon: material/transit-connection @@ -29,7 +29,7 @@ AgentPool supports three connection types: ## Configuration Reference /// mknodes -{{ "agentpool_config.forward_targets.ForwardingTarget" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.forward_targets.ForwardingTarget" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Connection Types diff --git a/docs/how-to/configuration/event-sources.md b/docs/how-to/configuration/event-sources.md index 01acdc3b2..d54e1ff2d 100644 --- a/docs/how-to/configuration/event-sources.md +++ b/docs/how-to/configuration/event-sources.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/events.py + - src/wolfharness_config/events.py title: Event Sources description: Event source configuration for triggering agent actions icon: material/bell-ring @@ -27,7 +27,7 @@ Event sources are typically used with task configurations to create event-driven ### Event Sources /// mknodes -{{ "agentpool_config.events.EventConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.events.EventConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ### Connection Event Conditions @@ -35,7 +35,7 @@ Event sources are typically used with task configurations to create event-driven Connection events can be filtered using conditions: /// mknodes -{{ "agentpool_config.events.ConnectionEventConditionType" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.events.ConnectionEventConditionType" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Usage with Tasks diff --git a/docs/how-to/configuration/events.md b/docs/how-to/configuration/events.md index 2479923ac..448656ce9 100644 --- a/docs/how-to/configuration/events.md +++ b/docs/how-to/configuration/events.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/event_handlers.py + - src/wolfharness_config/event_handlers.py title: Event Handlers description: Event handler setup and configuration icon: material/bell @@ -11,5 +11,5 @@ icon: material/bell Event handlers allow you to respond to various agent events such as messages, tool calls, and state changes. /// mknodes -{{ "agentpool_config.event_handlers.EventHandlerConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.event_handlers.EventHandlerConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// diff --git a/docs/how-to/configuration/hooks.md b/docs/how-to/configuration/hooks.md index 50db2af08..06ff4db6d 100644 --- a/docs/how-to/configuration/hooks.md +++ b/docs/how-to/configuration/hooks.md @@ -18,13 +18,13 @@ Hooks allow you to intercept and customize agent behavior at key lifecycle point ## Configuration Reference /// mknodes -{{ "agentpool_config.hooks.HooksConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.hooks.HooksConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Hook Types /// mknodes -{{ "agentpool_config.hooks.HookConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.hooks.HookConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Command Hook @@ -178,8 +178,8 @@ agents: ## Programmatic Usage ```python -from agentpool import Agent -from agentpool.hooks import AgentHooks, CallableHook +from wolfharness import Agent +from wolfharness.hooks import AgentHooks, CallableHook def my_pre_tool_hook(tool_name: str, tool_input: dict, **kwargs): if tool_name == "Bash" and "rm" in tool_input.get("command", ""): diff --git a/docs/how-to/configuration/index.md b/docs/how-to/configuration/index.md index 22b6e06f4..3e3d6538c 100644 --- a/docs/how-to/configuration/index.md +++ b/docs/how-to/configuration/index.md @@ -14,7 +14,7 @@ and IDE support for YAML linters by providing an extensive, detailed schema. Here's the complete manifest structure with all available top-level sections: /// mknodes -{{ "agentpool.AgentsManifest" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", as_listitem=False) }} +{{ "wolfharness.AgentsManifest" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", as_listitem=False) }} /// ## Top-Level Sections @@ -97,10 +97,10 @@ Configs are loaded and merged in this order (later sources override earlier ones | Priority | Source | Location | Description | |----------|--------|----------|-------------| -| 1 | Global | `~/.config/agentpool/agentpool.yml` | User-wide preferences | +| 1 | Global | `~/.config/wolfharness/wolfharness.yml` | User-wide preferences | | 2 | Custom | `AGENTPOOL_CONFIG` env var | CI/deployment overrides | | 3 | Inline | `AGENTPOOL_CONFIG_CONTENT` env var | Runtime config as YAML/JSON string | -| 4 | Project | `agentpool.yml` in project root | Project-specific settings | +| 4 | Project | `wolfharness.yml` in project root | Project-specific settings | | 5 | Explicit | CLI argument | Highest precedence | | fallback | Built-in | Package defaults | Only if no agents defined elsewhere | @@ -114,7 +114,7 @@ Configs are loaded and merged in this order (later sources override earlier ones ### Project Config Discovery -Project config is discovered by searching for `agentpool.yml` (or `.yaml`, `.json`, `.jsonc`) starting from the current directory and traversing up to the nearest git repository root. +Project config is discovered by searching for `wolfharness.yml` (or `.yaml`, `.json`, `.jsonc`) starting from the current directory and traversing up to the nearest git repository root. ### Fallback Behavior @@ -127,7 +127,7 @@ The built-in fallback config (e.g., `acp_assistant.yml`) is **only loaded if no === "Global config" - ```yaml title="~/.config/agentpool/agentpool.yml" + ```yaml title="~/.config/wolfharness/wolfharness.yml" # User preferences applied everywhere model_variants: fast: @@ -139,12 +139,12 @@ The built-in fallback config (e.g., `acp_assistant.yml`) is **only loaded if no storage: provider: sql - database_url: sqlite:///~/.local/share/agentpool/history.db + database_url: sqlite:///~/.local/share/wolfharness/history.db ``` === "Project config" - ```yaml title="./agentpool.yml" + ```yaml title="./wolfharness.yml" # Project-specific agents (inherits global model_variants and storage) agents: coder: @@ -184,14 +184,14 @@ Use these commands to inspect config resolution: ```bash # Show which configs are found and loaded -agentpool config show +wolfharness config show # Show standard config paths -agentpool config paths +wolfharness config paths # Create a starter config file -agentpool config init # In current project -agentpool config init global # In global config dir +wolfharness config init # In current project +wolfharness config init global # In global config dir ``` ## File-Level Inheritance (INHERIT) @@ -226,7 +226,7 @@ AgentPool supports UPaths (universal-pathlib) for `INHERIT`, allowing pointing t You can get IDE linter support by adding this line at the top of your YAML: ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json ``` !!! note @@ -237,7 +237,7 @@ You can get IDE linter support by adding this line at the top of your YAML: Load a manifest in your code: ```python -from agentpool import AgentPool +from wolfharness import AgentPool async with AgentPool("agents.yml") as pool: agent = pool.get_agent("analyzer") diff --git a/docs/how-to/configuration/knowledge.md b/docs/how-to/configuration/knowledge.md index 4813cc44b..1a88d3ea9 100644 --- a/docs/how-to/configuration/knowledge.md +++ b/docs/how-to/configuration/knowledge.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/knowledge.py + - src/wolfharness_config/knowledge.py title: Knowledge Sources description: Knowledge source configuration and management icon: material/book-open-variant diff --git a/docs/how-to/configuration/mcp.md b/docs/how-to/configuration/mcp.md index 03386a2f6..6dec3aecd 100644 --- a/docs/how-to/configuration/mcp.md +++ b/docs/how-to/configuration/mcp.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/mcp_server.py + - src/wolfharness_config/mcp_server.py title: MCP Servers description: MCP server configuration and integration icon: material/server-network @@ -31,7 +31,7 @@ Servers can use simple string syntax (e.g., `"python -m mcp_server"`) or detaile ## Configuration Reference /// mknodes -{{ "agentpool_config.mcp_server.MCPServerConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.mcp_server.MCPServerConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Configuration Notes diff --git a/docs/how-to/configuration/node-types/acp-agents.md b/docs/how-to/configuration/node-types/acp-agents.md index 977090464..73cec2c24 100644 --- a/docs/how-to/configuration/node-types/acp-agents.md +++ b/docs/how-to/configuration/node-types/acp-agents.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool/models/acp_agents.py + - src/wolfharness/models/acp_agents.py title: ACP Agents description: External ACP agent integration icon: material/link-variant @@ -34,7 +34,7 @@ These agents can be configured to work alongside AgentPool agents, enabling hybr ## Configuration Reference /// mknodes -{{ "agentpool.models.acp_agents.ACPAgentConfigTypes" | union_to_markdown(display_mode="yaml", header_style="pymdownx", as_listitem=False, wrapped_in="agentname") }} +{{ "wolfharness.models.acp_agents.ACPAgentConfigTypes" | union_to_markdown(display_mode="yaml", header_style="pymdownx", as_listitem=False, wrapped_in="agentname") }} /// ## Configuration Notes diff --git a/docs/how-to/configuration/node-types/agent.md b/docs/how-to/configuration/node-types/agent.md index 267d2439b..2acbeeca2 100644 --- a/docs/how-to/configuration/node-types/agent.md +++ b/docs/how-to/configuration/node-types/agent.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/nodes.py + - src/wolfharness_config/nodes.py title: Agent description: Agent configuration options icon: material/robot @@ -27,7 +27,7 @@ Agent configuration includes: ## Configuration Reference /// mknodes -{{ "agentpool.models.agents.NativeAgentConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", as_listitem=False) }} +{{ "wolfharness.models.agents.NativeAgentConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", as_listitem=False) }} /// ## Configuration Inheritance diff --git a/docs/how-to/configuration/node-types/agui-agents.md b/docs/how-to/configuration/node-types/agui-agents.md index cde93cfda..3ffdbfa32 100644 --- a/docs/how-to/configuration/node-types/agui-agents.md +++ b/docs/how-to/configuration/node-types/agui-agents.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool/models/agui_agents.py + - src/wolfharness/models/agui_agents.py title: AG-UI Agents description: AG-UI protocol agent integration icon: material/monitor diff --git a/docs/how-to/configuration/node-types/claude-code-agents.md b/docs/how-to/configuration/node-types/claude-code-agents.md index ef9249e71..a9fd6771f 100644 --- a/docs/how-to/configuration/node-types/claude-code-agents.md +++ b/docs/how-to/configuration/node-types/claude-code-agents.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool/models/claude_code_agents.py + - src/wolfharness/models/claude_code_agents.py title: Claude Code Agents description: Native Claude Agent SDK integration icon: material/robot-outline diff --git a/docs/how-to/configuration/node-types/codex-agents.md b/docs/how-to/configuration/node-types/codex-agents.md index 074ffae51..fedeec06a 100644 --- a/docs/how-to/configuration/node-types/codex-agents.md +++ b/docs/how-to/configuration/node-types/codex-agents.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool/models/codex_agents.py + - src/wolfharness/models/codex_agents.py title: Codex Agents description: Codex app-server integration icon: material/code-braces @@ -147,7 +147,7 @@ agents: Then use structured output programmatically: ```python -from agentpool import AgentPool +from wolfharness import AgentPool from pydantic import BaseModel class BugReport(BaseModel): @@ -199,7 +199,7 @@ Codex supports three reasoning effort levels that can be changed at runtime: Example runtime mode switching: ```python -from agentpool import AgentPool +from wolfharness import AgentPool async with AgentPool("config.yml") as pool: agent = pool.get_agents()["my_codex"] @@ -233,7 +233,7 @@ await agent.set_mode("always", category_id="approval_policy") Codex provides access to various models. The agent can dynamically fetch available models: ```python -from agentpool import AgentPool +from wolfharness import AgentPool async with AgentPool("config.yml") as pool: agent = pool.get_agents()["my_codex"] diff --git a/docs/how-to/configuration/node-types/team.md b/docs/how-to/configuration/node-types/team.md index 620159174..015e072ec 100644 --- a/docs/how-to/configuration/node-types/team.md +++ b/docs/how-to/configuration/node-types/team.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/teams.py + - src/wolfharness_config/teams.py title: Teams description: Team configuration and setup icon: material/account-group diff --git a/docs/how-to/configuration/observability.md b/docs/how-to/configuration/observability.md index b00e55d99..1e47bca5e 100644 --- a/docs/how-to/configuration/observability.md +++ b/docs/how-to/configuration/observability.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/observability.py + - src/wolfharness_config/observability.py title: Observability description: Monitoring and tracing configuration icon: material/chart-line @@ -25,7 +25,7 @@ These integrations provide automatic instrumentation of agent operations, captur ## Configuration Reference /// mknodes -{{ "agentpool_config.observability.ObservabilityProviderConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.observability.ObservabilityProviderConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Key Features diff --git a/docs/how-to/configuration/path-resolution.md b/docs/how-to/configuration/path-resolution.md index 8aa3a8a9d..278483a59 100644 --- a/docs/how-to/configuration/path-resolution.md +++ b/docs/how-to/configuration/path-resolution.md @@ -2,8 +2,8 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/paths.py - - src/agentpool_config/context.py + - src/wolfharness_config/paths.py + - src/wolfharness_config/context.py title: Configuration Path Resolution description: How relative paths are resolved in configuration files icon: material/map-marker-path @@ -49,7 +49,7 @@ skills: ```bash # Force all paths to resolve to a specific directory export AGENTPOOL_CONFIG_DIR=/custom/path -agentpool serve-acp agents.yml +wolfharness serve-acp agents.yml ``` ### Legacy Mode @@ -57,7 +57,7 @@ agentpool serve-acp agents.yml ```bash # Revert to CWD-relative resolution (pre-RFC-0009 behavior) export AGENTPOOL_LEGACY_PATHS=1 -agentpool serve-acp agents.yml +wolfharness serve-acp agents.yml ``` ## Implementation Details @@ -73,14 +73,14 @@ agentpool serve-acp agents.yml Paths resolved relative to CWD: ```bash cd /home/user/project -agentpool serve-acp agents.yml # ./skills relative to CWD +wolfharness serve-acp agents.yml # ./skills relative to CWD ``` **After (vX.Z):** Paths resolve relative to config file: ```bash cd /home/user/anywhere -agentpool serve-acp /home/user/project/agents.yml # ./skills relative to config dir +wolfharness serve-acp /home/user/project/agents.yml # ./skills relative to config dir ``` To maintain old behavior, set `AGENTPOOL_LEGACY_PATHS=1`. diff --git a/docs/how-to/configuration/prompt-hubs.md b/docs/how-to/configuration/prompt-hubs.md index 7f68110fe..7ed9f3280 100644 --- a/docs/how-to/configuration/prompt-hubs.md +++ b/docs/how-to/configuration/prompt-hubs.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/prompt_hubs.py + - src/wolfharness_config/prompt_hubs.py title: Prompt Hubs description: External prompt hub integration icon: material/library @@ -24,7 +24,7 @@ These integrations allow you to fetch prompts from these services by identifiers ## Configuration Reference /// mknodes -{{ "agentpool_config.prompt_hubs.PromptHubConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.prompt_hubs.PromptHubConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Configuration Notes diff --git a/docs/how-to/configuration/prompts.md b/docs/how-to/configuration/prompts.md index 05442893c..11cb20a0b 100644 --- a/docs/how-to/configuration/prompts.md +++ b/docs/how-to/configuration/prompts.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/system_prompts.py + - src/wolfharness_config/system_prompts.py title: System Prompts description: System prompt configuration and library icon: material/format-text @@ -37,7 +37,7 @@ System prompts can be categorized by their purpose: ## Configuration Reference /// mknodes -{{ "agentpool_config.system_prompts.PromptConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.system_prompts.PromptConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Complete Example diff --git a/docs/how-to/configuration/prompts_example.yml b/docs/how-to/configuration/prompts_example.yml index de1119550..a466a6e1d 100644 --- a/docs/how-to/configuration/prompts_example.yml +++ b/docs/how-to/configuration/prompts_example.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json prompts: # Define reusable system prompts system_prompts: diff --git a/docs/how-to/configuration/resources.md b/docs/how-to/configuration/resources.md index d354ce238..8f412ef9e 100644 --- a/docs/how-to/configuration/resources.md +++ b/docs/how-to/configuration/resources.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/loaders.py + - src/wolfharness_config/loaders.py title: Resources description: Resource loader configuration icon: material/file-multiple @@ -25,7 +25,7 @@ Resources are loaded on-demand when agents request them, supporting parameteriza ## Configuration Reference -For the full resource type reference, see the `agentpool_config.loaders.Resource` schema documentation. +For the full resource type reference, see the `wolfharness_config.loaders.Resource` schema documentation. ## Key Features @@ -62,9 +62,9 @@ ResourceProviders can now provide dynamic instructions that are re-evaluated on ResourceProviders can implement the `get_instructions()` method to return instruction functions: ```python -from agentpool.resource_providers import ResourceProvider -from agentpool.prompts.instructions import InstructionFunc -from agentpool.agents.context import AgentContext +from wolfharness.resource_providers import ResourceProvider +from wolfharness.prompts.instructions import InstructionFunc +from wolfharness.agents.context import AgentContext class MyProvider(ResourceProvider): async def get_instructions(self) -> list[InstructionFunc]: diff --git a/docs/how-to/configuration/responses.yml b/docs/how-to/configuration/responses.yml index 578425427..08e1ded61 100644 --- a/docs/how-to/configuration/responses.yml +++ b/docs/how-to/configuration/responses.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json responses: WebResult: response_schema: diff --git a/docs/how-to/configuration/session.md b/docs/how-to/configuration/session.md index f8b62680b..dcb6ecd91 100644 --- a/docs/how-to/configuration/session.md +++ b/docs/how-to/configuration/session.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/session.py + - src/wolfharness_config/session.py title: Sessions description: Session management and configuration icon: material/account-clock diff --git a/docs/how-to/configuration/skill-uri-usage.md b/docs/how-to/configuration/skill-uri-usage.md index 6cb01a627..d9975b39b 100644 --- a/docs/how-to/configuration/skill-uri-usage.md +++ b/docs/how-to/configuration/skill-uri-usage.md @@ -42,7 +42,7 @@ When you use a bare skill name, AgentPool searches all skill sources (local skills first, then MCP providers) in priority order: ```python -from agentpool import AgentPool +from wolfharness import AgentPool async with AgentPool("config.yml") as pool: agent = pool.get_agent("assistant") diff --git a/docs/how-to/configuration/skills.md b/docs/how-to/configuration/skills.md index 6ca20e935..c32ad4aeb 100644 --- a/docs/how-to/configuration/skills.md +++ b/docs/how-to/configuration/skills.md @@ -24,7 +24,7 @@ Skills injection allows you to: skills: # Skill discovery paths paths: - - ~/.config/agentpool/skills + - ~/.config/wolfharness/skills - ./skills # Include default AgentPool skills (default: true) @@ -87,7 +87,7 @@ With `inject: all` (or matcher-selected skills), full content is also injected: # Global skills configuration skills: paths: - - ~/.config/agentpool/skills + - ~/.config/wolfharness/skills - ./project-skills include_default: true diff --git a/docs/how-to/configuration/storage.md b/docs/how-to/configuration/storage.md index dee632998..a72a3b045 100644 --- a/docs/how-to/configuration/storage.md +++ b/docs/how-to/configuration/storage.md @@ -2,7 +2,7 @@ sync: agent: doc_sync_agent dependencies: - - src/agentpool_config/storage.py + - src/wolfharness_config/storage.py title: Storage description: Database and storage setup icon: material/database @@ -13,7 +13,7 @@ The storage configuration defines how agent interactions, messages, and tool usa ## Configuration Reference /// mknodes -{{ "agentpool_config.storage.StorageProviderConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.storage.StorageProviderConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Overview diff --git a/docs/how-to/configuration/storage_example.yml b/docs/how-to/configuration/storage_example.yml index 705d2eb55..89670ffa6 100644 --- a/docs/how-to/configuration/storage_example.yml +++ b/docs/how-to/configuration/storage_example.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json storage: # Global settings agents: ["planner", "executor"] diff --git a/docs/how-to/configuration/tasks.md b/docs/how-to/configuration/tasks.md index 2c329f4f3..7121f2682 100644 --- a/docs/how-to/configuration/tasks.md +++ b/docs/how-to/configuration/tasks.md @@ -129,7 +129,7 @@ All tasks are available through the agent pool: job = pool.get_job("analyze_code") # Register new task -from agentpool_config.task import Job +from wolfharness_config.task import Job job = Job( name="new_task", description="A new task", diff --git a/docs/how-to/configuration/toolsets/code-mode.md b/docs/how-to/configuration/toolsets/code-mode.md index 4b84213c9..b1aca24c4 100644 --- a/docs/how-to/configuration/toolsets/code-mode.md +++ b/docs/how-to/configuration/toolsets/code-mode.md @@ -27,5 +27,5 @@ Instead of calling tools directly, the agent generates Python code that invokes ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.CodeModeToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.CodeModeToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// diff --git a/docs/how-to/configuration/toolsets/code.md b/docs/how-to/configuration/toolsets/code.md index 32a42997c..f5d27a28e 100644 --- a/docs/how-to/configuration/toolsets/code.md +++ b/docs/how-to/configuration/toolsets/code.md @@ -20,8 +20,8 @@ agents: ## Available Tools ```python exec="true" -from agentpool_toolsets.builtin.code import CodeTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.builtin.code import CodeTools +from wolfharness.docs.utils import generate_tool_docs toolset = CodeTools() print(generate_tool_docs(toolset)) @@ -30,5 +30,5 @@ print(generate_tool_docs(toolset)) ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.CodeToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.CodeToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// diff --git a/docs/how-to/configuration/toolsets/composio.md b/docs/how-to/configuration/toolsets/composio.md index 097917a56..4f9361e3d 100644 --- a/docs/how-to/configuration/toolsets/composio.md +++ b/docs/how-to/configuration/toolsets/composio.md @@ -23,7 +23,7 @@ agents: ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.ComposioToolSetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} +{{ "wolfharness_config.toolsets.ComposioToolSetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} /// ## Available Apps diff --git a/docs/how-to/configuration/toolsets/config-creation.md b/docs/how-to/configuration/toolsets/config-creation.md index 9a7f106da..a80747bfe 100644 --- a/docs/how-to/configuration/toolsets/config-creation.md +++ b/docs/how-to/configuration/toolsets/config-creation.md @@ -20,8 +20,8 @@ agents: ## Available Tools ```python exec="true" -from agentpool_toolsets.config_creation import ConfigCreationTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.config_creation import ConfigCreationTools +from wolfharness.docs.utils import generate_tool_docs toolset = ConfigCreationTools() print(generate_tool_docs(toolset)) @@ -36,5 +36,5 @@ print(generate_tool_docs(toolset)) ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.ConfigCreationToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.ConfigCreationToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// diff --git a/docs/how-to/configuration/toolsets/custom.md b/docs/how-to/configuration/toolsets/custom.md index ee4edc4ec..7595eab36 100644 --- a/docs/how-to/configuration/toolsets/custom.md +++ b/docs/how-to/configuration/toolsets/custom.md @@ -21,7 +21,7 @@ agents: ## Creating a Custom Toolset ```python -from agentpool.resource_providers import ResourceProvider +from wolfharness.resource_providers import ResourceProvider class MyCustomToolset(ResourceProvider): async def get_tools(self): @@ -37,5 +37,5 @@ class MyCustomToolset(ResourceProvider): ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.CustomToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.CustomToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// diff --git a/docs/how-to/configuration/toolsets/entry-points.md b/docs/how-to/configuration/toolsets/entry-points.md index 7ca9fb050..1850d95a2 100644 --- a/docs/how-to/configuration/toolsets/entry-points.md +++ b/docs/how-to/configuration/toolsets/entry-points.md @@ -32,5 +32,5 @@ The toolset then discovers and loads these tools automatically. ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.EntryPointToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} +{{ "wolfharness_config.toolsets.EntryPointToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} /// diff --git a/docs/how-to/configuration/toolsets/execution.md b/docs/how-to/configuration/toolsets/execution.md index bbfa46c94..73f8f4ca6 100644 --- a/docs/how-to/configuration/toolsets/execution.md +++ b/docs/how-to/configuration/toolsets/execution.md @@ -56,8 +56,8 @@ Execute on remote machines via SSH or other protocols. ## Available Tools ```python exec="true" -from agentpool_toolsets.builtin.execution_environment import ProcessManagementTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.builtin.execution_environment import ProcessManagementTools +from wolfharness.docs.utils import generate_tool_docs toolset = ProcessManagementTools() print(generate_tool_docs(toolset)) @@ -80,7 +80,7 @@ tools: ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.ProcessManagementToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.ProcessManagementToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// ## Security Considerations diff --git a/docs/how-to/configuration/toolsets/file-access.md b/docs/how-to/configuration/toolsets/file-access.md index a1e33e451..f6b53fc9a 100644 --- a/docs/how-to/configuration/toolsets/file-access.md +++ b/docs/how-to/configuration/toolsets/file-access.md @@ -62,8 +62,8 @@ tools: ## Available Tools ```python exec="true" -from agentpool_toolsets.fsspec_toolset import FSSpecTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.fsspec_toolset import FSSpecTools +from wolfharness.docs.utils import generate_tool_docs toolset = FSSpecTools() print(generate_tool_docs(toolset)) @@ -72,7 +72,7 @@ print(generate_tool_docs(toolset)) ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.FSSpecToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.FSSpecToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// ## Examples diff --git a/docs/how-to/configuration/toolsets/import-tools.md b/docs/how-to/configuration/toolsets/import-tools.md index ec5e1acf6..2d2e092c9 100644 --- a/docs/how-to/configuration/toolsets/import-tools.md +++ b/docs/how-to/configuration/toolsets/import-tools.md @@ -39,7 +39,7 @@ Each tool supports these options: ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.ImportToolsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.ImportToolsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// ## Use Cases diff --git a/docs/how-to/configuration/toolsets/notifications.md b/docs/how-to/configuration/toolsets/notifications.md index 6cc2b8933..d20915d2d 100644 --- a/docs/how-to/configuration/toolsets/notifications.md +++ b/docs/how-to/configuration/toolsets/notifications.md @@ -25,8 +25,8 @@ agents: ## Available Tools ```python exec="true" -from agentpool_toolsets.notifications import NotificationsTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.notifications import NotificationsTools +from wolfharness.docs.utils import generate_tool_docs toolset = NotificationsTools() print(generate_tool_docs(toolset)) @@ -42,5 +42,5 @@ print(generate_tool_docs(toolset)) ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.NotificationsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.NotificationsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// diff --git a/docs/how-to/configuration/toolsets/openapi.md b/docs/how-to/configuration/toolsets/openapi.md index d94af0daf..32b4600a3 100644 --- a/docs/how-to/configuration/toolsets/openapi.md +++ b/docs/how-to/configuration/toolsets/openapi.md @@ -59,7 +59,7 @@ Each OpenAPI operation becomes a tool with: ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.OpenAPIToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} +{{ "wolfharness_config.toolsets.OpenAPIToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} /// ## Tips diff --git a/docs/how-to/configuration/toolsets/remote-code-mode.md b/docs/how-to/configuration/toolsets/remote-code-mode.md index 62dcf243a..ce5997017 100644 --- a/docs/how-to/configuration/toolsets/remote-code-mode.md +++ b/docs/how-to/configuration/toolsets/remote-code-mode.md @@ -23,5 +23,5 @@ agents: ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.RemoteCodeModeToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.RemoteCodeModeToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// diff --git a/docs/how-to/configuration/toolsets/search.md b/docs/how-to/configuration/toolsets/search.md index b4c551de4..fe16514b0 100644 --- a/docs/how-to/configuration/toolsets/search.md +++ b/docs/how-to/configuration/toolsets/search.md @@ -69,8 +69,8 @@ tools: ## Available Tools ```python exec="true" -from agentpool_toolsets.search_toolset import SearchTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.search_toolset import SearchTools +from wolfharness.docs.utils import generate_tool_docs toolset = SearchTools() print(generate_tool_docs(toolset)) @@ -79,7 +79,7 @@ print(generate_tool_docs(toolset)) ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.SearchToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.SearchToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// ## Environment Variables diff --git a/docs/how-to/configuration/toolsets/skills.md b/docs/how-to/configuration/toolsets/skills.md index ef7442509..d685dd822 100644 --- a/docs/how-to/configuration/toolsets/skills.md +++ b/docs/how-to/configuration/toolsets/skills.md @@ -35,9 +35,9 @@ pool loads skills. Call them directly; no separate toolset is needed. ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.SkillsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.SkillsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// /// mknodes -{{ "agentpool_config.toolsets.SkillsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.SkillsToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// diff --git a/docs/how-to/configuration/toolsets/subagent.md b/docs/how-to/configuration/toolsets/subagent.md index 61352e38c..2dfa31fd3 100644 --- a/docs/how-to/configuration/toolsets/subagent.md +++ b/docs/how-to/configuration/toolsets/subagent.md @@ -20,8 +20,8 @@ agents: ## Available Tools ```python exec="true" -from agentpool_toolsets.builtin.subagent_tools import SubagentTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.builtin.subagent_tools import SubagentTools +from wolfharness.docs.utils import generate_tool_docs toolset = SubagentTools() print(generate_tool_docs(toolset)) @@ -30,7 +30,7 @@ print(generate_tool_docs(toolset)) ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.SubagentToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.SubagentToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// ## Example Workflow diff --git a/docs/how-to/configuration/toolsets/user-interaction.md b/docs/how-to/configuration/toolsets/user-interaction.md index 01a088289..5331a4daf 100644 --- a/docs/how-to/configuration/toolsets/user-interaction.md +++ b/docs/how-to/configuration/toolsets/user-interaction.md @@ -50,4 +50,4 @@ Supported question types: The tool returns the user's answers through the agent tool result. Agents can then use the answers to decide next steps or personalize their response. -For implementation details, see `agentpool_toolsets.builtin.question_tools`. +For implementation details, see `wolfharness_toolsets.builtin.question_tools`. diff --git a/docs/how-to/configuration/toolsets/vfs.md b/docs/how-to/configuration/toolsets/vfs.md index 2c2a8d1a3..e9b8b6a2d 100644 --- a/docs/how-to/configuration/toolsets/vfs.md +++ b/docs/how-to/configuration/toolsets/vfs.md @@ -33,8 +33,8 @@ agents: ## Available Tools ```python exec="true" -from agentpool_toolsets.vfs_toolset import VFSTools -from agentpool.docs.utils import generate_tool_docs +from wolfharness_toolsets.vfs_toolset import VFSTools +from wolfharness.docs.utils import generate_tool_docs toolset = VFSTools() print(generate_tool_docs(toolset)) @@ -61,7 +61,7 @@ vfs_read("code/src", recursive=True) ## Configuration Reference /// mknodes -{{ "agentpool_config.toolsets.VFSToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} +{{ "wolfharness_config.toolsets.VFSToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets", header_level=3) }} /// ## When to Use VFS vs File Access diff --git a/docs/how-to/configuration/toolsets/workers.md b/docs/how-to/configuration/toolsets/workers.md index 3f8a5e5e0..77cb8a1ac 100644 --- a/docs/how-to/configuration/toolsets/workers.md +++ b/docs/how-to/configuration/toolsets/workers.md @@ -36,7 +36,7 @@ This creates tools `ask_code_reviewer` and `ask_researcher` for the manager agen ## Toolset Configuration /// mknodes -{{ "agentpool_config.toolsets.WorkersToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} +{{ "wolfharness_config.toolsets.WorkersToolsetConfig" | schema_to_markdown(display_mode="yaml", header_style="pymdownx", wrapped_in="toolsets") }} /// ## Worker Types @@ -101,7 +101,7 @@ tools: ## Worker Configuration Reference /// mknodes -{{ "agentpool_config.workers.WorkerConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} +{{ "wolfharness_config.workers.WorkerConfig" | union_to_markdown(display_mode="yaml", header_style="pymdownx") }} /// ## Example: Manager with Specialized Workers diff --git a/docs/how-to/programmatic-usage/injection.md b/docs/how-to/programmatic-usage/injection.md index 36d2fe5b8..085f82c18 100644 --- a/docs/how-to/programmatic-usage/injection.md +++ b/docs/how-to/programmatic-usage/injection.md @@ -229,7 +229,7 @@ async def shared_analysis( The connection between your YAML manifest and the injection system is made through the AgentPool: ```python -from agentpool import AgentPool, AgentsManifest +from wolfharness import AgentPool, AgentsManifest # 1. Define your agents in YAML manifest = """ diff --git a/docs/how-to/programmatic-usage/run-interface.md b/docs/how-to/programmatic-usage/run-interface.md index 97faa8312..19f464654 100644 --- a/docs/how-to/programmatic-usage/run-interface.md +++ b/docs/how-to/programmatic-usage/run-interface.md @@ -5,7 +5,7 @@ description: Agent and team run interface # Run Interface -The run interface provides a consistent way to interact with all message handlers (Agents, Teams, TeamRuns) in agentpool. +The run interface provides a consistent way to interact with all message handlers (Agents, Teams, TeamRuns) in wolfharness. It serves both as a messaging protocol and a public API. ## Core Methods diff --git a/docs/how-to/programmatic-usage/task-monitoring.md b/docs/how-to/programmatic-usage/task-monitoring.md index 62012c6ad..eba4aaaa2 100644 --- a/docs/how-to/programmatic-usage/task-monitoring.md +++ b/docs/how-to/programmatic-usage/task-monitoring.md @@ -86,7 +86,7 @@ stats.error_log # Any errors that occurred ### Example: Monitored Execution ```python -from agentpool import AgentPool +from wolfharness import AgentPool async def main(): async with AgentPool() as pool: diff --git a/docs/how-to/programmatic-usage/team.md b/docs/how-to/programmatic-usage/team.md index 01ad2c7bc..d277f30ca 100644 --- a/docs/how-to/programmatic-usage/team.md +++ b/docs/how-to/programmatic-usage/team.md @@ -19,7 +19,7 @@ Teams in AgentPool allow you to organize and orchestrate multiple agents as a gr ### Direct Creation ```python -from agentpool.delegation import Team +from wolfharness.delegation import Team team = Team( agents=[agent1, agent2, agent3], @@ -62,7 +62,7 @@ for agent_name, error in team_response.errors.items(): For sequential execution, use `TeamRun` instead: ```python -from agentpool import TeamRun +from wolfharness import TeamRun # Create sequential pipeline run = TeamRun([agent1, agent2, agent3]) diff --git a/docs/how-to/programmatic-usage/tools.md b/docs/how-to/programmatic-usage/tools.md index 56b4f5451..fa6d1da18 100644 --- a/docs/how-to/programmatic-usage/tools.md +++ b/docs/how-to/programmatic-usage/tools.md @@ -20,7 +20,7 @@ The `ToolManager` is a central component that manages the lifecycle and availabi ### 1. Tool Registration and Access ```python -from agentpool.tools import ToolManager +from wolfharness.tools import ToolManager diff --git a/docs/how-to/servers/acp-server.md b/docs/how-to/servers/acp-server.md index 8a3b489ca..6ba78fa2a 100644 --- a/docs/how-to/servers/acp-server.md +++ b/docs/how-to/servers/acp-server.md @@ -22,10 +22,10 @@ ACP is a standardized JSON-RPC 2.0 protocol that provides: ```bash # Using uvx (no installation required) -uvx --python 3.13 agentpool@latest serve-acp config.yml +uvx --python 3.13 wolfharness@latest serve-acp config.yml # Or with installed package -agentpool serve-acp config.yml +wolfharness serve-acp config.yml ``` See [`serve-acp`](../../reference/cli/serve-acp.md) for all CLI options. @@ -43,7 +43,7 @@ Add to your Zed `settings.json`: "command": "uvx", "args": [ "--python", "3.13", - "agentpool[coding]@latest", + "wolfharness[coding]@latest", "serve-acp", "path/to/config.yml" ], @@ -59,7 +59,7 @@ Add to your Zed `settings.json`: For IDEs that support ACP: -1. Set command to `agentpool` or `uvx agentpool@latest` +1. Set command to `wolfharness` or `uvx wolfharness@latest` 2. Add `serve-acp` as first argument 3. Specify your configuration file 4. Add CLI options as needed diff --git a/docs/how-to/servers/agui-server.md b/docs/how-to/servers/agui-server.md index 209ed66e5..6632c071a 100644 --- a/docs/how-to/servers/agui-server.md +++ b/docs/how-to/servers/agui-server.md @@ -22,10 +22,10 @@ GET http://localhost:8002/ -> List all agents ```bash # Run with default settings -agentpool serve-agui config.yml +wolfharness serve-agui config.yml # Custom host and port -agentpool serve-agui config.yml --host 0.0.0.0 --port 8002 +wolfharness serve-agui config.yml --host 0.0.0.0 --port 8002 ``` See [`serve-agui`](../../reference/cli/serve-agui.md) for all CLI options. @@ -34,8 +34,8 @@ See [`serve-agui`](../../reference/cli/serve-agui.md) for all CLI options. ```python import anyio -from agentpool import AgentPool -from agentpool_server.agui_server import AGUIServer +from wolfharness import AgentPool +from wolfharness_server.agui_server import AGUIServer async def main(): diff --git a/docs/how-to/servers/index.md b/docs/how-to/servers/index.md index 43cea52ee..f528af0e1 100644 --- a/docs/how-to/servers/index.md +++ b/docs/how-to/servers/index.md @@ -12,11 +12,11 @@ AgentPool can expose agents through multiple server protocols, enabling integrat | Server | Protocol | Use Case | CLI Command | |--------|----------|----------|-------------| -| [ACP Server](acp-server.md) | Agent Communication Protocol | IDE integration (Zed, etc.) | `agentpool serve-acp` | -| [OpenCode Server](opencode-server.md) | OpenCode REST + SSE | OpenCode TUI/Desktop | `agentpool serve-opencode` | -| [MCP Server](mcp-server.md) | Model Context Protocol | Expose tools to other agents | `agentpool serve-mcp` | -| [AG-UI Server](agui-server.md) | AG-UI Protocol | AG-UI compatible clients | `agentpool serve-agui` | -| [OpenAI API Server](openai-api-server.md) | OpenAI API | Drop-in OpenAI replacement | `agentpool serve-api` | +| [ACP Server](acp-server.md) | Agent Communication Protocol | IDE integration (Zed, etc.) | `wolfharness serve-acp` | +| [OpenCode Server](opencode-server.md) | OpenCode REST + SSE | OpenCode TUI/Desktop | `wolfharness serve-opencode` | +| [MCP Server](mcp-server.md) | Model Context Protocol | Expose tools to other agents | `wolfharness serve-mcp` | +| [AG-UI Server](agui-server.md) | AG-UI Protocol | AG-UI compatible clients | `wolfharness serve-agui` | +| [OpenAI API Server](openai-api-server.md) | OpenAI API | Drop-in OpenAI replacement | `wolfharness serve-api` | ## Architecture Overview @@ -113,19 +113,19 @@ You can run multiple servers simultaneously: ```bash # Terminal 1: ACP for IDE -agentpool serve-acp config.yml +wolfharness serve-acp config.yml # Terminal 2: OpenCode for TUI -agentpool serve-opencode config.yml --port 4096 +wolfharness serve-opencode config.yml --port 4096 # Terminal 3: MCP for tool sharing -agentpool serve-mcp config.yml +wolfharness serve-mcp config.yml # Terminal 4: AG-UI for custom frontends -agentpool serve-agui config.yml --port 8002 +wolfharness serve-agui config.yml --port 8002 # Terminal 5: OpenAI API for SDK compatibility -agentpool serve-api config.yml --port 8000 +wolfharness serve-api config.yml --port 8000 ``` ### Common Configuration diff --git a/docs/how-to/servers/mcp-server.md b/docs/how-to/servers/mcp-server.md index 44fd92b3c..ffb8892dd 100644 --- a/docs/how-to/servers/mcp-server.md +++ b/docs/how-to/servers/mcp-server.md @@ -21,10 +21,10 @@ MCP is a standardized protocol for AI tool integration: ```bash # Expose tools from a pool -agentpool serve-mcp config.yml +wolfharness serve-mcp config.yml # Or expose specific toolsets -agentpool serve-mcp config.yml --toolset file_access --toolset search +wolfharness serve-mcp config.yml --toolset file_access --toolset search ``` See [`serve-mcp`](../../reference/cli/serve-mcp.md) for all CLI options. @@ -37,7 +37,7 @@ Expose your custom tools to Claude Code: ```bash # Start MCP server -agentpool serve-mcp tools.yml --transport sse --port 3001 +wolfharness serve-mcp tools.yml --transport sse --port 3001 ``` Configure Claude Code's `mcp.json`: @@ -45,7 +45,7 @@ Configure Claude Code's `mcp.json`: ```json { "mcpServers": { - "agentpool": { + "wolfharness": { "url": "http://localhost:3001/sse" } } @@ -80,8 +80,8 @@ External agents can now delegate to your experts via MCP. Expose custom Python functions: ```python -from agentpool import Agent -from agentpool.tools import tool +from wolfharness import Agent +from wolfharness.tools import tool @tool def analyze_data(data: str) -> str: @@ -93,7 +93,7 @@ agent = Agent("analyzer", model="...", tools=[analyze_data]) ``` ```bash -agentpool serve-mcp --config agent.yml +wolfharness serve-mcp --config agent.yml ``` ## Exposed Capabilities @@ -165,7 +165,7 @@ graph LR For subprocess communication: ```bash -agentpool serve-mcp config.yml --transport stdio +wolfharness serve-mcp config.yml --transport stdio ``` Used when the client spawns the server as a subprocess. @@ -175,7 +175,7 @@ Used when the client spawns the server as a subprocess. For HTTP-based communication: ```bash -agentpool serve-mcp config.yml --transport sse --port 3001 +wolfharness serve-mcp config.yml --transport sse --port 3001 ``` diff --git a/docs/how-to/servers/openai-api-server.md b/docs/how-to/servers/openai-api-server.md index 5302b18b3..74c98f6ed 100644 --- a/docs/how-to/servers/openai-api-server.md +++ b/docs/how-to/servers/openai-api-server.md @@ -22,10 +22,10 @@ POST /v1/responses -> Responses API ```bash # Run with default settings -agentpool serve-api config.yml +wolfharness serve-api config.yml # Custom host and port -agentpool serve-api config.yml --host 0.0.0.0 --port 8000 +wolfharness serve-api config.yml --host 0.0.0.0 --port 8000 ``` See [`serve-api`](../../reference/cli/serve-api.md) for all CLI options. @@ -34,8 +34,8 @@ See [`serve-api`](../../reference/cli/serve-api.md) for all CLI options. ```python import anyio -from agentpool import AgentPool -from agentpool_server.openai_api_server import OpenAIAPIServer +from wolfharness import AgentPool +from wolfharness_server.openai_api_server import OpenAIAPIServer async def main(): @@ -76,13 +76,13 @@ Returns available agents as OpenAI-compatible models: "id": "assistant", "object": "model", "created": 0, - "owned_by": "agentpool" + "owned_by": "wolfharness" }, { "id": "coder", "object": "model", "created": 0, - "owned_by": "agentpool" + "owned_by": "wolfharness" } ] } diff --git a/docs/how-to/servers/opencode-server.md b/docs/how-to/servers/opencode-server.md index 9a4c5fc64..895d47b48 100644 --- a/docs/how-to/servers/opencode-server.md +++ b/docs/how-to/servers/opencode-server.md @@ -21,7 +21,7 @@ The OpenCode API protocol can be used by any compatible client to interact with ```bash # Start the OpenCode server -agentpool serve-opencode config.yml --port 4096 +wolfharness serve-opencode config.yml --port 4096 ``` See [`serve-opencode`](../../reference/cli/serve-opencode.md) for all CLI options. @@ -218,8 +218,8 @@ Connect to `/global/event` or `/event` to receive events. ## Programmatic Usage ```python -from agentpool_server.opencode_server.server import OpenCodeServer -from agentpool import Agent +from wolfharness_server.opencode_server.server import OpenCodeServer +from wolfharness import Agent async def main(): agent = Agent("assistant", model="openai:gpt-4o") @@ -250,7 +250,7 @@ Current limitations: - `fork` not yet implemented - Symbol search returns empty (LSP not integrated) -See [ENDPOINTS.md](https://github.com/Million-mo/agentpool/blob/main/src/agentpool_server/opencode_server/ENDPOINTS.md) for full implementation status. +See [ENDPOINTS.md](https://github.com/Million-mo/wolfharness/blob/main/src/wolfharness_server/opencode_server/ENDPOINTS.md) for full implementation status. ## See Also diff --git a/docs/index.md b/docs/index.md index 970a8ef4e..bdf1e8725 100644 --- a/docs/index.md +++ b/docs/index.md @@ -65,7 +65,7 @@ Everything is a MessageNode - enabling seamless composition: ## License -MIT License - see [LICENSE](https://github.com/Leoyzen/agentpool/blob/main/LICENSE) for details. +MIT License - see [LICENSE](https://github.com/Leoyzen/wolfharness/blob/main/LICENSE) for details. ## Documentation @@ -95,7 +95,7 @@ agents: ### Python Usage ```python -from agentpool import AgentPool +from wolfharness import AgentPool async def main(): async with AgentPool("agents.yml") as pool: diff --git a/docs/records/acp-protocol-gap-analysis.md b/docs/records/acp-protocol-gap-analysis.md index 75831117b..64d49fe94 100644 --- a/docs/records/acp-protocol-gap-analysis.md +++ b/docs/records/acp-protocol-gap-analysis.md @@ -22,9 +22,9 @@ AgentPool's ACP implementation covers **most v1 stable features** but has severa This analysis was produced by: -1. **Code-level verification** of AgentPool schema definitions in `packages/agentpool/src/acp/schema/` -2. **Protocol interface audit** of `packages/agentpool/src/acp/agent/protocol.py` and `packages/agentpool/src/acp/client/protocol.py` -3. **Server implementation review** of `packages/agentpool/src/agentpool_server/acp_server/` +1. **Code-level verification** of AgentPool schema definitions in `packages/wolfharness/src/acp/schema/` +2. **Protocol interface audit** of `packages/wolfharness/src/acp/agent/protocol.py` and `packages/wolfharness/src/acp/client/protocol.py` +3. **Server implementation review** of `packages/wolfharness/src/wolfharness_server/acp_server/` 4. **Cross-reference** with ACP spec Rust types in `packages/agent-client-protocol/src/v1/` and `src/v2/` 5. **Documentation review** of ACP protocol docs in `packages/agent-client-protocol/docs/` @@ -58,7 +58,7 @@ All claims below include specific file paths and line numbers for traceability. - `src/acp/schema/messages.py:34` — `AgentMethod` includes `"session/close"` in the Literal - `src/acp/agent/protocol.py:57` — Interface method is `stop_session()` -- `src/agentpool_server/acp_server/acp_agent.py:843` — Implementation is `stop_session()` +- `src/wolfharness_server/acp_server/acp_agent.py:843` — Implementation is `stop_session()` - `src/acp/bridge/bridge.py:143-146` — Bridge correctly maps `"session/close"` → `stop_session()` **Note:** The bridge handles the wire protocol mapping, so external clients using `session/close` work correctly. However, internal code and type names use `stop`, which is confusing and inconsistent with the spec. @@ -94,7 +94,7 @@ This means the capability can never be advertised to clients, despite the method The spec defines `providers/list`, `providers/set`, `providers/disable` as **native agent methods** with typed responses (`ListProvidersResponse`, `SetProvidersResponse`, `DisableProvidersResponse`). AgentPool implements them in `ext_method()` returning raw dicts: ```python -# src/agentpool_server/acp_server/acp_agent.py:875-898 +# src/wolfharness_server/acp_server/acp_agent.py:875-898 async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]: match method: case "providers/list": @@ -110,7 +110,7 @@ async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any **Evidence of partial schema support:** - `src/acp/schema/providers.py` — Defines `ProviderInfo`, `ProviderStatus` - `src/acp/schema/capabilities.py:272` — `providers: bool` capability flag -- `src/agentpool_server/acp_server/provider_router.py` — Full provider management logic +- `src/wolfharness_server/acp_server/provider_router.py` — Full provider management logic --- @@ -224,7 +224,7 @@ The spec defines `$/cancel_request` as a protocol-level method to cancel in-flig **Status:** Confirmed missing **Impact:** AgentPool cannot act as MCP proxy over ACP -The spec defines `mcp/connect`, `mcp/message`, `mcp/disconnect` for tunneling MCP through ACP. AgentPool has MCP server support (`src/agentpool/mcp_server/`) but no MCP-over-ACP bridge. +The spec defines `mcp/connect`, `mcp/message`, `mcp/disconnect` for tunneling MCP through ACP. AgentPool has MCP server support (`src/wolfharness/mcp_server/`) but no MCP-over-ACP bridge. **Evidence:** - `src/acp/schema/messages.py:22-35` — No `mcp/*` methods in `AgentMethod` @@ -420,26 +420,26 @@ This document was reviewed by an Oracle agent with full codebase access. Key cor Key files referenced in this analysis: **Schema Definitions:** -- `packages/agentpool/src/acp/schema/base.py` — Base models, `_meta` handling -- `packages/agentpool/src/acp/schema/messages.py` — `AgentMethod`, `ClientMethod` enums -- `packages/agentpool/src/acp/schema/client_requests.py` — All client→agent request types -- `packages/agentpool/src/acp/schema/agent_responses.py` — All agent→client response types -- `packages/agentpool/src/acp/schema/session_updates.py` — Session update notification types -- `packages/agentpool/src/acp/schema/tool_call.py` — Tool call types, `ToolCallLocation` -- `packages/agentpool/src/acp/schema/client_responses.py` — Client response types, terminal -- `packages/agentpool/src/acp/schema/elicitation.py` — Elicitation types -- `packages/agentpool/src/acp/schema/providers.py` — Provider types -- `packages/agentpool/src/acp/schema/capabilities.py` — Capability flags -- `packages/agentpool/src/acp/schema/notifications.py` — Notification types +- `packages/wolfharness/src/acp/schema/base.py` — Base models, `_meta` handling +- `packages/wolfharness/src/acp/schema/messages.py` — `AgentMethod`, `ClientMethod` enums +- `packages/wolfharness/src/acp/schema/client_requests.py` — All client→agent request types +- `packages/wolfharness/src/acp/schema/agent_responses.py` — All agent→client response types +- `packages/wolfharness/src/acp/schema/session_updates.py` — Session update notification types +- `packages/wolfharness/src/acp/schema/tool_call.py` — Tool call types, `ToolCallLocation` +- `packages/wolfharness/src/acp/schema/client_responses.py` — Client response types, terminal +- `packages/wolfharness/src/acp/schema/elicitation.py` — Elicitation types +- `packages/wolfharness/src/acp/schema/providers.py` — Provider types +- `packages/wolfharness/src/acp/schema/capabilities.py` — Capability flags +- `packages/wolfharness/src/acp/schema/notifications.py` — Notification types **Protocol Interfaces:** -- `packages/agentpool/src/acp/agent/protocol.py` — Agent-side protocol interface -- `packages/agentpool/src/acp/client/protocol.py` — Client-side protocol interface +- `packages/wolfharness/src/acp/agent/protocol.py` — Agent-side protocol interface +- `packages/wolfharness/src/acp/client/protocol.py` — Client-side protocol interface **Server Implementation:** -- `packages/agentpool/src/agentpool_server/acp_server/acp_agent.py` — ACP agent protocol implementation -- `packages/agentpool/src/agentpool_server/acp_server/provider_router.py` — Provider management -- `packages/agentpool/src/acp/bridge/bridge.py` — ACP bridge (stdio → HTTP) +- `packages/wolfharness/src/wolfharness_server/acp_server/acp_agent.py` — ACP agent protocol implementation +- `packages/wolfharness/src/wolfharness_server/acp_server/provider_router.py` — Provider management +- `packages/wolfharness/src/acp/bridge/bridge.py` — ACP bridge (stdio → HTTP) **ACP Spec Reference:** - `packages/agent-client-protocol/src/v1/` — Rust v1 stable types diff --git a/docs/records/bugs/BUG-001-run-stream-break-error.md b/docs/records/bugs/BUG-001-run-stream-break-error.md index 8ba21cb49..645f980d9 100644 --- a/docs/records/bugs/BUG-001-run-stream-break-error.md +++ b/docs/records/bugs/BUG-001-run-stream-break-error.md @@ -43,7 +43,7 @@ CancelledError: Cancelled via cancel scope ### Where the Bug Occurs -**File**: `/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool/src/agentpool/agents/native_agent/agent.py` +**File**: `/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src/wolfharness/agents/native_agent/agent.py` **Lines**: 829-845 @@ -88,13 +88,13 @@ agent.run_stream() ### Test Script Location -`/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool/tests/test_break_behavior.py` +`/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/tests/test_break_behavior.py` ### Minimal Reproduction ```python import asyncio -from agentpool import Agent +from wolfharness import Agent from pydantic_ai.models.test import TestModel async def test_break(): @@ -165,7 +165,7 @@ But this loses the ability to interrupt mid-execution. ### Deep Dive Areas -1. **merge_queue_into_iterator** (`/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool/src/agentpool/utils/streams.py`) +1. **merge_queue_into_iterator** (`/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src/wolfharness/utils/streams.py`) - How are tasks spawned? - How is cancellation propagated? - Can we make scope exit task-aware? @@ -186,10 +186,10 @@ But this loses the ability to interrupt mid-execution. | File | Lines | Purpose | |------|-------|---------| -| `agentpool/agents/native_agent/agent.py` | 808-895 | Native agent stream with GeneratorExit handling | -| `agentpool/agents/native_agent/agent.py` | 829-845 | merge_queue_into_iterator usage | -| `agentpool/utils/streams.py` | ~ | merge_queue_into_iterator implementation | -| `agentpool/agents/base_agent.py` | 566-648 | run_stream() top-level loop | +| `wolfharness/agents/native_agent/agent.py` | 808-895 | Native agent stream with GeneratorExit handling | +| `wolfharness/agents/native_agent/agent.py` | 829-845 | merge_queue_into_iterator usage | +| `wolfharness/utils/streams.py` | ~ | merge_queue_into_iterator implementation | +| `wolfharness/agents/base_agent.py` | 566-648 | run_stream() top-level loop | ### Key Code Segments @@ -263,12 +263,12 @@ The bug was fixed by isolating the entire `agentlet.iter()` iteration in a backg ### Changes Made -1. **`src/agentpool/utils/streams.py`**: Enhanced `merge_queue_into_iterator` with: +1. **`src/wolfharness/utils/streams.py`**: Enhanced `merge_queue_into_iterator` with: - `shutdown_event` for cooperative cancellation - `except GeneratorExit` handler to signal graceful shutdown - Task-aware cleanup that uses `asyncio.shield` to prevent CancelScope issues -2. **`src/agentpool/agents/native_agent/agent.py`**: Refactored `_stream_events()` to: +2. **`src/wolfharness/agents/native_agent/agent.py`**: Refactored `_stream_events()` to: - Run the entire `agentlet.iter()` iteration in a background task - Yield events via a queue from the consumer task - Properly signal cancellation and cleanup diff --git a/docs/reference/acp-meta-field-reference.md b/docs/reference/acp-meta-field-reference.md index ce8ad2d19..b6a112a6c 100644 --- a/docs/reference/acp-meta-field-reference.md +++ b/docs/reference/acp-meta-field-reference.md @@ -6,7 +6,7 @@ icon: material/file-document ## Overview -The Agent Client Protocol (ACP) includes a `_meta` field (called `field_meta` in agentpool's Python implementation) on all message types for extensibility. This field allows clients and agents to pass custom metadata that isn't part of the core protocol. +The Agent Client Protocol (ACP) includes a `_meta` field (called `field_meta` in wolfharness's Python implementation) on all message types for extensibility. This field allows clients and agents to pass custom metadata that isn't part of the core protocol. ## Protocol Specification diff --git a/docs/reference/api/agent.md b/docs/reference/api/agent.md index 27e82af5a..51ff520d3 100644 --- a/docs/reference/api/agent.md +++ b/docs/reference/api/agent.md @@ -4,4 +4,4 @@ icon: material/robot # Agent API Reference -::: agentpool.agents.native_agent.agent +::: wolfharness.agents.native_agent.agent diff --git a/docs/reference/api/agentpool.md b/docs/reference/api/agentpool.md index 73da4bfe4..f9f213f6b 100644 --- a/docs/reference/api/agentpool.md +++ b/docs/reference/api/agentpool.md @@ -4,4 +4,4 @@ icon: material/package # AgentPool API Reference -::: agentpool.delegation.pool.AgentPool +::: wolfharness.delegation.pool.AgentPool diff --git a/docs/reference/api/eventbus.md b/docs/reference/api/eventbus.md index 3515bc2b8..b4de2d483 100644 --- a/docs/reference/api/eventbus.md +++ b/docs/reference/api/eventbus.md @@ -6,8 +6,8 @@ icon: material/transit-connection-variant ## EventBus -::: agentpool.orchestrator.event_bus.EventBus +::: wolfharness.orchestrator.event_bus.EventBus ## SessionController -::: agentpool.orchestrator.session_controller.SessionController +::: wolfharness.orchestrator.session_controller.SessionController diff --git a/docs/reference/api/messagenode.md b/docs/reference/api/messagenode.md index 7df089db4..b0eaafe26 100644 --- a/docs/reference/api/messagenode.md +++ b/docs/reference/api/messagenode.md @@ -4,4 +4,4 @@ icon: material/message # MessageNode API Reference -::: agentpool.messaging.messagenode.MessageNode +::: wolfharness.messaging.messagenode.MessageNode diff --git a/docs/reference/cli/add.md b/docs/reference/cli/add.md index f34882526..983e6aed7 100644 --- a/docs/reference/cli/add.md +++ b/docs/reference/cli/add.md @@ -9,11 +9,11 @@ icon: material/plus Register a new agent configuration file. ```bash -agentpool add +wolfharness add ``` For a full list of options, run: ```bash -agentpool add --help +wolfharness add --help ``` diff --git a/docs/reference/cli/config.md b/docs/reference/cli/config.md index 2555c8a54..9f67a5bbb 100644 --- a/docs/reference/cli/config.md +++ b/docs/reference/cli/config.md @@ -10,9 +10,9 @@ The `config` command group helps you understand and manage AgentPool's layered c AgentPool automatically discovers and merges configuration from multiple sources: -1. **Global config** (`~/.config/agentpool/agentpool.yml`) +1. **Global config** (`~/.config/wolfharness/wolfharness.yml`) 2. **Custom config** (`AGENTPOOL_CONFIG` environment variable) -3. **Project config** (`agentpool.yml` in project/git root) +3. **Project config** (`wolfharness.yml` in project/git root) 4. **Explicit config** (CLI argument) These commands help you inspect which configs are being loaded and create new ones. @@ -23,13 +23,13 @@ The `config` command group includes the following commands: ```bash # Show current configuration -agentpool config show [config_path] +wolfharness config show [config_path] # Show config search paths -agentpool config paths +wolfharness config paths # Initialize a new configuration -agentpool config init [path] [--force] +wolfharness config init [path] [--force] ``` ### config show @@ -38,13 +38,13 @@ Display the current configuration, showing which config files are found and what ```bash # Show merged configuration -agentpool config show +wolfharness config show # Show with a specific explicit config -agentpool config show my-agents.yml +wolfharness config show my-agents.yml # Output as YAML -agentpool config show --format yaml +wolfharness config show --format yaml ``` ### config paths @@ -52,7 +52,7 @@ agentpool config show --format yaml Display the paths AgentPool searches for configuration files. ```bash -agentpool config paths +wolfharness config paths ``` ### config init @@ -61,16 +61,16 @@ Create a new configuration file. ```bash # Create a starter config in current project -agentpool config init +wolfharness config init # Create global config for user-wide preferences -agentpool config init global +wolfharness config init global # Create at a specific path -agentpool config init ./configs/my-agents.yml +wolfharness config init ./configs/my-agents.yml # Overwrite existing config -agentpool config init --force +wolfharness config init --force ``` ## Examples @@ -79,36 +79,36 @@ agentpool config init --force ```bash # Show which config files are found and what they contain -agentpool config show +wolfharness config show # Show with a specific explicit config included -agentpool config show my-agents.yml +wolfharness config show my-agents.yml # Output as YAML for scripting -agentpool config show --format yaml +wolfharness config show --format yaml ``` ### View Config Paths ```bash # Show where AgentPool looks for config files -agentpool config paths +wolfharness config paths ``` ### Create New Config ```bash # Create a starter config in current project -agentpool config init +wolfharness config init # Create a global config for user-wide preferences -agentpool config init global +wolfharness config init global # Create at a specific path -agentpool config init ./configs/my-agents.yml +wolfharness config init ./configs/my-agents.yml # Overwrite existing config -agentpool config init --force +wolfharness config init --force ``` ## Use Cases @@ -118,10 +118,10 @@ agentpool config init --force Create a global config to set preferences that apply to all projects: ```bash -agentpool config init global +wolfharness config init global ``` -Then edit `~/.config/agentpool/agentpool.yml`: +Then edit `~/.config/wolfharness/wolfharness.yml`: ```yaml # Global preferences @@ -135,7 +135,7 @@ model_variants: storage: provider: sql - database_url: sqlite:///~/.local/share/agentpool/history.db + database_url: sqlite:///~/.local/share/wolfharness/history.db ``` ### Project-Specific Agents @@ -143,10 +143,10 @@ storage: Create a project config that inherits global settings: ```bash -agentpool config init +wolfharness config init ``` -Then edit `./agentpool.yml`: +Then edit `./wolfharness.yml`: ```yaml agents: @@ -163,7 +163,7 @@ agents: If your agent isn't behaving as expected, check which configs are being loaded: ```bash -agentpool config show +wolfharness config show ``` This shows: diff --git a/docs/reference/cli/history.md b/docs/reference/cli/history.md index ba9aa9e19..dcbd209a9 100644 --- a/docs/reference/cli/history.md +++ b/docs/reference/cli/history.md @@ -9,11 +9,11 @@ icon: material/history View and analyze agent interaction history. ```bash -agentpool history stats --group-by model +wolfharness history stats --group-by model ``` For a full list of options, run: ```bash -agentpool history --help +wolfharness history --help ``` diff --git a/docs/reference/cli/index.md b/docs/reference/cli/index.md index 177d2b6b1..ec6ae2b87 100644 --- a/docs/reference/cli/index.md +++ b/docs/reference/cli/index.md @@ -14,9 +14,9 @@ This avoids the need to pass the config file path each time you want to run a co The CLI maintains an "active agent file" setting which determines which agents are available for commands like `run`, `task`, or `watch`. You can: -- Add agent files with `agentpool add ` -- Set the active file with `agentpool set ` -- List agents from the active config with `agentpool list` +- Add agent files with `wolfharness add ` +- Set the active file with `wolfharness set ` +- List agents from the active config with `wolfharness list` Most commands will use the currently active agent file by default, but can be overridden with the `--config` option. @@ -68,14 +68,14 @@ Most commands will use the currently active agent file by default, but can be ov ### Option 1: Automatic Config Discovery -AgentPool automatically discovers config files. Just create `agentpool.yml` in your project: +AgentPool automatically discovers config files. Just create `wolfharness.yml` in your project: ```bash # Create a starter config -agentpool config init +wolfharness config init # Edit the config, then run -agentpool run myagent "Hello" +wolfharness run myagent "Hello" ``` ### Option 2: Named Configurations @@ -85,20 +85,20 @@ For managing multiple config files: 1. Add and activate an agent configuration: ```bash - agentpool add myconfig agents.yml - agentpool set myconfig + wolfharness add myconfig agents.yml + wolfharness set myconfig ``` 2. List available agents: ```bash - agentpool list + wolfharness list ``` 3. Run a prompt with an agent: ```bash - agentpool run analyzer "Analyze this text" + wolfharness run analyzer "Analyze this text" ``` ## Command Examples @@ -107,61 +107,61 @@ For managing multiple config files: ```bash # Run a single agent with a prompt -agentpool run myagent "Analyze this" +wolfharness run myagent "Analyze this" # Run a team -agentpool run myteam "Process this" +wolfharness run myteam "Process this" # Show detailed output with costs -agentpool run myagent "Hello" --detail full --costs +wolfharness run myagent "Hello" --detail full --costs ``` ### Executing Tasks ```bash # Execute a defined task -agentpool task docs write_api_docs +wolfharness task docs write_api_docs # Execute with additional prompt -agentpool task docs write_api_docs --prompt "Include code examples" +wolfharness task docs write_api_docs --prompt "Include code examples" ``` ### Server Commands ```bash # ACP server for IDE integration (Zed, Toad) -agentpool serve-acp config.yml +wolfharness serve-acp config.yml # OpenCode server for OpenCode TUI/Desktop -agentpool serve-opencode config.yml --port 4096 +wolfharness serve-opencode config.yml --port 4096 # MCP server (stdio transport) -agentpool serve-mcp config.yml +wolfharness serve-mcp config.yml # MCP server with SSE transport -agentpool serve-mcp config.yml --transport sse --port 3001 +wolfharness serve-mcp config.yml --transport sse --port 3001 # AG-UI server -agentpool serve-agui config.yml --port 8002 +wolfharness serve-agui config.yml --port 8002 # OpenAI-compatible API server -agentpool serve-api config.yml --port 8000 +wolfharness serve-api config.yml --port 8000 ``` ### History Commands ```bash # Show last 5 conversations -agentpool history show -n 5 +wolfharness history show -n 5 # Show conversations from last 24 hours -agentpool history show --period 24h +wolfharness history show --period 24h # Show stats grouped by model -agentpool history stats --period 1w --group-by model +wolfharness history stats --period 1w --group-by model # Clear history for specific agent -agentpool history reset --agent myagent +wolfharness history reset --agent myagent ``` ## Global Options diff --git a/docs/reference/cli/list.md b/docs/reference/cli/list.md index 1d03a7946..e1ceb81ef 100644 --- a/docs/reference/cli/list.md +++ b/docs/reference/cli/list.md @@ -9,11 +9,11 @@ icon: material/format-list-bulleted Show available agents from the configuration. ```bash -agentpool list +wolfharness list ``` For a full list of options, run: ```bash -agentpool list --help +wolfharness list --help ``` diff --git a/docs/reference/cli/run.md b/docs/reference/cli/run.md index 180f68501..ffdb8289b 100644 --- a/docs/reference/cli/run.md +++ b/docs/reference/cli/run.md @@ -6,10 +6,10 @@ icon: material/play # run -Run a node with prompts using the `agentpool run` command. +Run a node with prompts using the `wolfharness run` command. ```bash -agentpool run "prompt text" +wolfharness run "prompt text" ``` The `run` command executes a single prompt against a configured agent. @@ -18,17 +18,17 @@ The `run` command executes a single prompt against a configured agent. ```bash # Simple run -agentpool run assistant "Hello!" +wolfharness run assistant "Hello!" # With streaming output -agentpool run assistant "Tell me a story" --stream +wolfharness run assistant "Tell me a story" --stream # With explicit config file -agentpool run assistant "Hello!" --config my-agents.yml +wolfharness run assistant "Hello!" --config my-agents.yml ``` For a full list of options, run: ```bash -agentpool run --help +wolfharness run --help ``` \ No newline at end of file diff --git a/docs/reference/cli/serve-acp.md b/docs/reference/cli/serve-acp.md index b1ab821d0..2a307235a 100644 --- a/docs/reference/cli/serve-acp.md +++ b/docs/reference/cli/serve-acp.md @@ -9,21 +9,21 @@ icon: material/server Start the Agent Communication Protocol (ACP) server to expose agents to ACP-compatible clients. ```bash -agentpool serve-acp config.yml +wolfharness serve-acp config.yml ``` For basic usage: ```bash # Start with default config -agentpool serve-acp agents.yml +wolfharness serve-acp agents.yml # Listen on specific host and port -agentpool serve-acp agents.yml --host 0.0.0.0 --port 8321 +wolfharness serve-acp agents.yml --host 0.0.0.0 --port 8321 ``` For a full list of options, run: ```bash -agentpool serve-acp --help +wolfharness serve-acp --help ``` diff --git a/docs/reference/cli/serve-agui.md b/docs/reference/cli/serve-agui.md index daee1f82d..27a4b36d3 100644 --- a/docs/reference/cli/serve-agui.md +++ b/docs/reference/cli/serve-agui.md @@ -9,11 +9,11 @@ icon: material/monitor Start the AG-UI protocol server to expose agents to AG-UI compatible frontends. ```bash -agentpool serve-agui config.yml +wolfharness serve-agui config.yml ``` For a full list of options, run: ```bash -agentpool serve-agui --help +wolfharness serve-agui --help ``` diff --git a/docs/reference/cli/serve-api.md b/docs/reference/cli/serve-api.md index 8688a59ba..63a5a0ebd 100644 --- a/docs/reference/cli/serve-api.md +++ b/docs/reference/cli/serve-api.md @@ -9,11 +9,11 @@ icon: material/api Start an OpenAI-compatible API server that exposes agents through a drop-in OpenAI API replacement. ```bash -agentpool serve-api config.yml +wolfharness serve-api config.yml ``` For a full list of options, run: ```bash -agentpool serve-api --help +wolfharness serve-api --help ``` diff --git a/docs/reference/cli/serve-mcp.md b/docs/reference/cli/serve-mcp.md index dbb8c7228..09f5d0d51 100644 --- a/docs/reference/cli/serve-mcp.md +++ b/docs/reference/cli/serve-mcp.md @@ -9,11 +9,11 @@ icon: material/tools Start the MCP (Model Context Protocol) server to expose agent tools to other MCP-compatible clients. ```bash -agentpool serve-mcp config.yml +wolfharness serve-mcp config.yml ``` For a full list of options, run: ```bash -agentpool serve-mcp --help +wolfharness serve-mcp --help ``` diff --git a/docs/reference/cli/serve-opencode.md b/docs/reference/cli/serve-opencode.md index 082271e81..34a9b7022 100644 --- a/docs/reference/cli/serve-opencode.md +++ b/docs/reference/cli/serve-opencode.md @@ -9,11 +9,11 @@ icon: material/code-braces Start the OpenCode server to expose agents to the OpenCode TUI/Desktop interface. ```bash -agentpool serve-opencode config.yml +wolfharness serve-opencode config.yml ``` For a full list of options, run: ```bash -agentpool serve-opencode --help +wolfharness serve-opencode --help ``` diff --git a/docs/reference/cli/set.md b/docs/reference/cli/set.md index 4c23d99d7..a51ca2695 100644 --- a/docs/reference/cli/set.md +++ b/docs/reference/cli/set.md @@ -9,11 +9,11 @@ icon: material/file-cog Set the active configuration file. ```bash -agentpool set +wolfharness set ``` For a full list of options, run: ```bash -agentpool set --help +wolfharness set --help ``` diff --git a/docs/reference/cli/task.md b/docs/reference/cli/task.md index f4646413b..ba9d9b669 100644 --- a/docs/reference/cli/task.md +++ b/docs/reference/cli/task.md @@ -9,11 +9,11 @@ icon: material/clipboard-check Execute a defined task from the configuration. ```bash -agentpool task +wolfharness task ``` For a full list of options, run: ```bash -agentpool task --help +wolfharness task --help ``` diff --git a/docs/reference/cli/watch.md b/docs/reference/cli/watch.md index 8841db93a..993231345 100644 --- a/docs/reference/cli/watch.md +++ b/docs/reference/cli/watch.md @@ -9,11 +9,11 @@ icon: material/eye Watch for triggers and react automatically based on configured event sources. ```bash -agentpool watch --config agents.yml +wolfharness watch --config agents.yml ``` For a full list of options, run: ```bash -agentpool watch --help +wolfharness watch --help ``` diff --git a/docs/reference/core-concepts/agent-pool.md b/docs/reference/core-concepts/agent-pool.md index 07c1894a3..ee7899dfa 100644 --- a/docs/reference/core-concepts/agent-pool.md +++ b/docs/reference/core-concepts/agent-pool.md @@ -35,7 +35,7 @@ agents: ``` ```python -from agentpool import AgentPool +from wolfharness import AgentPool from myapp.config import AppConfig # Your dependency type async def main(): diff --git a/docs/reference/core-concepts/basic-agent.md b/docs/reference/core-concepts/basic-agent.md index 950222c0b..d43e6d68a 100644 --- a/docs/reference/core-concepts/basic-agent.md +++ b/docs/reference/core-concepts/basic-agent.md @@ -80,7 +80,7 @@ await agent.run_in_background(get_prompt) ## Agents with output types ```python -from agentpool import Agent +from wolfharness import Agent from pydantic import BaseModel class AnalysisResult(BaseModel): diff --git a/docs/reference/core-concepts/events.md b/docs/reference/core-concepts/events.md index 42f9f6a2a..6df091ceb 100644 --- a/docs/reference/core-concepts/events.md +++ b/docs/reference/core-concepts/events.md @@ -179,10 +179,10 @@ Run agents in event-watching mode to handle events: ```bash # Start watching with default configuration -agentpool watch agents.yml +wolfharness watch agents.yml # With specific log level -agentpool watch agents.yml --log-level DEBUG +wolfharness watch agents.yml --log-level DEBUG ``` The agents will: diff --git a/docs/reference/core-concepts/mcp.md b/docs/reference/core-concepts/mcp.md index 040844aac..7d28f6e5e 100644 --- a/docs/reference/core-concepts/mcp.md +++ b/docs/reference/core-concepts/mcp.md @@ -38,7 +38,7 @@ agents: ### In Agent Constructor ```python -from agentpool import Agent, StdioMCPServerConfig +from wolfharness import Agent, StdioMCPServerConfig # Create agent with MCP servers agent = Agent( diff --git a/docs/reference/js/run_code_config.js b/docs/reference/js/run_code_config.js index a194b3f83..9df840bc2 100644 --- a/docs/reference/js/run_code_config.js +++ b/docs/reference/js/run_code_config.js @@ -1 +1 @@ -window.mkdocs_run_deps = ["agentpool"]; +window.mkdocs_run_deps = ["wolfharness"]; diff --git a/docs/rfcs/RFC-0034-background-task-redesign.md b/docs/rfcs/RFC-0034-background-task-redesign.md index 5bd490346..30ff03ff2 100644 --- a/docs/rfcs/RFC-0034-background-task-redesign.md +++ b/docs/rfcs/RFC-0034-background-task-redesign.md @@ -44,7 +44,7 @@ related_rfcs: ### 1.1 Summary -This RFC proposes a fundamental redesign of the BackgroundTask system, migrating it from the xeno-agent business layer into agentpool as a first-class core infrastructure, while preserving xeno-agent-specific provider customizations. The redesign replaces the legacy string-based task ID API with a structured `TaskHandle` object pattern aligned with MCP SEP-1686, introduces configurable session-end lifecycle policies, and hard-cuts over from the old API without a compatibility layer. +This RFC proposes a fundamental redesign of the BackgroundTask system, migrating it from the xeno-agent business layer into wolfharness as a first-class core infrastructure, while preserving xeno-agent-specific provider customizations. The redesign replaces the legacy string-based task ID API with a structured `TaskHandle` object pattern aligned with MCP SEP-1686, introduces configurable session-end lifecycle policies, and hard-cuts over from the old API without a compatibility layer. ### 1.2 Why This Matters Now @@ -53,14 +53,14 @@ The current BackgroundTask implementation lives entirely in `xeno-agent` and suf 1. **Fire-and-forget bug**: The `_notify_parent` callback uses `asyncio.create_task()` without `cancel_and_drain`, causing exceptions to be silently discarded when the parent session has ended. 2. **`parent_session_id` empty string bug**: Prevents auto-resume of background task completion notifications. 3. **String ID API**: The legacy API returns raw strings (`task_id`) with no structured handle, making it impossible to chain operations or attach metadata. -4. **No upstream standard**: Each downstream project reinvents background task patterns; agentpool lacks a unified abstraction. +4. **No upstream standard**: Each downstream project reinvents background task patterns; wolfharness lacks a unified abstraction. These issues block reliable multi-agent delegation workflows and create support burden. ### 1.3 Expected Outcome After implementation: -- **Unified infrastructure**: `BackgroundTaskManager` and `TaskHandle` are core agentpool primitives. +- **Unified infrastructure**: `BackgroundTaskManager` and `TaskHandle` are core wolfharness primitives. - **Structured API**: All background task operations return `TaskHandle` objects with typed methods (`status`, `result()`, `cancel()`). - **Session lifecycle integration**: Configurable `session_end_policy` controls what happens to background tasks when their parent session ends. - **SEP-1686 alignment**: API surface is compatible with MCP task semantics, enabling future protocol-native task support. @@ -78,7 +78,7 @@ The BackgroundTask system currently consists of three layers: |-------|----------|---------------| | **Core types** | `xeno_agent/task/types.py` | `BackgroundTask` dataclass, `TaskHandle` dataclass, `TaskStatus` literal | | **Lifecycle manager** | `xeno_agent/task/manager.py` | `BackgroundTaskManager` — semaphore, timeout, cancellation, cleanup | -| **Provider integration** | `xeno_agent/agentpool/resource_providers/background_task_provider.py` | Tool definitions (`task`, `background_output`, `background_cancel`) | +| **Provider integration** | `xeno_agent/wolfharness/resource_providers/background_task_provider.py` | Tool definitions (`task`, `background_output`, `background_cancel`) | The existing `BackgroundTaskProvider` exposes three tools: @@ -104,7 +104,7 @@ Key problems with this design: ### 2.2 Historical Context -The BackgroundTask system was initially designed as a xeno-agent-specific augmentation to agentpool's `subagent_tools.py`. The original RFC (RFC-0001-v2) explicitly stated: "xeno-agent layer优先:先在业务层验证,成熟后考虑 upstream 到 agentpool." After 3 months of production validation with 130+ tests, the pattern has proven stable enough to upstream. +The BackgroundTask system was initially designed as a xeno-agent-specific augmentation to wolfharness's `subagent_tools.py`. The original RFC (RFC-0001-v2) explicitly stated: "xeno-agent layer优先:先在业务层验证,成熟后考虑 upstream 到 wolfharness." After 3 months of production validation with 130+ tests, the pattern has proven stable enough to upstream. ### 2.3 Glossary @@ -113,7 +113,7 @@ The BackgroundTask system was initially designed as a xeno-agent-specific augmen | **BackgroundTask** | Serializable dataclass representing a task's metadata and lifecycle state | | **TaskHandle** | Runtime object wrapping an `asyncio.Task` with structured query/cancel methods | | **TaskStatus** | Literal union: `pending`, `running`, `cancelling`, `completed`, `error`, `cancelled`, `timed_out` | -| **SessionPool** | agentpool's session orchestration layer (`SessionPool` + `SessionController`) | +| **SessionPool** | wolfharness's session orchestration layer (`SessionPool` + `SessionController`) | | **session_end_policy** | Configuration controlling background task behavior on parent session end | | **SEP-1686** | MCP Specification Enhancement Proposal for asynchronous task tools | | **fire-and-forget** | Pattern of spawning async work without awaiting or cleanup; explicitly avoided | @@ -123,7 +123,7 @@ The BackgroundTask system was initially designed as a xeno-agent-specific augmen - **RFC-0021**: Introduced `AgentRunContext` for per-call state isolation; BackgroundTask redesign must respect per-run context boundaries. - **RFC-0026**: Per-session agent isolation ensures background tasks do not share `MessageHistory` across concurrent subagents. -- **RFC-0001 (agentpool)**: Workers and Teams session management establishes `SpawnSessionStart` patterns that background tasks must emit consistently. +- **RFC-0001 (wolfharness)**: Workers and Teams session management establishes `SpawnSessionStart` patterns that background tasks must emit consistently. --- @@ -137,7 +137,7 @@ The BackgroundTask system was initially designed as a xeno-agent-specific augmen 3. **Empty `parent_session_id`**: Line 379 of `background_task_provider.py` falls back to `getattr(ctx.node, 'session_id', '')` — an empty string prevents `SessionPool.inject_prompt()` from routing the notification correctly. -4. **No upstream abstraction**: Every downstream project that wants background tasks must copy xeno-agent's implementation. There is no agentpool-native primitive. +4. **No upstream abstraction**: Every downstream project that wants background tasks must copy xeno-agent's implementation. There is no wolfharness-native primitive. 5. **String-based API**: Returning raw string IDs makes it impossible for the LLM to introspect task state without making another tool call. @@ -163,7 +163,7 @@ The BackgroundTask system was initially designed as a xeno-agent-specific augmen ### 4.1 Goals (In Scope) -1. **Upstream core infrastructure**: `BackgroundTaskManager`, `TaskHandle`, and `TaskStatus` become agentpool core primitives. +1. **Upstream core infrastructure**: `BackgroundTaskManager`, `TaskHandle`, and `TaskStatus` become wolfharness core primitives. 2. **Structured API**: Replace string-ID returns with `TaskHandle` objects exposing `.status`, `.result()`, `.cancel()`. 3. **Session lifecycle integration**: Introduce `session_end_policy` with `cancel`, `keep`, and `notify` strategies. 4. **SEP-1686 alignment**: API signatures and semantics match MCP task tool patterns. @@ -174,16 +174,16 @@ The BackgroundTask system was initially designed as a xeno-agent-specific augmen 1. **Not**: Implementing MCP server-side task support (SEP-1686 server changes are a separate effort). 2. **Not**: Persistent task storage across process restarts. -3. **Not**: Distributed task execution across multiple agentpool instances. +3. **Not**: Distributed task execution across multiple wolfharness instances. 4. **Not**: Changing the underlying LLM provider concurrency model. 5. **Not**: Adding new event types beyond what already exists (`SpawnSessionStart`, `SubAgentEvent`, `StreamCompleteEvent`). ### 4.3 Success Criteria -- [ ] `TaskHandle` is importable from `agentpool.tasks`. -- [ ] `BackgroundTaskManager` is importable from `agentpool.tasks`. +- [ ] `TaskHandle` is importable from `wolfharness.tasks`. +- [ ] `BackgroundTaskManager` is importable from `wolfharness.tasks`. - [ ] `session_end_policy` is configurable per `AgentPool` or per session. -- [ ] xeno-agent's `BackgroundTaskProvider` delegates to agentpool core. +- [ ] xeno-agent's `BackgroundTaskProvider` delegates to wolfharness core. - [ ] All 124 xeno-agent background task tests pass with new API. - [ ] No `asyncio.create_task()` without matching `cancel_and_drain` pattern. - [ ] Pydantic-AI `cancel_and_drain` is used in all cleanup paths. @@ -209,15 +209,15 @@ This section documents the four confirmed design decisions that govern the archi ### Q1: Where does the BackgroundTask infrastructure live? -**Decision: C — Hybrid approach: agentpool core infrastructure + xeno-agent specific Provider** +**Decision: C — Hybrid approach: wolfharness core infrastructure + xeno-agent specific Provider** **Rationale**: -The core lifecycle manager (`BackgroundTaskManager`), types (`TaskHandle`, `BackgroundTask`), and session integration belong in agentpool so all downstream projects benefit. However, the specific tool schemas, prompt formatting, and xeno-agent-specific behaviors (e.g., `load_skills` injection, XML prompt formatting) remain in xeno-agent's `BackgroundTaskProvider`. +The core lifecycle manager (`BackgroundTaskManager`), types (`TaskHandle`, `BackgroundTask`), and session integration belong in wolfharness so all downstream projects benefit. However, the specific tool schemas, prompt formatting, and xeno-agent-specific behaviors (e.g., `load_skills` injection, XML prompt formatting) remain in xeno-agent's `BackgroundTaskProvider`. **Scope split**: -| Component | agentpool (core) | xeno-agent (provider) | +| Component | wolfharness (core) | xeno-agent (provider) | |-----------|-----------------|----------------------| | `TaskHandle` | ✅ | ❌ | | `BackgroundTask` dataclass | ✅ | ❌ | @@ -233,8 +233,8 @@ The core lifecycle manager (`BackgroundTaskManager`), types (`TaskHandle`, `Back **Trade-offs**: - **Pro**: Downstream projects (xeno-rag, xeno-serve) can reuse core infrastructure. -- **Pro**: agentpool can integrate background tasks with `SessionPool` natively. -- **Con**: Slightly more complex import graph; xeno-agent depends on agentpool tasks. +- **Pro**: wolfharness can integrate background tasks with `SessionPool` natively. +- **Con**: Slightly more complex import graph; xeno-agent depends on wolfharness tasks. - **Con**: Core changes require coordinated releases. ### Q2: What is the API pattern for task handles? @@ -255,7 +255,7 @@ status = await background_output(task_id="bg_abc123", block=False) **New API** (SEP-1686 style): ```python -from agentpool.tasks import TaskHandle +from wolfharness.tasks import TaskHandle # Returns a structured handle handle: TaskHandle = await run_background_task(agent_mode="expert", prompt="analyze") @@ -292,7 +292,7 @@ await handle.cancel() **Rationale**: -A single hardcoded behavior cannot satisfy all use cases. Industrial diagnostics (`xeno-agent`) often needs `notify` so the fault expert receives completion prompts. Batch processing jobs need `keep`. Default agentpool behavior should be `cancel` for safety. +A single hardcoded behavior cannot satisfy all use cases. Industrial diagnostics (`xeno-agent`) often needs `notify` so the fault expert receives completion prompts. Batch processing jobs need `keep`. Default wolfharness behavior should be `cancel` for safety. **Implementation hook**: @@ -327,7 +327,7 @@ for task in task_manager.get_tasks_by_session(session_id): **Migration scope**: - `xeno-agent` tests: 124 test cases updated. -- `xeno-agent` provider: `BackgroundTaskProvider` rewritten to delegate to agentpool core. +- `xeno-agent` provider: `BackgroundTaskProvider` rewritten to delegate to wolfharness core. - `diag-agent.yaml`: Tool schema references updated. **Trade-offs**: @@ -376,10 +376,10 @@ for task in task_manager.get_tasks_by_session(session_id): ### 7.2 Component Design -#### 7.2.1 `agentpool.tasks` Module (New) +#### 7.2.1 `wolfharness.tasks` Module (New) ```python -# agentpool/tasks/__init__.py +# wolfharness/tasks/__init__.py from __future__ import annotations from dataclasses import dataclass, field @@ -458,10 +458,10 @@ class TaskHandle: ... ``` -#### 7.2.2 `BackgroundTaskManager` (agentpool core) +#### 7.2.2 `BackgroundTaskManager` (wolfharness core) ```python -# agentpool/tasks/manager.py +# wolfharness/tasks/manager.py from __future__ import annotations import asyncio @@ -476,7 +476,7 @@ from .types import BackgroundTask, TaskHandle, TaskStatus, TERMINAL_STATES class BackgroundTaskManager: """Manages background task lifecycle, concurrency, timeout, and cleanup. - This is the agentpool-core equivalent of xeno-agent's + This is the wolfharness-core equivalent of xeno-agent's ``BackgroundTaskManager``, with these key differences: 1. Uses ``cancel_and_drain`` from pydantic-ai for all cleanup paths. @@ -576,10 +576,10 @@ def create_task(..., parent_session_id: str | None = None, ...) -> TaskHandle: ## 8. API Design -### 8.1 agentpool Core API +### 8.1 wolfharness Core API ```python -# agentpool/tasks/__init__.py +# wolfharness/tasks/__init__.py from dataclasses import dataclass from datetime import datetime @@ -644,7 +644,7 @@ class BackgroundTaskManager: ### 8.2 xeno-agent Provider API (New Tools) ```python -# xeno_agent/agentpool/resource_providers/background_task_provider.py +# xeno_agent/wolfharness/resource_providers/background_task_provider.py async def run_background_task( ctx: AgentContext, @@ -704,7 +704,7 @@ agents: session_end_policy: notify # cancel | keep | notify tools: - type: custom - import_path: xeno_agent.agentpool.resource_providers.background_task_provider.XenoBackgroundTaskProvider + import_path: xeno_agent.wolfharness.resource_providers.background_task_provider.XenoBackgroundTaskProvider enabled_tools: - run_background_task - task_status @@ -721,9 +721,9 @@ agents: The `SessionController.close_session()` method is the hook point for policy enforcement: ```python -# agentpool/sessions/controller.py +# wolfharness/sessions/controller.py -from agentpool.tasks import BackgroundTaskManager +from wolfharness.tasks import BackgroundTaskManager class SessionController: def __init__(self, ..., task_manager: BackgroundTaskManager): @@ -859,9 +859,9 @@ status = handle.status # Query current status await handle.cancel() # Request cancellation ``` -### 11.2 agentpool-to-SEP-1686 Mapping +### 11.2 wolfharness-to-SEP-1686 Mapping -| SEP-1686 Concept | agentpool Equivalent | Notes | +| SEP-1686 Concept | wolfharness Equivalent | Notes | |-----------------|----------------------|-------| | `@tool(task=True)` | `run_background_task()` tool | Explicit tool, not decorator | | `TaskHandle.result()` | `TaskHandle.result()` | Direct equivalent | @@ -871,11 +871,11 @@ await handle.cancel() # Request cancellation ### 11.3 Future MCP Server Integration -When agentpool implements an MCP server with SEP-1686 support: +When wolfharness implements an MCP server with SEP-1686 support: ```python -# Future: agentpool MCP server -from agentpool.tasks import BackgroundTaskManager +# Future: wolfharness MCP server +from wolfharness.tasks import BackgroundTaskManager class AgentPoolMCPServer: def __init__(self, task_manager: BackgroundTaskManager): @@ -913,8 +913,8 @@ The following test files in `xeno-agent` must be updated: ### 12.2 Migration Checklist -- [ ] **Phase 0**: Create agentpool `tasks` module with `TaskHandle`, `BackgroundTask`, `BackgroundTaskManager`. -- [ ] **Phase 0**: Port `BackgroundTaskManager` from xeno-agent to agentpool, adding `cancel_and_drain` compliance. +- [ ] **Phase 0**: Create wolfharness `tasks` module with `TaskHandle`, `BackgroundTask`, `BackgroundTaskManager`. +- [ ] **Phase 0**: Port `BackgroundTaskManager` from xeno-agent to wolfharness, adding `cancel_and_drain` compliance. - [ ] **Phase 1**: Add `session_end_policy` to `SessionConfig` and enforcement in `SessionController`. - [ ] **Phase 2**: Rewrite xeno-agent `BackgroundTaskProvider`: - [ ] Replace `task()` with `run_background_task()` returning `TaskHandle`. @@ -946,20 +946,20 @@ The following test files in `xeno-agent` must be updated: ## 13. Implementation Plan -### Phase 0: agentpool Core (Week 1) +### Phase 0: wolfharness Core (Week 1) **Deliverables**: -- `agentpool/tasks/types.py` — `TaskStatus`, `BackgroundTask`, `TaskHandle` -- `agentpool/tasks/manager.py` — `BackgroundTaskManager` with `cancel_and_drain` -- `agentpool/tasks/__init__.py` — Public exports +- `wolfharness/tasks/types.py` — `TaskStatus`, `BackgroundTask`, `TaskHandle` +- `wolfharness/tasks/manager.py` — `BackgroundTaskManager` with `cancel_and_drain` +- `wolfharness/tasks/__init__.py` — Public exports - Unit tests for core manager (semaphore, timeout, cancellation, cleanup) **Files Modified**: -- `src/agentpool/tasks/` (new directory) -- `src/agentpool/sessions/controller.py` (session_end_policy hook) -- `src/agentpool/sessions/config.py` (session_end_policy field) +- `src/wolfharness/tasks/` (new directory) +- `src/wolfharness/sessions/controller.py` (session_end_policy hook) +- `src/wolfharness/sessions/config.py` (session_end_policy field) -**Rollback**: Delete `src/agentpool/tasks/` directory. +**Rollback**: Delete `src/wolfharness/tasks/` directory. ### Phase 1: Session Integration (Week 1-2) @@ -969,9 +969,9 @@ The following test files in `xeno-agent` must be updated: - `BackgroundTaskManager` session association methods **Files Modified**: -- `src/agentpool/sessions/config.py` -- `src/agentpool/sessions/controller.py` -- `src/agentpool/tasks/manager.py` +- `src/wolfharness/sessions/config.py` +- `src/wolfharness/sessions/controller.py` +- `src/wolfharness/tasks/manager.py` **Rollback**: Revert session controller changes. @@ -983,7 +983,7 @@ The following test files in `xeno-agent` must be updated: - Empty string `parent_session_id` fix **Files Modified**: -- `src/xeno_agent/agentpool/resource_providers/background_task_provider.py` +- `src/xeno_agent/wolfharness/resource_providers/background_task_provider.py` **Rollback**: Restore from git. @@ -995,14 +995,14 @@ The following test files in `xeno-agent` must be updated: - New tests for `TaskHandle` methods **Files Modified**: -- `tests/agentpool/resource_providers/test_background_task_*.py` +- `tests/wolfharness/resource_providers/test_background_task_*.py` **Rollback**: Restore from git. ### Dependencies -- agentpool Phase 0 must complete before xeno-agent Phase 2. -- agentpool Phase 1 must complete before xeno-agent test additions for `session_end_policy`. +- wolfharness Phase 0 must complete before xeno-agent Phase 2. +- wolfharness Phase 1 must complete before xeno-agent test additions for `session_end_policy`. --- @@ -1028,7 +1028,7 @@ The following test files in `xeno-agent` must be updated: - Owner: Performance - Status: Open -5. **Should agentpool expose a `@background_task` decorator for tool functions?** +5. **Should wolfharness expose a `@background_task` decorator for tool functions?** - Context: Would allow declarative background task registration. - Owner: API design - Status: Open @@ -1054,7 +1054,7 @@ The following test files in `xeno-agent` must be updated: ### Key Discussion Points -1. **Why not Option A (all in agentpool)?** Would force xeno-agent's XML formatting and skill-loading into core, creating inappropriate coupling. +1. **Why not Option A (all in wolfharness)?** Would force xeno-agent's XML formatting and skill-loading into core, creating inappropriate coupling. 2. **Why not Option B (all in xeno-agent)?** Defeats the purpose of upstreaming proven infrastructure. 3. **Why not keep string IDs?** String parsing is error-prone and incompatible with MCP SEP-1686. 4. **Why not a compatibility layer?** Doubles maintenance burden for a single-consumer API. @@ -1062,11 +1062,11 @@ The following test files in `xeno-agent` must be updated: ### Conditions on Approval - [ ] At least 2 code reviewers approve -- [ ] agentpool core tests demonstrate 100% manager coverage +- [ ] wolfharness core tests demonstrate 100% manager coverage - [ ] All 124 xeno-agent tests pass with new API - [ ] Session-end policy tests cover all 3 variants - [ ] No `asyncio.create_task()` without matching cleanup in changed code -- [ ] Documentation updated (agentpool tasks module, xeno-agent migration guide) +- [ ] Documentation updated (wolfharness tasks module, xeno-agent migration guide) --- @@ -1077,11 +1077,11 @@ The following test files in `xeno-agent` must be updated: 1. **RFC-0001-v2 (xeno-agent)** — Original background task RFC Location: `packages/xeno-agent/docs/rfcs/RFC-0001-async-task-background-task-v2.md` -2. **RFC-0021 (agentpool)** — Agent Concurrent Execution Safety - Location: `packages/agentpool/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md` +2. **RFC-0021 (wolfharness)** — Agent Concurrent Execution Safety + Location: `packages/wolfharness/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md` 3. **BackgroundTaskProvider Implementation** - Location: `packages/xeno-agent/src/xeno_agent/agentpool/resource_providers/background_task_provider.py` + Location: `packages/xeno-agent/src/xeno_agent/wolfharness/resource_providers/background_task_provider.py` 4. **Research Findings** Location: `.omo/notepads/background-task-provider/learnings.md` @@ -1101,10 +1101,10 @@ The following test files in `xeno-agent` must be updated: |-----------|------| | xeno-agent BackgroundTaskManager | `packages/xeno-agent/src/xeno_agent/task/manager.py` | | xeno-agent BackgroundTask types | `packages/xeno-agent/src/xeno_agent/task/types.py` | -| xeno-agent BackgroundTaskProvider | `packages/xeno-agent/src/xeno_agent/agentpool/resource_providers/background_task_provider.py` | +| xeno-agent BackgroundTaskProvider | `packages/xeno-agent/src/xeno_agent/wolfharness/resource_providers/background_task_provider.py` | | Pydantic-AI cancel_and_drain | `packages/pydantic-ai/pydantic_ai_slim/pydantic_ai/_utils.py:223-240` | -| AgentPool SessionController | `packages/agentpool/src/agentpool/sessions/controller.py` | -| AgentPool SessionConfig | `packages/agentpool/src/agentpool/sessions/config.py` | +| AgentPool SessionController | `packages/wolfharness/src/wolfharness/sessions/controller.py` | +| AgentPool SessionConfig | `packages/wolfharness/src/wolfharness/sessions/config.py` | --- diff --git a/docs/rfcs/accepted/RFC-0001-unified-run-tracking.md b/docs/rfcs/accepted/RFC-0001-unified-run-tracking.md index 6115b94fc..c31f28c74 100644 --- a/docs/rfcs/accepted/RFC-0001-unified-run-tracking.md +++ b/docs/rfcs/accepted/RFC-0001-unified-run-tracking.md @@ -21,7 +21,7 @@ The proposal introduces `RunHandle` as a first-class ephemeral runtime object tr ### Current Architecture -AgentPool's session orchestration lives in `src/agentpool/orchestrator/core.py`, combining `SessionController` (session CRUD) and `TurnRunner` (turn execution + manual queue management). Key components: +AgentPool's session orchestration lives in `src/wolfharness/orchestrator/core.py`, combining `SessionController` (session CRUD) and `TurnRunner` (turn execution + manual queue management). Key components: - **`SessionState`**: Long-lived session metadata holding `turn_lock` (asyncio.Lock) and `active_run_ctx` (manually-synchronized pointer to `AgentRunContext`) - **`AgentRunContext`**: Ephemeral per-run container with `injection_manager` (`PromptInjectionManager`) diff --git a/docs/rfcs/accepted/RFC-0022-opencode-v144-global-event-protocol.md b/docs/rfcs/accepted/RFC-0022-opencode-v144-global-event-protocol.md index e9c019356..e6254bb84 100644 --- a/docs/rfcs/accepted/RFC-0022-opencode-v144-global-event-protocol.md +++ b/docs/rfcs/accepted/RFC-0022-opencode-v144-global-event-protocol.md @@ -19,7 +19,7 @@ related_rfcs: OpenCode v1.4.4 introduced a breaking change to the SSE event protocol. The TUI now subscribes to `/global/event` (instead of `/event`) and expects events in a new `GlobalEvent` envelope format with `directory` routing field. Without this field, the TUI cannot route events to the correct project context and silently drops all SSE events, resulting in a completely non-functional UI despite the server processing messages successfully. -This RFC proposes updating agentpool's `/global/event` SSE output to conform to the new `GlobalEvent` protocol, ensuring compatibility with OpenCode v1.4.4+ while maintaining backward compatibility with v1.4.3 clients via the unchanged `/event` endpoint. +This RFC proposes updating wolfharness's `/global/event` SSE output to conform to the new `GlobalEvent` protocol, ensuring compatibility with OpenCode v1.4.4+ while maintaining backward compatibility with v1.4.3 clients via the unchanged `/event` endpoint. > **Review Status**: This RFC has been reviewed by Oracle and Metis against the actual OpenCode v1.4.4 source at `~/src/opencode`. Key corrections applied: (1) `directory` must use `working_dir` not `base_path` to match the `/path` endpoint, (2) `workspace` field is optional in the SDK and should be omitted for single-directory servers, (3) `server.connected`/`server.heartbeat` must NOT be wrapped in GlobalEvent, (4) SyncEvent support is unnecessary for basic TUI functionality. @@ -118,8 +118,8 @@ AgentPool's `/global/event` endpoint produces events that lack the `directory` r ### Impact of Inaction -- **Critical**: All users on OpenCode v1.4.4+ cannot use agentpool's `serve-opencode` at all -- **Blocking**: agentpool is effectively incompatible with the current OpenCode release +- **Critical**: All users on OpenCode v1.4.4+ cannot use wolfharness's `serve-opencode` at all +- **Blocking**: wolfharness is effectively incompatible with the current OpenCode release - **Risk**: As OpenCode auto-updates, the user base on v1.4.4+ will only grow - **No workaround**: Pinning to v1.4.3 is not sustainable (security updates, bug fixes) @@ -140,7 +140,7 @@ AgentPool's `/global/event` endpoint produces events that lack the `directory` r 1. Rewriting the entire event system — only the serialization layer changes 2. Supporting the OpenCode Desktop app (`packages/app`) — uses same protocol but different SDK entry point -3. Adding workspace-level isolation (multi-workspace) — agentpool serves a single directory; `workspace` field omitted +3. Adding workspace-level isolation (multi-workspace) — wolfharness serves a single directory; `workspace` field omitted 4. Protocol versioning or negotiation — not needed for this change 5. Changing the `/event` endpoint behavior (backward compat preserved by default) 6. SyncEvent emission — TUI discards sync events in `useEvent()`; not needed for basic TUI functionality @@ -186,7 +186,7 @@ Add `directory`, `project`, and `workspace` fields directly in the `_serialize_e - `_serialize_event()` gains awareness of server state (currently pure function) - `project_id` computation happens on every event (though lightweight) -- `workspace` field is intentionally omitted — agentpool doesn't have a workspace concept +- `workspace` field is intentionally omitted — wolfharness doesn't have a workspace concept **Evaluation Against Criteria** @@ -208,7 +208,7 @@ Add `directory`, `project`, and `workspace` fields directly in the `_serialize_e | Risk | Likelihood | Impact | Mitigation | |------|------------|--------|------------| -| `workspace` field semantics unclear | Medium | Low | Omit `workspace` entirely; document that agentpool doesn't support workspaces | +| `workspace` field semantics unclear | Medium | Low | Omit `workspace` entirely; document that wolfharness doesn't support workspaces | | OpenCode changes protocol again | Low | Medium | Centralized serialization makes updates easy | --- @@ -434,7 +434,7 @@ class GlobalEventFactory: # symlinked paths won't match and events get dropped. self._directory = state.working_dir self._project = helpers.compute_project_id(state.working_dir) - # workspace is intentionally omitted (None) — agentpool + # workspace is intentionally omitted (None) — wolfharness # doesn't have workspaces, and workspace=project_id would # be wrong (WorkspaceID format is 'wrk_xxx', not a git SHA) @@ -623,7 +623,7 @@ No new endpoints. Changes are limited to the SSE data format: - Owner: yuchen.liu - Status: Open (investigation needed) -3. **Should agentpool support `directory: "global"` for cross-project events?** +3. **Should wolfharness support `directory: "global"` for cross-project events?** - Context: OpenCode uses `directory: "global"` for events that should be delivered regardless of project context (e.g., `workspace.status`). AgentPool currently doesn't emit such events, but future features (like multi-agent status broadcasting) might need it. - Owner: yuchen.liu - Status: Open (not needed for Phase 1) diff --git a/docs/rfcs/draft/RFC-0015-multiple-questions-elicitation.md b/docs/rfcs/draft/RFC-0015-multiple-questions-elicitation.md index 4cfb881ae..6fa199ee1 100644 --- a/docs/rfcs/draft/RFC-0015-multiple-questions-elicitation.md +++ b/docs/rfcs/draft/RFC-0015-multiple-questions-elicitation.md @@ -19,7 +19,7 @@ references: ## Overview -This RFC proposes extending agentpool's OpenCode Server to support multi-question elicitation through the MCP Elicitation protocol. Currently, the `OpenCodeInputProvider` only supports single-question elicitation with enum/array schemas. This limitation prevents tools like `question_for_user` (defined in RFC-0010) from presenting multiple questions in a single interaction. +This RFC proposes extending wolfharness's OpenCode Server to support multi-question elicitation through the MCP Elicitation protocol. Currently, the `OpenCodeInputProvider` only supports single-question elicitation with enum/array schemas. This limitation prevents tools like `question_for_user` (defined in RFC-0010) from presenting multiple questions in a single interaction. The proposal involves extending `_handle_question_elicitation` to support `object` type schemas with multiple properties, where each property represents a separate question. @@ -39,7 +39,7 @@ The proposal involves extending `_handle_question_elicitation` to support `objec ### Current State -The agentpool OpenCode Server (`agentpool_server/opencode_server/`) provides user interaction capabilities through two main input providers: +The wolfharness OpenCode Server (`wolfharness_server/opencode_server/`) provides user interaction capabilities through two main input providers: 1. **OpenCodeInputProvider**: Handles elicitation via OpenCode protocol (SSE events) 2. **ACPInputProvider**: Handles elicitation via ACP protocol (permission requests) @@ -47,7 +47,7 @@ The agentpool OpenCode Server (`agentpool_server/opencode_server/`) provides use The current `OpenCodeInputProvider._handle_question_elicitation()` implementation: ```python -# Current limitation (agentpool_server/opencode_server/input_provider.py) +# Current limitation (wolfharness_server/opencode_server/input_provider.py) async def _handle_question_elicitation(self, params, schema): match schema: case {"type": "array", "items": {"enum": [...]}}: # Multi-select single question @@ -70,7 +70,7 @@ async def _handle_question_elicitation(self, params, schema): Importantly, the OpenCode data models already support multiple questions: ```python -# agentpool_server/opencode_server/models/question.py +# wolfharness_server/opencode_server/models/question.py class QuestionRequest: questions: list[QuestionInfo] # ✅ Already supports list @@ -164,7 +164,7 @@ When a tool sends an elicitation request with an object schema containing multip ### Implementation Location -**Primary File**: `src/agentpool_server/opencode_server/input_provider.py` +**Primary File**: `src/wolfharness_server/opencode_server/input_provider.py` The implementation extends `OpenCodeInputProvider._handle_question_elicitation()` to support `object` type schemas with multiple properties. @@ -273,7 +273,7 @@ content = {key: answers[i] for i, key in enumerate(properties.keys())} #### 1. Schema Parsing Extension ```python -# agentpool_server/opencode_server/input_provider.py +# wolfharness_server/opencode_server/input_provider.py async def _handle_question_elicitation( self, @@ -311,8 +311,8 @@ async def _handle_multi_question( properties: dict[str, dict[str, Any]], ) -> types.ElicitResult | types.ErrorData: """Handle object schema with multiple properties as multiple questions.""" - from agentpool_server.opencode_server.models import QuestionInfo, QuestionOption - from agentpool_server.opencode_server.models.events import QuestionAskedEvent + from wolfharness_server.opencode_server.models import QuestionInfo, QuestionOption + from wolfharness_server.opencode_server.models.events import QuestionAskedEvent question_id = self._generate_permission_id() questions: list[QuestionInfo] = [] @@ -359,7 +359,7 @@ def _property_to_question( prop_schema: dict[str, Any], ) -> QuestionInfo: """Convert a JSON Schema property to QuestionInfo.""" - from agentpool_server.opencode_server.models import QuestionInfo, QuestionOption + from wolfharness_server.opencode_server.models import QuestionInfo, QuestionOption title = prop_schema.get("title", key) description = prop_schema.get("description", "") @@ -443,7 +443,7 @@ def resolve_question(self, question_id: str, answers: list[list[str]]) -> bool: 4. Add unit tests for multi-question scenarios **Files Modified**: -- `agentpool_server/opencode_server/input_provider.py` +- `wolfharness_server/opencode_server/input_provider.py` ### Phase 2: Testing & Validation @@ -494,9 +494,9 @@ def resolve_question(self, question_id: str, answers: list[list[str]]) -> bool: ### Code References -- `/packages/agentpool/src/agentpool_server/opencode_server/input_provider.py` -- `/packages/agentpool/src/agentpool_server/opencode_server/models/question.py` -- `/packages/agentpool/src/agentpool_server/opencode_server/state.py` +- `/packages/wolfharness/src/wolfharness_server/opencode_server/input_provider.py` +- `/packages/wolfharness/src/wolfharness_server/opencode_server/models/question.py` +- `/packages/wolfharness/src/wolfharness_server/opencode_server/state.py` ### Protocol References diff --git a/docs/rfcs/draft/RFC-0016-skill-slash-commands.md b/docs/rfcs/draft/RFC-0016-skill-slash-commands.md index f8bd1a6f4..803ec73b3 100644 --- a/docs/rfcs/draft/RFC-0016-skill-slash-commands.md +++ b/docs/rfcs/draft/RFC-0016-skill-slash-commands.md @@ -34,7 +34,7 @@ This RFC proposes a unified architecture to expose **Skills** as **Slash Command ### Current State -**Skills System** (`src/agentpool/skills/`): +**Skills System** (`src/wolfharness/skills/`): - Skills are defined in `SKILL.md` files with YAML frontmatter containing `name`, `description`, `allowed-tools`, etc. - `Skill` class models skill metadata with validation per Agent Skills Spec - `SkillsRegistry` auto-discovers skills from directories (e.g., `~/.claude/skills/`) @@ -46,13 +46,13 @@ This RFC proposes a unified architecture to expose **Skills** as **Slash Command - `CommandInputHint`: Text input specification for commands - ACP agents can expose capabilities including slash commands -**OpenCode Protocol** (`src/agentpool_server/opencode_server/`): +**OpenCode Protocol** (`src/wolfharness_server/opencode_server/`): - Uses `slashed` library for command handling - Has `SkillMetadata` TypedDict in tool_metadata.py - Commands defined with name, description, usage, and category - Supports streaming command output -**AG-UI Protocol** (`src/agentpool_server/agui_server/`): +**AG-UI Protocol** (`src/wolfharness_server/agui_server/`): - HTTP-based protocol with event streaming - Agents expose tools and capabilities via HTTP endpoints - Currently no native slash command support @@ -333,7 +333,7 @@ All protocols (OpenCode/ACP/AG-UI) use existing tool exposure #### 1. SkillCommand (Protocol-Agnostic) ```python -# src/agentpool/skills/command.py +# src/wolfharness/skills/command.py from __future__ import annotations @@ -341,7 +341,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Callable, Coroutine, ParamSpec if TYPE_CHECKING: - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill P = ParamSpec("P") @@ -383,21 +383,21 @@ class SkillCommand: #### 2. SkillCommandRegistry ```python -# src/agentpool/skills/command_registry.py +# src/wolfharness/skills/command_registry.py from __future__ import annotations from typing import TYPE_CHECKING -from agentpool.log import get_logger -from agentpool.skills.command import SkillCommand -from agentpool.utils.baseregistry import BaseRegistry +from wolfharness.log import get_logger +from wolfharness.skills.command import SkillCommand +from wolfharness.utils.baseregistry import BaseRegistry if TYPE_CHECKING: from collections.abc import Callable - from agentpool.skills.skill import Skill - from agentpool.skills.registry import SkillsRegistry + from wolfharness.skills.skill import Skill + from wolfharness.skills.registry import SkillsRegistry logger = get_logger(__name__) @@ -505,14 +505,14 @@ class SkillCommandRegistry(BaseRegistry[str, SkillCommand]): @property def _error_class(self) -> type[Exception]: - from agentpool.tools.exceptions import ToolError + from wolfharness.tools.exceptions import ToolError return ToolError ``` #### 3. Protocol Bridges -**OpenCode Bridge** (`src/agentpool_server/opencode_server/skill_bridge.py`): +**OpenCode Bridge** (`src/wolfharness_server/opencode_server/skill_bridge.py`): **⚠️ CRITICAL**: OpenCode uses **native slashed Commands** for `/command-name` execution, NOT MCP Prompts. MCP Prompts are read-only templates that cannot execute commands or modify state. @@ -526,9 +526,9 @@ from typing import TYPE_CHECKING from slashed import Command, CommandContext if TYPE_CHECKING: - from agentpool.agents.opencode_agent import OpenCodeAgent - from agentpool.skills.command import SkillCommand - from agentpool.skills.manager import SkillsManager + from wolfharness.agents.opencode_agent import OpenCodeAgent + from wolfharness.skills.command import SkillCommand + from wolfharness.skills.manager import SkillsManager class SkillCommandWrapper(Command): @@ -634,7 +634,7 @@ class OpenCodeSkillBridge: return self._commands.get(name) ``` -**ACP Bridge** (`src/agentpool_server/acp_server/commands/skill_commands.py`): +**ACP Bridge** (`src/wolfharness_server/acp_server/commands/skill_commands.py`): ```python """Bridge between SkillCommandRegistry and ACP server.""" @@ -646,7 +646,7 @@ from typing import TYPE_CHECKING from acp.schema.slash_commands import AvailableCommand if TYPE_CHECKING: - from agentpool.skills.command import SkillCommand + from wolfharness.skills.command import SkillCommand class ACPSkillBridge: @@ -700,7 +700,7 @@ class AgentCapabilities(AnnotatedObject): """ ``` -**AG-UI Bridge** (`src/agentpool_server/agui_server/skill_tools.py`): +**AG-UI Bridge** (`src/wolfharness_server/agui_server/skill_tools.py`): **⚠️ CRITICAL**: AG-UI is **tool-oriented** with no native slash command concept. Skills must be exposed as **tools**. @@ -714,8 +714,8 @@ from typing import TYPE_CHECKING, Any from ag_ui import Tool, UserMessage if TYPE_CHECKING: - from agentpool.skills.command import SkillCommand - from agentpool.skills.manager import SkillsManager + from wolfharness.skills.command import SkillCommand + from wolfharness.skills.manager import SkillsManager class AGUISkillToolAdapter: @@ -794,7 +794,7 @@ class AGUISkillBridge: **SkillCommand Config** (optional enhancement): ```python -# src/agentpool_config/skill_commands.py +# src/wolfharness_config/skill_commands.py from __future__ import annotations @@ -879,7 +879,7 @@ Therefore, skill commands must be implemented as: async def _setup_skill_commands(self) -> None: """Setup skill command registry with optional skills support.""" - from agentpool.skills.command_registry import SkillCommandRegistry + from wolfharness.skills.command_registry import SkillCommandRegistry # SkillsRegistry may be None if no skill_dirs configured skills_registry = getattr(self, '_skills', None) @@ -938,12 +938,12 @@ def get_tools(self) -> list[AGUITool]: **OpenCode Server Integration**: ```python -# In agentpool_server/opencode_server/server.py +# In wolfharness_server/opencode_server/server.py async def _setup_skill_commands(self) -> None: """Setup skill command bridge with OpenCode's native slashed system.""" - from agentpool.skills.command_registry import SkillCommandRegistry - from agentpool_server.opencode_server.skill_bridge import OpenCodeSkillBridge + from wolfharness.skills.command_registry import SkillCommandRegistry + from wolfharness_server.opencode_server.skill_bridge import OpenCodeSkillBridge from slashed import CommandStore # Initialize command registry (watches SkillsRegistry for changes) @@ -1004,11 +1004,11 @@ This provides true slash command UX with full execution capabilities. **ACP Server Integration**: ```python -# In agentpool_server/acp_server/session.py or agent.py +# In wolfharness_server/acp_server/session.py or agent.py async def _get_capabilities(self) -> AgentCapabilities: """Get agent capabilities including skill commands.""" - from agentpool_server.acp_server.commands.skill_commands import ACPSkillBridge + from wolfharness_server.acp_server.commands.skill_commands import ACPSkillBridge bridge = ACPSkillBridge() @@ -1037,17 +1037,17 @@ async def _get_capabilities(self) -> AgentCapabilities: ### Phase 1: Core Foundation (Weeks 1-2) -1. **Create SkillCommand dataclass** (`src/agentpool/skills/command.py`) +1. **Create SkillCommand dataclass** (`src/wolfharness/skills/command.py`) - Define protocol-agnostic command representation - Add validation methods -2. **Create SkillCommandRegistry** (`src/agentpool/skills/command_registry.py`) +2. **Create SkillCommandRegistry** (`src/wolfharness/skills/command_registry.py`) - Implement watcher pattern for skills changes - Add callback registration mechanism - Add dependency resolution for skills (handle depends-on field) - Unit tests with mocked SkillsRegistry -3. **Integrate with AgentPool** (`src/agentpool/delegation/pool.py`) +3. **Integrate with AgentPool** (`src/wolfharness/delegation/pool.py`) - Add `skill_commands` property to AgentPool - Initialize registry during pool startup @@ -1061,7 +1061,7 @@ async def _get_capabilities(self) -> AgentCapabilities: **Strategy**: Implement ACP Bridge first as it has the cleanest mapping. -1. **Create ACPSkillBridge** (`src/agentpool_server/acp_server/commands/skill_commands.py`) +1. **Create ACPSkillBridge** (`src/wolfharness_server/acp_server/commands/skill_commands.py`) - Map SkillCommand to AvailableCommand - Handle capability updates via `AvailableCommandsUpdate` @@ -1069,7 +1069,7 @@ async def _get_capabilities(self) -> AgentCapabilities: - Add `slash_commands` field to `AgentCapabilities` - Update JSON schema validation -3. **Integrate with ACPServer** (`src/agentpool_server/acp_server/server.py`) +3. **Integrate with ACPServer** (`src/wolfharness_server/acp_server/server.py`) - Include skill slash commands in agent capabilities - Handle command execution via session/prompt mechanism @@ -1081,7 +1081,7 @@ async def _get_capabilities(self) -> AgentCapabilities: ### Phase 3: AG-UI Bridge (Weeks 3-5) -1. **Create AGUISkillBridge** (`src/agentpool_server/agui_server/skill_tools.py`) +1. **Create AGUISkillBridge** (`src/wolfharness_server/agui_server/skill_tools.py`) - Create `AGUISkillToolAdapter` for tool format conversion - Handle OpenAI function format conversion @@ -1101,7 +1101,7 @@ async def _get_capabilities(self) -> AgentCapabilities: **⚠️ CRITICAL**: OpenCode Bridge uses **native slashed Commands** (NOT MCP Prompts). -1. **Create OpenCodeSkillBridge** (`src/agentpool_server/opencode_server/skill_bridge.py`) +1. **Create OpenCodeSkillBridge** (`src/wolfharness_server/opencode_server/skill_bridge.py`) - Create `SkillCommandWrapper` extending slashed `Command` class - Implement `execute()` method for skill loading - Integrate with OpenCode's `CommandStore` @@ -1111,7 +1111,7 @@ async def _get_capabilities(self) -> AgentCapabilities: - Handle bash/fsspec argument substitution: `$1`, `$2`, `$@`, `$ARGUMENTS` - Implement `inject_skill_context()` in OpenCodeAgent -3. **Integrate with OpenCodeServer** (`src/agentpool_server/opencode_server/server.py`) +3. **Integrate with OpenCodeServer** (`src/wolfharness_server/opencode_server/server.py`) - Register bridge during server initialization - Make CommandStore available to GET /command endpoint - Wire up command execution in agent routes @@ -1334,7 +1334,7 @@ aliases: ["myalias", "ms"] # Alternative access names **Schema Addition**: ```python -# agentpool_config/skill_commands.py +# wolfharness_config/skill_commands.py class SkillSlashConfig(Schema): """Configuration for skill slash command exposure.""" @@ -1473,19 +1473,19 @@ allowed-tools: [read,grep] # Enforced on invocation ### File Paths Reference ``` -src/agentpool/skills/ +src/wolfharness/skills/ ├── command.py # NEW: SkillCommand dataclass ├── command_registry.py # NEW: SkillCommandRegistry └── ... # existing files -src/agentpool_server/opencode_server/ +src/wolfharness_server/opencode_server/ ├── skill_bridge.py # NEW: OpenCodeSkillBridge └── server.py # MODIFY: integration -src/agentpool_server/acp_server/ +src/wolfharness_server/acp_server/ └── commands/ └── skill_commands.py # NEW: ACPSkillBridge -src/agentpool_config/ +src/wolfharness_config/ └── skill_commands.py # NEW: Optional config schema ``` diff --git a/docs/rfcs/draft/RFC-0017-opencode-command-skill-support.md b/docs/rfcs/draft/RFC-0017-opencode-command-skill-support.md index 78d932e01..fb0fe181f 100644 --- a/docs/rfcs/draft/RFC-0017-opencode-command-skill-support.md +++ b/docs/rfcs/draft/RFC-0017-opencode-command-skill-support.md @@ -21,7 +21,7 @@ The current OpenCode server `/session/{id}/command` endpoint only executes MCP P ## Problem Statement -### Current Implementation (src/agentpool_server/opencode_server/routes/session_routes.py) +### Current Implementation (src/wolfharness_server/opencode_server/routes/session_routes.py) ```python @router.post("/{session_id}/command") @@ -219,7 +219,7 @@ class UnifiedCommandExecutor: ### Implementation Details -**Modified File**: `src/agentpool_server/opencode_server/routes/session_routes.py` +**Modified File**: `src/wolfharness_server/opencode_server/routes/session_routes.py` **New Helper Function**: @@ -300,7 +300,7 @@ async def execute_command( **CommandStore Integration**: ```python -# In src/agentpool_server/opencode_server/server.py +# In src/wolfharness_server/opencode_server/server.py async def _setup_skill_commands(self) -> None: """Setup command store with skills.""" diff --git a/docs/rfcs/draft/RFC-0018-simulation-framework.md b/docs/rfcs/draft/RFC-0018-simulation-framework.md index b9da773c1..4827c2414 100644 --- a/docs/rfcs/draft/RFC-0018-simulation-framework.md +++ b/docs/rfcs/draft/RFC-0018-simulation-framework.md @@ -732,7 +732,7 @@ Without storage, simulation runs but no trajectory is recorded. **Step 1: SimulationInputProvider** ```python -# File: src/agentpool/simulation/input_provider.py +# File: src/wolfharness/simulation/input_provider.py class SimulationInputProvider(InputProvider): """Step 1 implementation - core blocking mechanism.""" @@ -766,9 +766,9 @@ class SimulationInputProvider(InputProvider): **Step 2: ElicitationRequestEvent** ```python -# File: src/agentpool/simulation/events.py +# File: src/wolfharness/simulation/events.py from dataclasses import dataclass -from agentpool.models import CustomEvent +from wolfharness.models import CustomEvent @dataclass(frozen=True) class ElicitationRequestEvent(CustomEvent): @@ -783,7 +783,7 @@ class ElicitationRequestEvent(CustomEvent): **Step 3: SimulationToolProvider** ```python -# File: src/agentpool/simulation/tool_provider.py +# File: src/wolfharness/simulation/tool_provider.py class SimulationToolProvider(ResourceProvider): """Basic tools for Sim Agent.""" @@ -864,7 +864,7 @@ async def demo_simulation(): - Timeout/deep parameters in YAML 2. **CLI Commands** - - `agentpool simulate --sim-agent X --target Y` + - `wolfharness simulate --sim-agent X --target Y` - Batch simulation runner - Trajectory export @@ -1022,13 +1022,13 @@ async def cooperative_simulation(sim_agent, target_agent): ```bash # Run single simulation -agentpool simulate \ +wolfharness simulate \ --sim-agent engineer_sim \ --target diagnosis \ --scenario scenarios/motor_failure.yml # Batch execution -agentpool simulate-batch \ +wolfharness simulate-batch \ --sim-agent engineer_sim \ --target diagnosis \ --scenarios-dir scenarios/ \ @@ -1036,9 +1036,9 @@ agentpool simulate-batch \ --parallel 4 # View trajectories -agentpool history view --agent diagnosis --session +wolfharness history view --agent diagnosis --session # Evaluate results -agentpool simulate-eval results/ --criteria criteria.yml +wolfharness simulate-eval results/ --criteria criteria.yml ``` diff --git a/docs/rfcs/draft/RFC-0019-mcp-server-display-name-separation.md b/docs/rfcs/draft/RFC-0019-mcp-server-display-name-separation.md index 27ed3ab7d..03cbf1288 100644 --- a/docs/rfcs/draft/RFC-0019-mcp-server-display-name-separation.md +++ b/docs/rfcs/draft/RFC-0019-mcp-server-display-name-separation.md @@ -17,17 +17,17 @@ Currently, MCP servers in AgentPool are displayed with auto-generated identifier The MCP server naming follows this chain: -1. **MCPManager initialization** (`src/agentpool/delegation/pool.py:149`): +1. **MCPManager initialization** (`src/wolfharness/delegation/pool.py:149`): ```python self.mcp = MCPManager(name="pool_mcp", servers=servers, owner="pool") ``` -2. **Provider name construction** (`src/agentpool/mcp_server/manager.py:137`): +2. **Provider name construction** (`src/wolfharness/mcp_server/manager.py:137`): ```python name=f"{self.name}_{config.client_id}" ``` -3. **Client ID generation** (`src/agentpool_config/mcp_server.py`): +3. **Client ID generation** (`src/wolfharness_config/mcp_server.py`): - StreamableHTTP: `f"streamable_http_{self.url}"` - SSE: `f"sse_{self.url}"` - Stdio: `f"{self.command}_{args}"` @@ -162,7 +162,7 @@ The MCP server naming follows this chain: ### Changes Required -#### 1. Config Layer (`src/agentpool_config/mcp_server.py`) +#### 1. Config Layer (`src/wolfharness_config/mcp_server.py`) Add `display_name` property to base or each config class: @@ -182,7 +182,7 @@ Apply to: - `SSEMCPServerConfig` - `StreamableHTTPMCPServerConfig` -#### 2. Manager Layer (`src/agentpool/mcp_server/manager.py`) +#### 2. Manager Layer (`src/wolfharness/mcp_server/manager.py`) Update provider name construction (line 137): @@ -194,7 +194,7 @@ name=f"{self.name}_{config.client_id}" name=f"{self.name}_{config.display_name}" ``` -#### 3. API Layer (`src/agentpool_server/opencode_server/routes/agent_routes.py`) +#### 3. API Layer (`src/wolfharness_server/opencode_server/routes/agent_routes.py`) Update MCP status response (line 178): diff --git a/docs/rfcs/draft/RFC-0024-agent-stateless-refactor.md b/docs/rfcs/draft/RFC-0024-agent-stateless-refactor.md index 622a19ce7..14e226c84 100644 --- a/docs/rfcs/draft/RFC-0024-agent-stateless-refactor.md +++ b/docs/rfcs/draft/RFC-0024-agent-stateless-refactor.md @@ -19,7 +19,7 @@ related_rfcs: ## Overview -This RFC proposes refactoring `BaseAgent` to be stateless with respect to sessions: move session-scoped state (`conversation`, `_input_provider`, `session_id`) off the agent instance and into parameters passed at call time. This aligns agentpool's agent model with pydantic-ai's design (where the agent is a stateless worker and conversation persistence is the caller's responsibility), and enables Phase 3's shared-agent architecture where a single `BaseAgent` instance serves all sessions. +This RFC proposes refactoring `BaseAgent` to be stateless with respect to sessions: move session-scoped state (`conversation`, `_input_provider`, `session_id`) off the agent instance and into parameters passed at call time. This aligns wolfharness's agent model with pydantic-ai's design (where the agent is a stateless worker and conversation persistence is the caller's responsibility), and enables Phase 3's shared-agent architecture where a single `BaseAgent` instance serves all sessions. ## Background & Context @@ -42,7 +42,7 @@ Key design elements: 2. **`deps`** — Caller-injected business dependencies, per-call, NOT per-agent 3. **`GraphAgentState`** — Internal framework state, created per-run, returned via `AgentRunResult`, NOT stored on the agent -### agentpool's Current Misalignment +### wolfharness's Current Misalignment `BaseAgent` holds session-scoped state as **instance variables**, making the agent stateful: @@ -74,7 +74,7 @@ This coupling forces each session to have its own agent instance, incurring MCP - **Resource waste**: 5 concurrent sessions × 2 MCP servers = 10 subprocesses, ~50–250MB - **Architecture fragility**: New session-scoped state added to `BaseAgent` automatically becomes shared-state risk -- **pydantic-ai misalignment**: agentpool's agent model diverges from the framework it's built on +- **pydantic-ai misalignment**: wolfharness's agent model diverges from the framework it's built on ## Goals & Non-Goals @@ -210,7 +210,7 @@ iterator = agent.run_stream( | `session_routes.py:1437` | `agent.conversation.compact()` | `state.compact_messages(session_id)` | | `session_routes.py:564,599` | `agent.conversation.chat_messages` (read) | `state.messages[session_id]` (read) | -**After refactor, `self.conversation` is REMOVED from `BaseAgent`** (hard cut — no dual-path deprecation). All callers must pass `message_history=` explicitly to `run_stream()`. This includes CLI usage (`agentpool run`), which must manage its own message history list. +**After refactor, `self.conversation` is REMOVED from `BaseAgent`** (hard cut — no dual-path deprecation). All callers must pass `message_history=` explicitly to `run_stream()`. This includes CLI usage (`wolfharness run`), which must manage its own message history list. #### 1a. Non-Mechanical `self.conversation` Edge Cases @@ -423,7 +423,7 @@ Each phase is independently revertable. If `_active_run_ctx` registry proves pro - Status: **Resolved** — per-instance dict, NOT ClassVar (avoids cross-agent-type collision as identified by Oracle/Metis review). ClassVar would cause `ACPAgent` and `AGUIAgent` to share the same dict, leading to `interrupt()` canceling the wrong agent type's run. 2. **Backward compatibility for `self.conversation`**: Should `BaseAgent.conversation` still exist as a fallback for non-server callers? - - Context: Direct agent usage (e.g., `agentpool run`) doesn't have a server managing message history. + - Context: Direct agent usage (e.g., `wolfharness run`) doesn't have a server managing message history. - Status: **Resolved** — hard cut, no dual-path. Remove `self.conversation` as fallback entirely. All callers must pass `message_history=` explicitly. Dual-path (deprecation warning) creates maintenance burden and masks migration errors, as identified by Metis review. 3. **`SessionState` dataclass**: Should the server consolidate per-session state into a single `SessionState` container? @@ -472,6 +472,6 @@ Each phase is independently revertable. If `_active_run_ctx` registry proves pro ### Key Source Files -- `packages/agentpool/src/agentpool/agents/base_agent.py` — `BaseAgent`, session-scoped instance variables -- `packages/agentpool/src/agentpool/agents/agent.py` — `Agent`, `_run_stream_once()`, `self.conversation` references -- `packages/agentpool/src/agentpool/running/run_context.py` — `AgentRunContext`, per-run isolation +- `packages/wolfharness/src/wolfharness/agents/base_agent.py` — `BaseAgent`, session-scoped instance variables +- `packages/wolfharness/src/wolfharness/agents/agent.py` — `Agent`, `_run_stream_once()`, `self.conversation` references +- `packages/wolfharness/src/wolfharness/running/run_context.py` — `AgentRunContext`, per-run isolation diff --git a/docs/rfcs/draft/RFC-0025-shared-agent-architecture.md b/docs/rfcs/draft/RFC-0025-shared-agent-architecture.md index 81ccb23b6..f5ded43f2 100644 --- a/docs/rfcs/draft/RFC-0025-shared-agent-architecture.md +++ b/docs/rfcs/draft/RFC-0025-shared-agent-architecture.md @@ -15,7 +15,7 @@ related_rfcs: # RFC-0025: Shared Agent Architecture — Single Agent, Per-Session State -> **Phase 3 of the Multi-Session Isolation Roadmap.** Depends on [RFC-0024](./RFC-0024-agent-stateless-refactor.md) (Phase 2). This is the convergence point that aligns agentpool's server architecture with pydantic-ai's stateless agent model. +> **Phase 3 of the Multi-Session Isolation Roadmap.** Depends on [RFC-0024](./RFC-0024-agent-stateless-refactor.md) (Phase 2). This is the convergence point that aligns wolfharness's server architecture with pydantic-ai's stateless agent model. ## Overview @@ -46,7 +46,7 @@ result1 = await agent.run("hello", message_history=[], deps=deps1) result2 = await agent.run("hello", message_history=[], deps=deps2) # Different session ``` -Phase 3 makes agentpool's server match this model exactly: one `BaseAgent` instance, many `run_stream()` calls, each with its own `message_history` and `input_provider`. +Phase 3 makes wolfharness's server match this model exactly: one `BaseAgent` instance, many `run_stream()` calls, each with its own `message_history` and `input_provider`. ### Resource Savings @@ -472,10 +472,10 @@ Revert to Phase 1's per-session agents by restoring `ServerState.agents: dict[st ### Key Source Files -- `packages/agentpool/src/agentpool_server/opencode_server/state.py` — `ServerState`, per-session agent registry -- `packages/agentpool/src/agentpool_server/opencode_server/routes/session_routes.py` — Session CRUD -- `packages/agentpool/src/agentpool_server/opencode_server/routes/message_routes.py` — Message handling -- `packages/agentpool/src/agentpool/agents/base_agent.py` — `BaseAgent` (stateless after Phase 2) +- `packages/wolfharness/src/wolfharness_server/opencode_server/state.py` — `ServerState`, per-session agent registry +- `packages/wolfharness/src/wolfharness_server/opencode_server/routes/session_routes.py` — Session CRUD +- `packages/wolfharness/src/wolfharness_server/opencode_server/routes/message_routes.py` — Message handling +- `packages/wolfharness/src/wolfharness/agents/base_agent.py` — `BaseAgent` (stateless after Phase 2) ## Review Notes diff --git a/docs/rfcs/draft/RFC-0027-acp-subagent-zed-compatibility.md b/docs/rfcs/draft/RFC-0027-acp-subagent-zed-compatibility.md index 10207cc1b..459665544 100644 --- a/docs/rfcs/draft/RFC-0027-acp-subagent-zed-compatibility.md +++ b/docs/rfcs/draft/RFC-0027-acp-subagent-zed-compatibility.md @@ -1191,17 +1191,17 @@ Phase 0 与 Phase 1 可并行执行。Phase 0 仅阻塞 Phase 2(需要传输 ### ACP Schema 文件 -- `packages/agentpool/src/acp/schema/base.py` — `AnnotatedObject` 基类,`field_meta` 字段(序列化为 `_meta`) -- `packages/agentpool/src/acp/schema/session_updates.py` — `ToolCallStart`/`ToolCallProgress` 定义 +- `packages/wolfharness/src/acp/schema/base.py` — `AnnotatedObject` 基类,`field_meta` 字段(序列化为 `_meta`) +- `packages/wolfharness/src/acp/schema/session_updates.py` — `ToolCallStart`/`ToolCallProgress` 定义 ### AgentPool 源码 -- `packages/agentpool/src/agentpool_server/acp_server/event_converter.py` — 核心 ACP 事件转换器 -- `packages/agentpool/src/agentpool_server/acp_server/session.py` — ACP session 管理 -- `packages/agentpool/src/agentpool_server/acp_server/session_manager.py` — Session 生命周期管理 -- `packages/agentpool/src/agentpool_server/acp_server/server.py` — ACP server 入口 -- `packages/agentpool_toolsets/builtin/subagent_tools.py` — 发射 SubAgentEvent 和 SpawnSessionStart -- `packages/agentpool/src/agentpool/agents/events/events.py` — 事件定义:SubAgentEvent(line 614)、SpawnSessionStart(line 646) +- `packages/wolfharness/src/wolfharness_server/acp_server/event_converter.py` — 核心 ACP 事件转换器 +- `packages/wolfharness/src/wolfharness_server/acp_server/session.py` — ACP session 管理 +- `packages/wolfharness/src/wolfharness_server/acp_server/session_manager.py` — Session 生命周期管理 +- `packages/wolfharness/src/wolfharness_server/acp_server/server.py` — ACP server 入口 +- `packages/wolfharness_toolsets/builtin/subagent_tools.py` — 发射 SubAgentEvent 和 SpawnSessionStart +- `packages/wolfharness/src/wolfharness/agents/events/events.py` — 事件定义:SubAgentEvent(line 614)、SpawnSessionStart(line 646) ### 相关 RFC diff --git a/docs/rfcs/draft/RFC-0028-delegation-provider-session-adaptation.md b/docs/rfcs/draft/RFC-0028-delegation-provider-session-adaptation.md index 36122171f..b043ead3b 100644 --- a/docs/rfcs/draft/RFC-0028-delegation-provider-session-adaptation.md +++ b/docs/rfcs/draft/RFC-0028-delegation-provider-session-adaptation.md @@ -41,7 +41,7 @@ This RFC proposes adapting all delegation providers (SubagentTools, WorkersTools ### Current State -AgentPool has a `SessionManager` class (`src/agentpool/sessions/manager.py`) with a `create_child_session()` method that: +AgentPool has a `SessionManager` class (`src/wolfharness/sessions/manager.py`) with a `create_child_session()` method that: 1. Generates a unique session ID 2. Loads the parent session and inherits `project_id` / `cwd` @@ -69,7 +69,7 @@ The server-side `EventProcessor` or `ServerState.ensure_session()` reactively ma | Term | Definition | |------|------------| | **Delegation Provider** | Any mechanism that spawns child agent execution: SubagentTools (`task`), WorkersTools, Team (parallel), TeamRun (sequential) | -| **SessionManager** | Central class for session CRUD and child session creation (`src/agentpool/sessions/manager.py`) | +| **SessionManager** | Central class for session CRUD and child session creation (`src/wolfharness/sessions/manager.py`) | | **SessionStore** | Protocol for session persistence (`SessionData` objects) — memory or SQL-backed | | **SessionData** | Pydantic model with `session_id`, `parent_id`, `project_id`, `cwd`, `agent_name`, `agent_type` | | **SpawnSessionStart** | Event emitted before child content starts; carries `child_session_id`, `parent_session_id`, `depth`, metadata | @@ -408,7 +408,7 @@ AFTER: Add a convenience method to `AgentContext` that wraps `pool.sessions.create_child_session()`: ```python -# src/agentpool/agents/context.py +# src/wolfharness/agents/context.py class AgentContext: # ... existing code ... @@ -448,7 +448,7 @@ class AgentContext: #### 2. SubagentTools Adaptation ```python -# src/agentpool_toolsets/builtin/subagent_tools.py +# src/wolfharness_toolsets/builtin/subagent_tools.py # BEFORE: child_session_id = identifier.ascending("session") @@ -532,7 +532,7 @@ child_depth = (ctx.run_ctx.depth if ctx.run_ctx is not None else 0) + 1 **Solution**: Add `depth: int = 0` as an explicit parameter to `BaseAgent.run_stream()`: ```python -# src/agentpool/agents/base_agent.py +# src/wolfharness/agents/base_agent.py # BEFORE (actual 12-param signature — simplified for illustration): async def run_stream( @@ -581,7 +581,7 @@ This is the **only correct approach** — alternatives like stripping `depth` fr Add session propagation to parallel team execution: ```python -# src/agentpool/delegation/team.py +# src/wolfharness/delegation/team.py async def run_stream(self, *prompts, depth: int = 0, **kwargs): all_nodes = list(self.nodes) @@ -668,7 +668,7 @@ async def run_stream(self, *prompts, depth: int = 0, **kwargs): Sequential team — each step's child session is created independently: ```python -# src/agentpool/delegation/teamrun.py +# src/wolfharness/delegation/teamrun.py async def run_stream(self, *prompts, depth: int = 0, require_all: bool = True, **kwargs): # Read session_id from kwargs first (set by SubagentTools caller), @@ -752,7 +752,7 @@ async def run_stream(self, *prompts, depth: int = 0, require_all: bool = True, * **Solution**: Remove the dead field or connect it to `BaseAgent.session_id`: ```python -# src/agentpool/agents/base_agent.py +# src/wolfharness/agents/base_agent.py # BEFORE: run_ctx = AgentRunContext(deps=deps) @@ -768,7 +768,7 @@ run_ctx = AgentRunContext(deps=deps, session_id=self.session_id) **Recommendation**: Option C (deprecate) — mark the field as deprecated using a descriptor that emits `DeprecationWarning` on access. Removing it is a public API break for external consumers. In a future major version, it can be removed entirely. ```python -# src/agentpool/agents/context.py +# src/wolfharness/agents/context.py import warnings from dataclasses import dataclass, field @@ -835,7 +835,7 @@ AgentRunContext.session_id = _DeprecatedSessionId() # type: ignore[assignment] **Solution**: Add `parent_session_id` parameter to `create_session()`. When provided, call `create_child_session()` (with inheritance). When not provided, create a top-level session with the existing project_id computation from cwd. ```python -# src/agentpool_server/acp_server/session_manager.py +# src/wolfharness_server/acp_server/session_manager.py # BEFORE: async def create_session( @@ -892,7 +892,7 @@ async def create_session( ) else: # Top-level ACP session — compute project_id from cwd - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id project_id = compute_project_id(cwd) data = SessionData( session_id=session_id, @@ -941,7 +941,7 @@ The second `store.save()` would **overwrite** the correct data from step 1. **Solution**: `ensure_session()` must skip `store.save()` when the session was already persisted: ```python -# src/agentpool_server/opencode_server/state.py +# src/wolfharness_server/opencode_server/state.py async def ensure_session(self, session_id: str, parent_id: str | None = None) -> Session: # Check if session already exists in memory @@ -1011,7 +1011,7 @@ A native agent's implementation type is `"native"`, but its delegation source ty **Solution**: Provide two separate, type-safe functions with distinct return types: ```python -# src/agentpool/messaging/messagenode.py +# src/wolfharness/messaging/messagenode.py class MessageNode: # ... existing code ... @@ -1024,9 +1024,9 @@ class MessageNode: Returns values from AgentTypeLiteral domain. """ # Import locally to avoid circular imports - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation.team import Team - from agentpool.delegation.teamrun import TeamRun + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation.team import Team + from wolfharness.delegation.teamrun import TeamRun if isinstance(self, Team | TeamRun): return "team" @@ -1036,10 +1036,10 @@ class MessageNode: ``` ```python -# src/agentpool/agents/helpers.py +# src/wolfharness/agents/helpers.py from typing import Literal -from agentpool.messaging.messagenode import MessageNode +from wolfharness.messaging.messagenode import MessageNode # The return type matches SpawnSessionStart.source_type and SubAgentEvent.source_type SourceType = Literal["agent", "team_parallel", "team_sequential"] @@ -1054,9 +1054,9 @@ def get_source_type(node: MessageNode) -> SourceType: - agent_type = implementation type (native, acp, claude, codex, team) - source_type = delegation type (agent, team_parallel, team_sequential) """ - from agentpool.delegation.team import Team - from agentpool.delegation.teamrun import TeamRun - from agentpool.agents.base_agent import BaseAgent + from wolfharness.delegation.team import Team + from wolfharness.delegation.teamrun import TeamRun + from wolfharness.agents.base_agent import BaseAgent match node: case Team(): @@ -1073,7 +1073,7 @@ def get_source_type(node: MessageNode) -> SourceType: - `get_source_type(node)` → used for `SpawnSessionStart.source_type`, `SubAgentEvent.source_type` (returns `"agent"` / `"team_parallel"` / `"team_sequential"`) - `node.agent_type` → used for `SessionData.agent_type`, `create_child_session(agent_type=...)` (returns `"native"` / `"acp"` / `"team"` etc.) -**Pre-existing codebase bug**: `team.py:32` and `teamrun.py:28` import `SubAgentType` from `agentpool.agents.events.events` inside `TYPE_CHECKING` blocks, but `SubAgentType` does **not exist** in that module. This is a type-checking error that's silently ignored at runtime. The new `SourceType` type alias and `get_source_type()` function in `helpers.py` replace this broken import. **Implementation action**: Update the `TYPE_CHECKING` imports in `team.py` and `teamrun.py` to use `from agentpool.agents.helpers import SourceType` instead of the broken `SubAgentType` import. +**Pre-existing codebase bug**: `team.py:32` and `teamrun.py:28` import `SubAgentType` from `wolfharness.agents.events.events` inside `TYPE_CHECKING` blocks, but `SubAgentType` does **not exist** in that module. This is a type-checking error that's silently ignored at runtime. The new `SourceType` type alias and `get_source_type()` function in `helpers.py` replace this broken import. **Implementation action**: Update the `TYPE_CHECKING` imports in `team.py` and `teamrun.py` to use `from wolfharness.agents.helpers import SourceType` instead of the broken `SubAgentType` import. **`AgentTypeLiteral` domain note**: `AgentTypeLiteral = Literal["native", "acp", "agui", "claude", "codex"]` does NOT include `"team"`. The `MessageNode.agent_type` property returns `"team"` for Team/TeamRun instances, which is outside the `AgentTypeLiteral` domain. This is acceptable because `SessionData.agent_type` is typed as `str | None` (not `AgentTypeLiteral`), so storing `"team"` is valid at runtime and in the persistence layer. The `"team"` value provides useful semantic information about which kind of node created the session. If stricter typing is desired in the future, `AgentTypeLiteral` could be extended to include `"team"`. @@ -1123,7 +1123,7 @@ Calling Session (parent) Providers must enforce a maximum delegation depth to prevent unbounded recursion: ```python -# src/agentpool/agents/base_agent.py or constants +# src/wolfharness/agents/base_agent.py or constants MAX_DELEGATION_DEPTH = 10 # Hard cap on delegation nesting @@ -1137,10 +1137,10 @@ if current_depth >= MAX_DELEGATION_DEPTH: child_depth = current_depth + 1 ``` -`DelegationDepthError` is defined in `src/agentpool/agents/exceptions.py` alongside other agent-specific exceptions. `MAX_DELEGATION_DEPTH` is a module-level constant in the same file. +`DelegationDepthError` is defined in `src/wolfharness/agents/exceptions.py` alongside other agent-specific exceptions. `MAX_DELEGATION_DEPTH` is a module-level constant in the same file. ```python -# src/agentpool/agents/exceptions.py +# src/wolfharness/agents/exceptions.py class DelegationDepthError(Exception): """Raised when delegation depth exceeds MAX_DELEGATION_DEPTH.""" @@ -1834,9 +1834,9 @@ Verify both `agent_type` and `get_source_type()` return domain-appropriate value async def test_messagenode_agent_type_and_source_type(): """MessageNode.agent_type and get_source_type() must return values from their respective domains.""" - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation.team import Team - from agentpool.delegation.teamrun import TeamRun + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation.team import Team + from wolfharness.delegation.teamrun import TeamRun native = Agent(name="native", model="test") team = Team([native], name="team") @@ -1848,7 +1848,7 @@ async def test_messagenode_agent_type_and_source_type(): assert teamrun.agent_type == "team" # Implementation type # get_source_type returns delegation source type (source_type domain) - from agentpool.agents.helpers import get_source_type + from wolfharness.agents.helpers import get_source_type assert get_source_type(native) == "agent" # NOT "native"! assert get_source_type(team) == "team_parallel" # NOT "team"! assert get_source_type(teamrun) == "team_sequential" # NOT "team"! @@ -1862,7 +1862,7 @@ Verify that `get_source_type()` return values are valid for `source_type` fields async def test_get_source_type_compatible_with_source_type_literal(): """get_source_type() must return values accepted by SpawnSessionStart.source_type and SubAgentEvent.source_type.""" - from agentpool.agents.helpers import get_source_type, SourceType + from wolfharness.agents.helpers import get_source_type, SourceType for node in [native_agent, acp_agent, team, teamrun]: result = get_source_type(node) @@ -1901,8 +1901,8 @@ Verify the `case _` branch in `get_source_type()` returns `"agent"` with a warni async def test_get_source_type_wildcard_defaults_to_agent(): """Unknown MessageNode subclasses should default to 'agent' with a warning log.""" - from agentpool.agents.helpers import get_source_type - from agentpool.messaging.messagenode import MessageNode + from wolfharness.agents.helpers import get_source_type + from wolfharness.messaging.messagenode import MessageNode class CustomNode(MessageNode): name: str = "custom" @@ -1921,7 +1921,7 @@ Verify the descriptor works correctly with `dataclasses.asdict()`: async def test_deprecated_session_id_asdict(): """dataclasses.asdict() should work with the _DeprecatedSessionId descriptor, triggering deprecation warning on access.""" - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.context import AgentRunContext ctx = AgentRunContext(deps=None, depth=0) with pytest.warns(DeprecationWarning, match="session_id"): @@ -1938,10 +1938,10 @@ def test_messagenode_agent_type_no_circular_import(): """MessageNode.agent_type property with local imports should not cause circular import errors at module load time.""" import importlib - import agentpool.messaging.messagenode + import wolfharness.messaging.messagenode # Force re-import to detect circular dependency - importlib.reload(agentpool.messaging.messagenode) + importlib.reload(wolfharness.messaging.messagenode) # Should not raise ImportError ``` @@ -2046,7 +2046,7 @@ async def test_ensure_session_store_first_skips_bind(): | 2 | Component #9 factual error — `BaseAgent.run_stream()` does NOT propagate `parent_session_id` → `parent_id`; they are independent parameters (session-level vs message-level) | Metis HR-5 | ✅ Rewrote Component #10 (renumbered) with correct distinction and explanation of how `Session.parent_id` is populated | | 3 | `_stream_task()` pseudocode missing — `skip_spawn_event` described in text but no pseudocode; simpler approach: remove SpawnSessionStart from `_stream_task()` entirely | Oracle SHOULD, Metis HR-7 | ✅ Replaced `skip_spawn_event` with structural single-emission: `_stream_task()` never emits `SpawnSessionStart` | | 4 | Duplicate "#### 8." numbering still broken — added Component #8 without renumbering | Oracle SHOULD | ✅ Renumbered 8→8, 8→9, 9→10, 10→11, 11→12 | -| 5 | `DelegationDepthError` referenced but undefined — no module location specified | Oracle SHOULD | ✅ Added definition in `src/agentpool/agents/exceptions.py` with `MAX_DELEGATION_DEPTH` constant | +| 5 | `DelegationDepthError` referenced but undefined — no module location specified | Oracle SHOULD | ✅ Added definition in `src/wolfharness/agents/exceptions.py` with `MAX_DELEGATION_DEPTH` constant | | 6 | `TeamRun.run_stream()` pseudocode drops `require_all: bool = True` parameter | Metis AMB-3 | ✅ Added `require_all` to TeamRun signature | | 7 | `ensure_session()` in-memory path returns without broadcasting `SessionUpdatedEvent` — TUI regression vs current code | Metis AMB-4 | ✅ Added `SessionUpdatedEvent` broadcast to in-memory return path | | 8 | WorkersTools hardcodes `depth=1` in four locations — breaks nested delegation | Metis AMB-6 | ✅ Added depth propagation specification: `(ctx.run_ctx.depth if ctx.run_ctx is not None else 0) + 1` | @@ -2123,15 +2123,15 @@ async def test_ensure_session_store_first_skips_bind(): ### Key Source Files -- `src/agentpool/sessions/manager.py` — `SessionManager.create_child_session()` -- `src/agentpool/sessions/store.py` — `SessionStore` protocol -- `src/agentpool/sessions/models.py` — `SessionData` model -- `src/agentpool_toolsets/builtin/subagent_tools.py` — SubagentTools -- `src/agentpool_toolsets/builtin/workers.py` — WorkersTools -- `src/agentpool/delegation/team.py` — Team (parallel) -- `src/agentpool/delegation/teamrun.py` — TeamRun (sequential) -- `src/agentpool/agents/context.py` — `AgentContext` -- `src/agentpool/agents/events/events.py` — `SpawnSessionStart`, `SubAgentEvent` -- `src/agentpool_server/opencode_server/state.py` — `ServerState.ensure_session()` -- `src/agentpool_server/opencode_server/event_processor.py` — `EventProcessor` -- `src/agentpool_server/acp_server/session_manager.py` — `ACPSessionManager` +- `src/wolfharness/sessions/manager.py` — `SessionManager.create_child_session()` +- `src/wolfharness/sessions/store.py` — `SessionStore` protocol +- `src/wolfharness/sessions/models.py` — `SessionData` model +- `src/wolfharness_toolsets/builtin/subagent_tools.py` — SubagentTools +- `src/wolfharness_toolsets/builtin/workers.py` — WorkersTools +- `src/wolfharness/delegation/team.py` — Team (parallel) +- `src/wolfharness/delegation/teamrun.py` — TeamRun (sequential) +- `src/wolfharness/agents/context.py` — `AgentContext` +- `src/wolfharness/agents/events/events.py` — `SpawnSessionStart`, `SubAgentEvent` +- `src/wolfharness_server/opencode_server/state.py` — `ServerState.ensure_session()` +- `src/wolfharness_server/opencode_server/event_processor.py` — `EventProcessor` +- `src/wolfharness_server/acp_server/session_manager.py` — `ACPSessionManager` diff --git a/docs/rfcs/draft/RFC-0029-agent-reactivation-pending-prompt-queue.md b/docs/rfcs/draft/RFC-0029-agent-reactivation-pending-prompt-queue.md index 88512ac5c..41979ae2c 100644 --- a/docs/rfcs/draft/RFC-0029-agent-reactivation-pending-prompt-queue.md +++ b/docs/rfcs/draft/RFC-0029-agent-reactivation-pending-prompt-queue.md @@ -563,7 +563,7 @@ Solution: First caller gets the prompt (queue.get() is atomic). Second caller se - `_pending_prompt_event` is lightweight — no task allocation, no timer. - No long-lived `asyncio.Task` is created by the agent — the caller decides when to start `run_stream()`. - `clear_pending_prompts()` provided for cleanup during agent shutdown. Should be wired into `__aexit__` or `stop()` (Phase 4). -- **Event loop affinity**: `asyncio.Queue` and `asyncio.Event` are not thread-safe. All callers must be in the same event loop. This matches current agentpool architecture where all agent operations are asyncio-based. +- **Event loop affinity**: `asyncio.Queue` and `asyncio.Event` are not thread-safe. All callers must be in the same event loop. This matches current wolfharness architecture where all agent operations are asyncio-based. --- @@ -649,10 +649,10 @@ Solution: First caller gets the prompt (queue.get() is atomic). Second caller se ## References -- `src/agentpool/agents/base_agent.py` — `inject_prompt()`, `queue_prompt()`, `run_stream()` -- `src/agentpool/agents/prompt_injection.py` — `PromptInjectionManager` -- `src/agentpool/agents/native_agent/hook_manager.py` — injection consumption in post-tool hooks -- `src/agentpool_server/opencode_server/routes/message_routes.py` — `_run_async_prompt_queue()` +- `src/wolfharness/agents/base_agent.py` — `inject_prompt()`, `queue_prompt()`, `run_stream()` +- `src/wolfharness/agents/prompt_injection.py` — `PromptInjectionManager` +- `src/wolfharness/agents/native_agent/hook_manager.py` — injection consumption in post-tool hooks +- `src/wolfharness_server/opencode_server/routes/message_routes.py` — `_run_async_prompt_queue()` - xeno-agent `background_task_provider.py` L803-811 — `_on_task_completed()` callback - OpenCode TypeScript: `effect/runner.ts` — Runner state machine - OpenCode TypeScript: `session/prompt.ts` — `while(true)` run loop diff --git a/docs/rfcs/draft/RFC-0030-acp-streamable-http-websocket-transport.md b/docs/rfcs/draft/RFC-0030-acp-streamable-http-websocket-transport.md index 260eeb88b..6b9687408 100644 --- a/docs/rfcs/draft/RFC-0030-acp-streamable-http-websocket-transport.md +++ b/docs/rfcs/draft/RFC-0030-acp-streamable-http-websocket-transport.md @@ -96,7 +96,7 @@ Without this change, AgentPool can only serve ACP agents via stdio or a non-comp ### Success Criteria -- [ ] ACP server can be started with `agentpool serve-acp config.yml --transport streamable-http --port 8080` and accepts WebSocket connections at `/acp` +- [ ] ACP server can be started with `wolfharness serve-acp config.yml --transport streamable-http --port 8080` and accepts WebSocket connections at `/acp` - [ ] WebSocket upgrade response includes `Acp-Connection-Id` header with UUID v4 - [ ] Pre-initialize requests receive JSON-RPC error code `-32002` - [ ] Post-initialize requests process normally through `AgentSideConnection` @@ -456,10 +456,10 @@ Client connects via WebSocket upgrade at /acp ### CLI Integration ``` -agentpool serve-acp config.yml # Default: stdio transport -agentpool serve-acp config.yml --transport streamable-http # WebSocket on localhost:8080 -agentpool serve-acp config.yml --transport streamable-http --port 9000 # WebSocket on localhost:9000 -agentpool serve-acp config.yml --transport streamable-http --host 0.0.0.0 --port 9000 # WebSocket on 0.0.0.0:9000 +wolfharness serve-acp config.yml # Default: stdio transport +wolfharness serve-acp config.yml --transport streamable-http # WebSocket on localhost:8080 +wolfharness serve-acp config.yml --transport streamable-http --port 9000 # WebSocket on localhost:9000 +wolfharness serve-acp config.yml --transport streamable-http --host 0.0.0.0 --port 9000 # WebSocket on 0.0.0.0:9000 ``` The `--transport` flag accepts `stdio` (default), `websocket` (legacy, deprecated), or `streamable-http` (new compliant transport). When `--transport streamable-http` is used, `--host` and `--port` control the bind address. When `--transport websocket` is used, the legacy non-compliant `_serve_websocket()` transport is instantiated; a deprecation warning is emitted directing the user to switch to `--transport streamable-http`. The legacy `--ws-host` and `--ws-port` parameters are still accepted for backward compatibility but emit deprecation warnings directing users to `--host` and `--port`. @@ -468,7 +468,7 @@ The `--transport` flag accepts `stdio` (default), `websocket` (legacy, deprecate #### ACPPoolServerConfig Fields -The `ACPPoolServerConfig` model in `src/agentpool_config/pool_server.py` is extended with: +The `ACPPoolServerConfig` model in `src/wolfharness_config/pool_server.py` is extended with: ```python class ACPPoolServerConfig(BasePoolServerConfig): @@ -756,5 +756,5 @@ None recorded yet. ### Appendix Current transport code: `src/acp/transports.py` -ACP server: `src/agentpool_server/acp_server/server.py` -Pool server config: `src/agentpool_config/pool_server.py` +ACP server: `src/wolfharness_server/acp_server/server.py` +Pool server config: `src/wolfharness_config/pool_server.py` diff --git a/docs/rfcs/draft/RFC-0032-acp-slash-commands-session-update.md b/docs/rfcs/draft/RFC-0032-acp-slash-commands-session-update.md index 501e210eb..161ec59de 100644 --- a/docs/rfcs/draft/RFC-0032-acp-slash-commands-session-update.md +++ b/docs/rfcs/draft/RFC-0032-acp-slash-commands-session-update.md @@ -58,9 +58,9 @@ The schema layer supports both paths: skill_commands = self.get_skill_commands() return InitializeResponse.create( protocol_version=version, - name="agentpool", + name="wolfharness", title="AgentPool", - version=_version("agentpool"), + version=_version("wolfharness"), # ... other capabilities ... slash_commands=skill_commands, # ← ADVERTISED AT INIT TIME ) @@ -388,7 +388,7 @@ slash_commands: list[AvailableCommand] = Field(default_factory=list) #### 3. `AgentPoolACPAgent.initialize()` Update -**File**: `src/agentpool_server/acp_server/acp_agent.py` +**File**: `src/wolfharness_server/acp_server/acp_agent.py` **Remove**: ```python @@ -437,7 +437,7 @@ The `get_skill_commands()` method on `AgentPoolACPAgent` is currently used **onl |------|---------| | `src/acp/schema/capabilities.py` | Remove `slash_commands` field from `AgentCapabilities`; update `create()` | | `src/acp/schema/agent_responses.py` | Remove `slash_commands` parameter from `InitializeResponse.create()` | -| `src/agentpool_server/acp_server/acp_agent.py` | Remove `slash_commands=skill_commands` from `initialize()` | +| `src/wolfharness_server/acp_server/acp_agent.py` | Remove `slash_commands=skill_commands` from `initialize()` | **Duration**: 0.5 day @@ -606,8 +606,8 @@ Revert by restoring: - `src/acp/schema/capabilities.py:273` — `AgentCapabilities.slash_commands` - `src/acp/schema/agent_responses.py:284-339` — `InitializeResponse.create()` -- `src/agentpool_server/acp_server/acp_agent.py:482-505` — `AgentPoolACPAgent.initialize()` -- `src/agentpool_server/acp_server/session.py:562-572` — `ACPSession.send_available_commands_update()` +- `src/wolfharness_server/acp_server/acp_agent.py:482-505` — `AgentPoolACPAgent.initialize()` +- `src/wolfharness_server/acp_server/session.py:562-572` — `ACPSession.send_available_commands_update()` - `src/acp/agent/notifications.py:336-339` — `ACPNotifications.update_commands()` - `src/acp/schema/session_updates.py:354-363` — `AvailableCommandsUpdate` diff --git a/docs/rfcs/draft/RFC-0034-acp-session-config-options-unified.md b/docs/rfcs/draft/RFC-0034-acp-session-config-options-unified.md index 234f94412..4496e1d12 100644 --- a/docs/rfcs/draft/RFC-0034-acp-session-config-options-unified.md +++ b/docs/rfcs/draft/RFC-0034-acp-session-config-options-unified.md @@ -48,7 +48,7 @@ AgentPool 当前实现: - `NewSessionResponse.config_options` → 透出所有 `agent.get_modes()` 返回的 categories(`get_session_config_options()`) - `NewSessionResponse.models` → 透出 tokonomics 发现的模型 + manifest model_variants(`get_session_model_state()`) -#### agentpool 内部的 `get_modes()` 体系 +#### wolfharness 内部的 `get_modes()` 体系 各 agent 类型的 `get_modes()` 返回如下 categories: @@ -465,7 +465,7 @@ ACP 协议 PR #648(已 MERGED)定义了 `providers/list`、`providers/set` │ │ │ ┌─ 运行时路由 ───────────────────────────────────────────────┐ │ │ │ │ │ -│ │ Provider 路由表(agentpool 内存维护) │ │ +│ │ Provider 路由表(wolfharness 内存维护) │ │ │ │ 默认:manifest model_variants → provider → default URL │ │ │ │ 覆盖:providers/set → provider → client URL + headers │ │ │ │ 禁用:providers/disable → provider → null(不可路由) │ │ @@ -483,7 +483,7 @@ ACP 协议 PR #648(已 MERGED)定义了 `providers/list`、`providers/set` #### 数据模型 -从 ACP PR #648 协议定义映射到 agentpool 的类型: +从 ACP PR #648 协议定义映射到 wolfharness 的类型: ```python # acp/schema/providers.py(新增文件) @@ -540,7 +540,7 @@ class ProvidersDisableResponse(BaseModel): #### Provider 路由表 -agentpool 内存维护一个 provider 路由表,存储 `providers/set` 的覆盖配置: +wolfharness 内存维护一个 provider 路由表,存储 `providers/set` 的覆盖配置: ```python # acp_server/provider_router.py(新增文件) @@ -806,7 +806,7 @@ async def get_session_model_state( agent: BaseAgent, provider_router: ProviderRouter | None = None, ) -> SessionModelState | None: - from agentpool_server.shared.model_utils import build_model_state_for_acp + from wolfharness_server.shared.model_utils import build_model_state_for_acp manifest = getattr(getattr(agent, "agent_pool", None), "manifest", None) return await build_model_state_for_acp(agent, manifest, provider_router) ``` @@ -1233,19 +1233,19 @@ Phase 0 必须最先实施(Phase 1 依赖 `ProviderRouter`)。Phase 1 和 Ph ### AgentPool 源码 -- `packages/agentpool/src/agentpool_server/acp_server/acp_agent.py` — ACP server 核心,`get_session_model_state`、`get_session_config_options` -- `packages/agentpool/src/agentpool_server/acp_server/converters.py` — `to_session_config_option()`、`agent_to_mode()`(已有但未使用) -- `packages/agentpool/src/agentpool_server/opencode_server/routes/config_routes.py` — OpenCode model list 逻辑、`/mode` 路由 -- `packages/agentpool/src/agentpool_server/shared/model_utils.py` — 现有共享 model 工具函数 -- `packages/agentpool/src/agentpool/agents/native_agent/helpers.py` — `get_permission_category()`、`get_model_category()` -- `packages/agentpool/src/agentpool/agents/modes.py` — `ModeCategory`、`ModeInfo`、`ModeCategoryId` -- `packages/agentpool/src/acp/schema/capabilities.py` — `AgentCapabilities`(需新增 `providers` 字段) -- `packages/agentpool/src/agentpool/models/manifest.py` — `AgentsManifest.model_variants`(`providers/list` 的数据来源) +- `packages/wolfharness/src/wolfharness_server/acp_server/acp_agent.py` — ACP server 核心,`get_session_model_state`、`get_session_config_options` +- `packages/wolfharness/src/wolfharness_server/acp_server/converters.py` — `to_session_config_option()`、`agent_to_mode()`(已有但未使用) +- `packages/wolfharness/src/wolfharness_server/opencode_server/routes/config_routes.py` — OpenCode model list 逻辑、`/mode` 路由 +- `packages/wolfharness/src/wolfharness_server/shared/model_utils.py` — 现有共享 model 工具函数 +- `packages/wolfharness/src/wolfharness/agents/native_agent/helpers.py` — `get_permission_category()`、`get_model_category()` +- `packages/wolfharness/src/wolfharness/agents/modes.py` — `ModeCategory`、`ModeInfo`、`ModeCategoryId` +- `packages/wolfharness/src/acp/schema/capabilities.py` — `AgentCapabilities`(需新增 `providers` 字段) +- `packages/wolfharness/src/wolfharness/models/manifest.py` — `AgentsManifest.model_variants`(`providers/list` 的数据来源) ### ACP 协议 -- `packages/agentpool/src/acp/schema/session_state.py` — `SessionConfigOption`、`SessionConfigOptionCategory`、`SessionModeState` -- `packages/agentpool/src/acp/schema/agent_responses.py` — `NewSessionResponse`(`models`、`modes`、`config_options` 字段) +- `packages/wolfharness/src/acp/schema/session_state.py` — `SessionConfigOption`、`SessionConfigOptionCategory`、`SessionModeState` +- `packages/wolfharness/src/acp/schema/agent_responses.py` — `NewSessionResponse`(`models`、`modes`、`config_options` 字段) - [ACP 协议官网](https://agentclientprotocol.com/protocol/session-modes) - [ACP PR #648: Configurable LLM Providers](https://github.com/agentclientprotocol/agent-client-protocol/pull/648) — `providers/*` 方法族定义(已 MERGED) - `packages/agent-client-protocol/docs/rfds/custom-llm-endpoint.mdx` — ACP RFD 原文 diff --git a/docs/rfcs/draft/RFC-0034-background-task-redesign.md b/docs/rfcs/draft/RFC-0034-background-task-redesign.md index 23f78ecaf..9f359695a 100644 --- a/docs/rfcs/draft/RFC-0034-background-task-redesign.md +++ b/docs/rfcs/draft/RFC-0034-background-task-redesign.md @@ -46,7 +46,7 @@ related_rfcs: ### 1.1 Summary -This RFC proposes a fundamental redesign of the BackgroundTask system, migrating it from the xeno-agent business layer into agentpool as a first-class core infrastructure, while preserving xeno-agent-specific provider customizations. The redesign replaces the legacy string-based task ID API with a structured `TaskHandle` object pattern aligned with MCP SEP-1686, introduces configurable session-end lifecycle policies, and hard-cuts over from the old API without a compatibility layer. +This RFC proposes a fundamental redesign of the BackgroundTask system, migrating it from the xeno-agent business layer into wolfharness as a first-class core infrastructure, while preserving xeno-agent-specific provider customizations. The redesign replaces the legacy string-based task ID API with a structured `TaskHandle` object pattern aligned with MCP SEP-1686, introduces configurable session-end lifecycle policies, and hard-cuts over from the old API without a compatibility layer. ### 1.2 Why This Matters Now @@ -55,14 +55,14 @@ The current BackgroundTask implementation lives entirely in `xeno-agent` and suf 1. **Fire-and-forget bug**: The `_notify_parent` callback uses `asyncio.create_task()` without `cancel_and_drain`, causing exceptions to be silently discarded when the parent session has ended. 2. **`parent_session_id` empty string bug**: Prevents auto-resume of background task completion notifications. 3. **String ID API**: The legacy API returns raw strings (`task_id`) with no structured handle, making it impossible to chain operations or attach metadata. -4. **No upstream standard**: Each downstream project reinvents background task patterns; agentpool lacks a unified abstraction. +4. **No upstream standard**: Each downstream project reinvents background task patterns; wolfharness lacks a unified abstraction. These issues block reliable multi-agent delegation workflows and create support burden. ### 1.3 Expected Outcome After implementation: -- **Unified infrastructure**: `BackgroundTaskManager` and `TaskHandle` are core agentpool primitives. +- **Unified infrastructure**: `BackgroundTaskManager` and `TaskHandle` are core wolfharness primitives. - **Structured API**: All background task operations return `TaskHandle` objects with typed methods (`status`, `result()`, `cancel()`). - **Session lifecycle integration**: Configurable `session_end_policy` controls what happens to background tasks when their parent session ends. - **SEP-1686 alignment**: API surface is compatible with MCP task semantics, enabling future protocol-native task support. @@ -80,7 +80,7 @@ The BackgroundTask system currently consists of three layers: |-------|----------|---------------| | **Core types** | `xeno_agent/task/types.py` | `BackgroundTask` dataclass, `TaskHandle` dataclass, `TaskStatus` literal | | **Lifecycle manager** | `xeno_agent/task/manager.py` | `BackgroundTaskManager` — semaphore, timeout, cancellation, cleanup | -| **Provider integration** | `xeno_agent/agentpool/resource_providers/background_task_provider.py` | Tool definitions (`task`, `background_output`, `background_cancel`) | +| **Provider integration** | `xeno_agent/wolfharness/resource_providers/background_task_provider.py` | Tool definitions (`task`, `background_output`, `background_cancel`) | The existing `BackgroundTaskProvider` exposes three tools: @@ -106,7 +106,7 @@ Key problems with this design: ### 2.2 Historical Context -The BackgroundTask system was initially designed as a xeno-agent-specific augmentation to agentpool's `subagent_tools.py`. The original RFC (RFC-0001-v2) explicitly stated: "xeno-agent layer优先:先在业务层验证,成熟后考虑 upstream 到 agentpool." After 3 months of production validation with 130+ tests, the pattern has proven stable enough to upstream. +The BackgroundTask system was initially designed as a xeno-agent-specific augmentation to wolfharness's `subagent_tools.py`. The original RFC (RFC-0001-v2) explicitly stated: "xeno-agent layer优先:先在业务层验证,成熟后考虑 upstream 到 wolfharness." After 3 months of production validation with 130+ tests, the pattern has proven stable enough to upstream. ### 2.3 Glossary @@ -115,7 +115,7 @@ The BackgroundTask system was initially designed as a xeno-agent-specific augmen | **BackgroundTask** | Serializable dataclass representing a task's metadata and lifecycle state | | **TaskHandle** | Runtime object wrapping an `asyncio.Task` with structured query/cancel methods | | **TaskStatus** | Literal union: `pending`, `running`, `cancelling`, `completed`, `error`, `cancelled`, `timed_out` | -| **SessionPool** | agentpool's session orchestration layer (`SessionPool` + `SessionController`) | +| **SessionPool** | wolfharness's session orchestration layer (`SessionPool` + `SessionController`) | | **session_end_policy** | Configuration controlling background task behavior on parent session end | | **SEP-1686** | MCP Specification Enhancement Proposal for asynchronous task tools | | **fire-and-forget** | Pattern of spawning async work without awaiting or cleanup; explicitly avoided | @@ -125,7 +125,7 @@ The BackgroundTask system was initially designed as a xeno-agent-specific augmen - **RFC-0021**: Introduced `AgentRunContext` for per-call state isolation; BackgroundTask redesign must respect per-run context boundaries. - **RFC-0026**: Per-session agent isolation ensures background tasks do not share `MessageHistory` across concurrent subagents. -- **RFC-0001 (agentpool)**: Workers and Teams session management establishes `SpawnSessionStart` patterns that background tasks must emit consistently. +- **RFC-0001 (wolfharness)**: Workers and Teams session management establishes `SpawnSessionStart` patterns that background tasks must emit consistently. ### 2.5 Cross-System Comparison: opencode @@ -180,7 +180,7 @@ opencode's "no status query" design works for its use case (short research tasks 3. **Empty `parent_session_id`**: Line 379 of `background_task_provider.py` falls back to `getattr(ctx.node, 'session_id', '')` — an empty string prevents `SessionPool.inject_prompt()` from routing the notification correctly. -4. **No upstream abstraction**: Every downstream project that wants background tasks must copy xeno-agent's implementation. There is no agentpool-native primitive. +4. **No upstream abstraction**: Every downstream project that wants background tasks must copy xeno-agent's implementation. There is no wolfharness-native primitive. 5. **String-based API**: Returning raw string IDs makes it impossible for the LLM to introspect task state without making another tool call. @@ -210,7 +210,7 @@ opencode's "no status query" design works for its use case (short research tasks ### 4.1 Goals (In Scope) -1. **Upstream core infrastructure**: `BackgroundTaskManager`, `TaskHandle`, and `TaskStatus` become agentpool core primitives. +1. **Upstream core infrastructure**: `BackgroundTaskManager`, `TaskHandle`, and `TaskStatus` become wolfharness core primitives. 2. **Structured API**: Replace string-ID returns with `TaskHandle` objects exposing `.status`, `.result()`, `.cancel()`. 3. **Session lifecycle integration**: Introduce `session_end_policy` with `cancel`, `keep`, and `notify` strategies. 4. **SEP-1686 alignment**: API signatures and semantics match MCP task tool patterns. @@ -225,16 +225,16 @@ opencode's "no status query" design works for its use case (short research tasks 1. **Not**: Implementing MCP server-side task support (SEP-1686 server changes are a separate effort). 2. **Not**: Persistent task storage across process restarts. -3. **Not**: Distributed task execution across multiple agentpool instances. +3. **Not**: Distributed task execution across multiple wolfharness instances. 4. **Not**: Changing the underlying LLM provider concurrency model. 5. **Not**: Adding new event types beyond what already exists (`SpawnSessionStart`, `SubAgentEvent`, `StreamCompleteEvent`). ### 4.3 Success Criteria -- [ ] `TaskHandle` is importable from `agentpool.tasks`. -- [ ] `BackgroundTaskManager` is importable from `agentpool.tasks`. +- [ ] `TaskHandle` is importable from `wolfharness.tasks`. +- [ ] `BackgroundTaskManager` is importable from `wolfharness.tasks`. - [ ] `session_end_policy` is configurable per `AgentPool` or per session. -- [ ] xeno-agent's `BackgroundTaskProvider` delegates to agentpool core. +- [ ] xeno-agent's `BackgroundTaskProvider` delegates to wolfharness core. - [ ] All 124 xeno-agent background task tests pass with new API. - [ ] No `asyncio.create_task()` without matching `cancel_and_drain` pattern. - [ ] Pydantic-AI `cancel_and_drain` is used in all cleanup paths. @@ -260,15 +260,15 @@ This section documents the four confirmed design decisions that govern the archi ### Q1: Where does the BackgroundTask infrastructure live? -**Decision: C — Hybrid approach: agentpool core infrastructure + xeno-agent specific Provider** +**Decision: C — Hybrid approach: wolfharness core infrastructure + xeno-agent specific Provider** **Rationale**: -The core lifecycle manager (`BackgroundTaskManager`), types (`TaskHandle`, `BackgroundTask`), and session integration belong in agentpool so all downstream projects benefit. However, the specific tool schemas, prompt formatting, and xeno-agent-specific behaviors (e.g., `load_skills` injection, XML prompt formatting) remain in xeno-agent's `BackgroundTaskProvider`. +The core lifecycle manager (`BackgroundTaskManager`), types (`TaskHandle`, `BackgroundTask`), and session integration belong in wolfharness so all downstream projects benefit. However, the specific tool schemas, prompt formatting, and xeno-agent-specific behaviors (e.g., `load_skills` injection, XML prompt formatting) remain in xeno-agent's `BackgroundTaskProvider`. **Scope split**: -| Component | agentpool (core) | xeno-agent (provider) | +| Component | wolfharness (core) | xeno-agent (provider) | |-----------|-----------------|----------------------| | `TaskHandle` | ✅ | ❌ | | `BackgroundTask` dataclass | ✅ | ❌ | @@ -284,8 +284,8 @@ The core lifecycle manager (`BackgroundTaskManager`), types (`TaskHandle`, `Back **Trade-offs**: - **Pro**: Downstream projects (xeno-rag, xeno-serve) can reuse core infrastructure. -- **Pro**: agentpool can integrate background tasks with `SessionPool` natively. -- **Con**: Slightly more complex import graph; xeno-agent depends on agentpool tasks. +- **Pro**: wolfharness can integrate background tasks with `SessionPool` natively. +- **Con**: Slightly more complex import graph; xeno-agent depends on wolfharness tasks. - **Con**: Core changes require coordinated releases. ### Q2: What is the API pattern for task handles? @@ -306,7 +306,7 @@ status = await background_output(task_id="bg_abc123", block=False) **New API** (SEP-1686 style): ```python -from agentpool.tasks import TaskHandle +from wolfharness.tasks import TaskHandle # Returns a structured handle handle: TaskHandle = await run_background_task(agent_mode="expert", prompt="analyze") @@ -343,7 +343,7 @@ await handle.cancel() **Rationale**: -A single hardcoded behavior cannot satisfy all use cases. Industrial diagnostics (`xeno-agent`) often needs `notify` so the fault expert receives completion prompts. Batch processing jobs need `keep`. Default agentpool behavior should be `cancel` for safety. +A single hardcoded behavior cannot satisfy all use cases. Industrial diagnostics (`xeno-agent`) often needs `notify` so the fault expert receives completion prompts. Batch processing jobs need `keep`. Default wolfharness behavior should be `cancel` for safety. **Implementation hook**: @@ -378,7 +378,7 @@ for task in task_manager.get_tasks_by_session(session_id): **Migration scope**: - `xeno-agent` tests: 124 test cases updated. -- `xeno-agent` provider: `BackgroundTaskProvider` rewritten to delegate to agentpool core. +- `xeno-agent` provider: `BackgroundTaskProvider` rewritten to delegate to wolfharness core. - `diag-agent.yaml`: Tool schema references updated. **Trade-offs**: @@ -455,10 +455,10 @@ Three options were considered: ### 7.2 Component Design -#### 7.2.1 `agentpool.tasks` Module (New) +#### 7.2.1 `wolfharness.tasks` Module (New) ```python -# agentpool/tasks/__init__.py +# wolfharness/tasks/__init__.py from __future__ import annotations from dataclasses import dataclass, field @@ -537,10 +537,10 @@ class TaskHandle: ... ``` -#### 7.2.2 `BackgroundTaskManager` (agentpool core) +#### 7.2.2 `BackgroundTaskManager` (wolfharness core) ```python -# agentpool/tasks/manager.py +# wolfharness/tasks/manager.py from __future__ import annotations import asyncio @@ -555,7 +555,7 @@ from .types import BackgroundTask, TaskHandle, TaskStatus, TERMINAL_STATES class BackgroundTaskManager: """Manages background task lifecycle, concurrency, timeout, and cleanup. - This is the agentpool-core equivalent of xeno-agent's + This is the wolfharness-core equivalent of xeno-agent's ``BackgroundTaskManager``, with these key differences: 1. Uses ``cancel_and_drain`` from pydantic-ai for all cleanup paths. @@ -788,10 +788,10 @@ No single layer is sufficient. Together, they create a gradient from "the LLM kn ## 8. API Design -### 8.1 agentpool Core API +### 8.1 wolfharness Core API ```python -# agentpool/tasks/__init__.py +# wolfharness/tasks/__init__.py from dataclasses import dataclass from datetime import datetime @@ -856,7 +856,7 @@ class BackgroundTaskManager: ### 8.2 xeno-agent Provider API (New Tools) ```python -# xeno_agent/agentpool/resource_providers/background_task_provider.py +# xeno_agent/wolfharness/resource_providers/background_task_provider.py async def run_background_task( ctx: AgentContext, @@ -952,7 +952,7 @@ agents: session_end_policy: notify # cancel | keep | notify tools: - type: custom - import_path: xeno_agent.agentpool.resource_providers.background_task_provider.XenoBackgroundTaskProvider + import_path: xeno_agent.wolfharness.resource_providers.background_task_provider.XenoBackgroundTaskProvider enabled_tools: - run_background_task - task_status @@ -969,9 +969,9 @@ agents: The `SessionController.close_session()` method is the hook point for policy enforcement: ```python -# agentpool/sessions/controller.py +# wolfharness/sessions/controller.py -from agentpool.tasks import BackgroundTaskManager +from wolfharness.tasks import BackgroundTaskManager class SessionController: def __init__(self, ..., task_manager: BackgroundTaskManager): @@ -1107,9 +1107,9 @@ status = handle.status # Query current status await handle.cancel() # Request cancellation ``` -### 11.2 agentpool-to-SEP-1686 Mapping +### 11.2 wolfharness-to-SEP-1686 Mapping -| SEP-1686 Concept | agentpool Equivalent | Notes | +| SEP-1686 Concept | wolfharness Equivalent | Notes | |-----------------|----------------------|-------| | `@tool(task=True)` | `run_background_task()` tool | Explicit tool, not decorator | | `TaskHandle.result()` | `TaskHandle.result()` | Direct equivalent | @@ -1119,11 +1119,11 @@ await handle.cancel() # Request cancellation ### 11.3 Future MCP Server Integration -When agentpool implements an MCP server with SEP-1686 support: +When wolfharness implements an MCP server with SEP-1686 support: ```python -# Future: agentpool MCP server -from agentpool.tasks import BackgroundTaskManager +# Future: wolfharness MCP server +from wolfharness.tasks import BackgroundTaskManager class AgentPoolMCPServer: def __init__(self, task_manager: BackgroundTaskManager): @@ -1161,8 +1161,8 @@ The following test files in `xeno-agent` must be updated: ### 12.2 Migration Checklist -- [ ] **Phase 0**: Create agentpool `tasks` module with `TaskHandle`, `BackgroundTask`, `BackgroundTaskManager`. -- [ ] **Phase 0**: Port `BackgroundTaskManager` from xeno-agent to agentpool, adding `cancel_and_drain` compliance. +- [ ] **Phase 0**: Create wolfharness `tasks` module with `TaskHandle`, `BackgroundTask`, `BackgroundTaskManager`. +- [ ] **Phase 0**: Port `BackgroundTaskManager` from xeno-agent to wolfharness, adding `cancel_and_drain` compliance. - [ ] **Phase 1**: Add `session_end_policy` to `SessionConfig` and enforcement in `SessionController`. - [ ] **Phase 2**: Rewrite xeno-agent `BackgroundTaskProvider`: - [ ] Replace `task()` with `run_background_task()` returning `TaskHandle`. @@ -1194,20 +1194,20 @@ The following test files in `xeno-agent` must be updated: ## 13. Implementation Plan -### Phase 0: agentpool Core (Week 1) +### Phase 0: wolfharness Core (Week 1) **Deliverables**: -- `agentpool/tasks/types.py` — `TaskStatus`, `BackgroundTask`, `TaskHandle` -- `agentpool/tasks/manager.py` — `BackgroundTaskManager` with `cancel_and_drain` -- `agentpool/tasks/__init__.py` — Public exports +- `wolfharness/tasks/types.py` — `TaskStatus`, `BackgroundTask`, `TaskHandle` +- `wolfharness/tasks/manager.py` — `BackgroundTaskManager` with `cancel_and_drain` +- `wolfharness/tasks/__init__.py` — Public exports - Unit tests for core manager (semaphore, timeout, cancellation, cleanup) **Files Modified**: -- `src/agentpool/tasks/` (new directory) -- `src/agentpool/sessions/controller.py` (session_end_policy hook) -- `src/agentpool/sessions/config.py` (session_end_policy field) +- `src/wolfharness/tasks/` (new directory) +- `src/wolfharness/sessions/controller.py` (session_end_policy hook) +- `src/wolfharness/sessions/config.py` (session_end_policy field) -**Rollback**: Delete `src/agentpool/tasks/` directory. +**Rollback**: Delete `src/wolfharness/tasks/` directory. ### Phase 1: Session Integration (Week 1-2) @@ -1217,9 +1217,9 @@ The following test files in `xeno-agent` must be updated: - `BackgroundTaskManager` session association methods **Files Modified**: -- `src/agentpool/sessions/config.py` -- `src/agentpool/sessions/controller.py` -- `src/agentpool/tasks/manager.py` +- `src/wolfharness/sessions/config.py` +- `src/wolfharness/sessions/controller.py` +- `src/wolfharness/tasks/manager.py` **Rollback**: Revert session controller changes. @@ -1231,7 +1231,7 @@ The following test files in `xeno-agent` must be updated: - Empty string `parent_session_id` fix **Files Modified**: -- `src/xeno_agent/agentpool/resource_providers/background_task_provider.py` +- `src/xeno_agent/wolfharness/resource_providers/background_task_provider.py` **Rollback**: Restore from git. @@ -1243,14 +1243,14 @@ The following test files in `xeno-agent` must be updated: - New tests for `TaskHandle` methods **Files Modified**: -- `tests/agentpool/resource_providers/test_background_task_*.py` +- `tests/wolfharness/resource_providers/test_background_task_*.py` **Rollback**: Restore from git. ### Dependencies -- agentpool Phase 0 must complete before xeno-agent Phase 2. -- agentpool Phase 1 must complete before xeno-agent test additions for `session_end_policy`. +- wolfharness Phase 0 must complete before xeno-agent Phase 2. +- wolfharness Phase 1 must complete before xeno-agent test additions for `session_end_policy`. --- @@ -1276,7 +1276,7 @@ The following test files in `xeno-agent` must be updated: - Owner: Performance - Status: Open -5. **Should agentpool expose a `@background_task` decorator for tool functions?** +5. **Should wolfharness expose a `@background_task` decorator for tool functions?** - Context: Would allow declarative background task registration. - Owner: API design - Status: Open @@ -1291,7 +1291,7 @@ The following test files in `xeno-agent` must be updated: - Owner: xeno-agent - Status: Open -8. **Should `list_tasks()` be a core agentpool tool or a xeno-agent provider tool?** +8. **Should `list_tasks()` be a core wolfharness tool or a xeno-agent provider tool?** - Context: Task listing is useful for any downstream project, but the markdown table format is presentation-level. - Owner: API design - Status: Open @@ -1323,7 +1323,7 @@ The following test files in `xeno-agent` must be updated: ### Key Discussion Points -1. **Why not Option A (all in agentpool)?** Would force xeno-agent's XML formatting and skill-loading into core, creating inappropriate coupling. +1. **Why not Option A (all in wolfharness)?** Would force xeno-agent's XML formatting and skill-loading into core, creating inappropriate coupling. 2. **Why not Option B (all in xeno-agent)?** Defeats the purpose of upstreaming proven infrastructure. 3. **Why not keep string IDs?** String parsing is error-prone and incompatible with MCP SEP-1686. 4. **Why not a compatibility layer?** Doubles maintenance burden for a single-consumer API. @@ -1331,11 +1331,11 @@ The following test files in `xeno-agent` must be updated: ### Conditions on Approval - [ ] At least 2 code reviewers approve -- [ ] agentpool core tests demonstrate 100% manager coverage +- [ ] wolfharness core tests demonstrate 100% manager coverage - [ ] All 124 xeno-agent tests pass with new API - [ ] Session-end policy tests cover all 3 variants - [ ] No `asyncio.create_task()` without matching cleanup in changed code -- [ ] Documentation updated (agentpool tasks module, xeno-agent migration guide) +- [ ] Documentation updated (wolfharness tasks module, xeno-agent migration guide) --- @@ -1346,11 +1346,11 @@ The following test files in `xeno-agent` must be updated: 1. **RFC-0001-v2 (xeno-agent)** — Original background task RFC Location: `packages/xeno-agent/docs/rfcs/RFC-0001-async-task-background-task-v2.md` -2. **RFC-0021 (agentpool)** — Agent Concurrent Execution Safety - Location: `packages/agentpool/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md` +2. **RFC-0021 (wolfharness)** — Agent Concurrent Execution Safety + Location: `packages/wolfharness/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md` 3. **BackgroundTaskProvider Implementation** - Location: `packages/xeno-agent/src/xeno_agent/agentpool/resource_providers/background_task_provider.py` + Location: `packages/xeno-agent/src/xeno_agent/wolfharness/resource_providers/background_task_provider.py` 4. **Research Findings** Location: `.omo/notepads/background-task-provider/learnings.md` @@ -1370,10 +1370,10 @@ The following test files in `xeno-agent` must be updated: |-----------|------| | xeno-agent BackgroundTaskManager | `packages/xeno-agent/src/xeno_agent/task/manager.py` | | xeno-agent BackgroundTask types | `packages/xeno-agent/src/xeno_agent/task/types.py` | -| xeno-agent BackgroundTaskProvider | `packages/xeno-agent/src/xeno_agent/agentpool/resource_providers/background_task_provider.py` | +| xeno-agent BackgroundTaskProvider | `packages/xeno-agent/src/xeno_agent/wolfharness/resource_providers/background_task_provider.py` | | Pydantic-AI cancel_and_drain | `packages/pydantic-ai/pydantic_ai_slim/pydantic_ai/_utils.py:223-240` | -| AgentPool SessionController | `packages/agentpool/src/agentpool/sessions/controller.py` | -| AgentPool SessionConfig | `packages/agentpool/src/agentpool/sessions/config.py` | +| AgentPool SessionController | `packages/wolfharness/src/wolfharness/sessions/controller.py` | +| AgentPool SessionConfig | `packages/wolfharness/src/wolfharness/sessions/config.py` | --- diff --git a/docs/rfcs/draft/RFC-0034-per-session-todo-tracker-isolation.md b/docs/rfcs/draft/RFC-0034-per-session-todo-tracker-isolation.md index 3868ce398..be759a752 100644 --- a/docs/rfcs/draft/RFC-0034-per-session-todo-tracker-isolation.md +++ b/docs/rfcs/draft/RFC-0034-per-session-todo-tracker-isolation.md @@ -120,7 +120,7 @@ When multiple sessions exist (ACP, OpenCode, or mixed), they all share a single ### Impact of Inaction -- **Risk**: Teams sharing an agentpool server see each other's task lists. This is a functional correctness issue, not just a UX concern. +- **Risk**: Teams sharing an wolfharness server see each other's task lists. This is a functional correctness issue, not just a UX concern. - **Risk**: Session restore (`load_session`, `resume_session`) cannot reconstruct the user's task list because todos are not persisted per-session. - **Cost**: ACP `AgentPlanUpdate` integration remains incomplete, blocking proper plan management for ACP-connected clients. - **Cost**: Memory waste — deleted sessions' todo entries accumulate in the global tracker with no eviction. @@ -521,7 +521,7 @@ async def _create_and_persist_session(self, session_id, parent_id): async def on_session_todo_change(t: TodoTracker) -> None: """Broadcast todo updates to this session only.""" - from agentpool_server.opencode_server.models.events import Todo, TodoUpdatedEvent + from wolfharness_server.opencode_server.models.events import Todo, TodoUpdatedEvent todos = [ Todo(id=e.id, content=e.content, status=e.status, priority=e.priority) for e in t.entries @@ -573,7 +573,7 @@ tracker = await self.agent_pool.aget_or_create_session_todos(session_id) async def on_acp_todo_change(t: TodoTracker) -> None: """Emit ACP AgentPlanUpdate when todos change.""" from acp.schema import AgentPlanUpdate, PlanEntry as ACPPlanEntry - from agentpool.utils.todos import PlanEntry + from wolfharness.utils.todos import PlanEntry entries = [ ACPPlanEntry(content=e.content, priority=e.priority, status=e.status) @@ -596,7 +596,7 @@ case AgentPlanUpdate(entries=entries): # Update per-session tracker with remote plan entries tracker = self._agent.agent_pool.get_session_todos(self._agent.session_id) if tracker is not None: - from agentpool.utils.todos import PlanEntry + from wolfharness.utils.todos import PlanEntry native_entries = [ PlanEntry(content=e.content, priority=e.priority, status=e.status) for e in entries @@ -778,12 +778,12 @@ Self-contained change. Revert by: ### Key Source Files -- `src/agentpool/utils/todos.py` — `TodoTracker`, `TodoEntry`, `PlanEntry` -- `src/agentpool/delegation/pool.py` — `AgentPool.todos` (global instance) -- `src/agentpool/resource_providers/plan_provider.py` — `PlanProvider` (tool implementation) -- `src/agentpool_server/opencode_server/server.py` — Global `on_todo_change` callback -- `src/agentpool_server/opencode_server/state.py` — `ServerState.todos` (in-memory cache) -- `src/agentpool_server/opencode_server/routes/session_routes.py` — `get_session_todos()` route -- `src/agentpool_server/acp_server/event_converter.py` — `PlanUpdateEvent` → `AgentPlanUpdate` -- `src/agentpool/agents/acp_agent/client_handler.py` — Unresolved `AgentPlanUpdate` TODO -- `src/agentpool/agents/codex_agent/codex_agent.py` — `pool.todos.replace_all()` sync +- `src/wolfharness/utils/todos.py` — `TodoTracker`, `TodoEntry`, `PlanEntry` +- `src/wolfharness/delegation/pool.py` — `AgentPool.todos` (global instance) +- `src/wolfharness/resource_providers/plan_provider.py` — `PlanProvider` (tool implementation) +- `src/wolfharness_server/opencode_server/server.py` — Global `on_todo_change` callback +- `src/wolfharness_server/opencode_server/state.py` — `ServerState.todos` (in-memory cache) +- `src/wolfharness_server/opencode_server/routes/session_routes.py` — `get_session_todos()` route +- `src/wolfharness_server/acp_server/event_converter.py` — `PlanUpdateEvent` → `AgentPlanUpdate` +- `src/wolfharness/agents/acp_agent/client_handler.py` — Unresolved `AgentPlanUpdate` TODO +- `src/wolfharness/agents/codex_agent/codex_agent.py` — `pool.todos.replace_all()` sync diff --git a/docs/rfcs/draft/RFC-0035-mcp-over-acp-complete-connection-chain.md b/docs/rfcs/draft/RFC-0035-mcp-over-acp-complete-connection-chain.md index 45a72f938..7984210eb 100644 --- a/docs/rfcs/draft/RFC-0035-mcp-over-acp-complete-connection-chain.md +++ b/docs/rfcs/draft/RFC-0035-mcp-over-acp-complete-connection-chain.md @@ -50,7 +50,7 @@ RFC-0033 已经实现了: ### MCP 客户端完整生命周期(Non-ACP 传输) -对于 stdio/SSE/HTTP 传输的 MCP server,agentpool 已经有一套成熟的连接链: +对于 stdio/SSE/HTTP 传输的 MCP server,wolfharness 已经有一套成熟的连接链: ``` session/new @@ -69,8 +69,8 @@ session/new ``` 关键组件: -- **`MCPResourceProvider`**(`agentpool/resource_providers/mcp_provider.py`):MCP server 的 ResourceProvider 包装,管理工具/提示/资源的生命周期 -- **`MCPClient`**(`agentpool/mcp_server/client.py`):fastmcp Client 的包装,处理连接、重连、消息路由 +- **`MCPResourceProvider`**(`wolfharness/resource_providers/mcp_provider.py`):MCP server 的 ResourceProvider 包装,管理工具/提示/资源的生命周期 +- **`MCPClient`**(`wolfharness/mcp_server/client.py`):fastmcp Client 的包装,处理连接、重连、消息路由 - **`fastmcp.Client`**:底层 MCP 客户端,负责 JSON-RPC 协议握手 ### ACP-transport MCP 当前状态 @@ -91,12 +91,12 @@ session/new 已实现的组件(RFC-0033): - **`AcpMcpConnectionManager`**:管理 `connectionId → AcpMcpConnection` 映射 - **`AcpMcpConnection`**:持有内存流和 `send_to_client` 回调 -- **`AcpMcpTransport`**(`agentpool_server/acp_server/acp_mcp_transport.py`):实现了 `fastmcp.ClientTransport` 接口,将内存流包装为 fastmcp 可用的传输层 +- **`AcpMcpTransport`**(`wolfharness_server/acp_server/acp_mcp_transport.py`):实现了 `fastmcp.ClientTransport` 接口,将内存流包装为 fastmcp 可用的传输层 未实现的组件: - **没有代码创建 fastmcp Client**:`AcpMcpTransport` 已经就绪,但没有任何地方调用 `fastmcp.Client(AcpMcpTransport(...))` - **没有 MCP initialize 握手**:即使创建了 Client,也需要进入 async context 才能触发 fastmcp 的自动初始化 -- **没有工具注册**:`tools/list` 的结果需要转换为 agentpool 的 `Tool` 对象并注册到 Agent 运行时 +- **没有工具注册**:`tools/list` 的结果需要转换为 wolfharness 的 `Tool` 对象并注册到 Agent 运行时 ### 现有代码的关键限制 @@ -401,7 +401,7 @@ ACPSession #### 1. MCPClient._get_client() 支持 ACP ```python -# agentpool/mcp_server/client.py +# wolfharness/mcp_server/client.py case AcpMCPServerConfig(acp_id=acp_id): # AcpMcpTransport 延迟初始化:在 connect() 时从 manager 获取连接 @@ -411,7 +411,7 @@ case AcpMCPServerConfig(acp_id=acp_id): #### 2. AcpMcpTransport 支持延迟初始化 ```python -# agentpool_server/acp_server/acp_mcp_transport.py +# wolfharness_server/acp_server/acp_mcp_transport.py class AcpMcpTransport(ClientTransport): def __init__(self, acp_id: str) -> None: @@ -428,7 +428,7 @@ class AcpMcpTransport(ClientTransport): #### 3. session.py 中 ACP server 的初始化流程 ```python -# agentpool_server/acp_server/session.py +# wolfharness_server/acp_server/session.py async def initialize_mcp_servers(self) -> None: for server in self.mcp_servers: @@ -565,12 +565,12 @@ async def initialize_mcp_servers(self) -> None: | 文件 | 说明 | |------|------| -| `agentpool/mcp_server/client.py` | MCPClient,需修改 `_get_client()` | -| `agentpool/resource_providers/mcp_provider.py` | MCPResourceProvider,工具注册 | -| `agentpool_server/acp_server/acp_mcp_transport.py` | AcpMcpTransport,需支持延迟初始化 | -| `agentpool_server/acp_server/acp_agent.py` | connect_acp_mcp_server() | -| `agentpool_server/acp_server/session.py` | initialize_mcp_servers() | -| `agentpool_server/acp_server/acp_mcp_manager.py` | AcpMcpConnectionManager | +| `wolfharness/mcp_server/client.py` | MCPClient,需修改 `_get_client()` | +| `wolfharness/resource_providers/mcp_provider.py` | MCPResourceProvider,工具注册 | +| `wolfharness_server/acp_server/acp_mcp_transport.py` | AcpMcpTransport,需支持延迟初始化 | +| `wolfharness_server/acp_server/acp_agent.py` | connect_acp_mcp_server() | +| `wolfharness_server/acp_server/session.py` | initialize_mcp_servers() | +| `wolfharness_server/acp_server/acp_mcp_manager.py` | AcpMcpConnectionManager | ### External Resources diff --git a/docs/rfcs/draft/RFC-0036-mcp-over-acp-test-coverage.md b/docs/rfcs/draft/RFC-0036-mcp-over-acp-test-coverage.md index 3ea133676..fd352ec1f 100644 --- a/docs/rfcs/draft/RFC-0036-mcp-over-acp-test-coverage.md +++ b/docs/rfcs/draft/RFC-0036-mcp-over-acp-test-coverage.md @@ -264,7 +264,7 @@ MCP-over-ACP 的测试覆盖存在系统性缺口。当前测试主要覆盖: **Description** 启动完整的 AgentPool ACP server 和 mock ACP client 进程,通过真实 ACP 协议通信: -- 使用 `agentpool serve-acp` 启动 server +- 使用 `wolfharness serve-acp` 启动 server - 使用 Python subprocess 启动 mock client(通过 stdio 或 WebSocket 连接) - 发送真实的 JSON-RPC 请求,验证响应 @@ -693,13 +693,13 @@ class MockACPClientWithoutSendRequest: | 文件 | 说明 | |------|------| -| `tests/agentpool_server/acp_server/test_acp_mcp_manager.py` | 现有 manager 测试 | -| `tests/agentpool_server/acp_server/test_acp_mcp_transport.py` | 现有 transport 测试 | +| `tests/wolfharness_server/acp_server/test_acp_mcp_manager.py` | 现有 manager 测试 | +| `tests/wolfharness_server/acp_server/test_acp_mcp_transport.py` | 现有 transport 测试 | | `tests/servers/acp_server/test_mcp_integration.py` | 现有 MCP 集成测试 | -| `src/agentpool_server/acp_server/acp_mcp_transport.py` | Transport 实现 | -| `src/agentpool_server/acp_server/acp_mcp_manager.py` | Manager 实现 | -| `src/agentpool/mcp_server/client.py` | MCPClient | -| `src/agentpool/resource_providers/mcp_provider.py` | MCPResourceProvider | +| `src/wolfharness_server/acp_server/acp_mcp_transport.py` | Transport 实现 | +| `src/wolfharness_server/acp_server/acp_mcp_manager.py` | Manager 实现 | +| `src/wolfharness/mcp_server/client.py` | MCPClient | +| `src/wolfharness/resource_providers/mcp_provider.py` | MCPResourceProvider | ### External Resources diff --git a/docs/rfcs/draft/RFC-0038-eliminate-pool-level-agents.md b/docs/rfcs/draft/RFC-0038-eliminate-pool-level-agents.md index fc3ac9a56..3813d734a 100644 --- a/docs/rfcs/draft/RFC-0038-eliminate-pool-level-agents.md +++ b/docs/rfcs/draft/RFC-0038-eliminate-pool-level-agents.md @@ -61,7 +61,7 @@ AgentPool does not leverage these capabilities. It calls `cfg.get_agent()` eager ### Historical Precedent in AgentPool -- `2026-06-17-thin-agentpool-core` (archived OpenSpec change): Already thinned agent types from 5 → 2 (native + acp), removing ~16K LOC. The `file` agent type was deliberately kept as a "config-only" mechanism — proving this pattern is viable. +- `2026-06-17-thin-wolfharness-core` (archived OpenSpec change): Already thinned agent types from 5 → 2 (native + acp), removing ~16K LOC. The `file` agent type was deliberately kept as a "config-only" mechanism — proving this pattern is viable. - `2026-06-03-thin-pydantic-ai-wrappers` (archived): Established the "Complement, Don't Wrap" vision. Event stream thinning was deferred (Phase 2g, never implemented). - `refactor-skills-as-capabilities` (active OpenSpec change): Already implements lazy MCP server connections — servers connect on first tool call, not activation. @@ -469,7 +469,7 @@ Each phase is independently revertible via git. Phase 1 can ship alone (adds API 3. **What about programmatic `Agent` construction (not from config)?** - `AgentPool.__init__()` currently supports `manifest=None` (empty config) and programmatic `add_agent(agent_instance)`. The programmatic path should remain for direct agent construction use cases. -4. **Does this affect `agentpool run "prompt"` CLI?** +4. **Does this affect `wolfharness run "prompt"` CLI?** - Yes. The CLI currently calls `pool.get_agent(name).run(prompt)`. It should instead create a session via SessionPool or directly call `cfg.get_agent().run(prompt)`. 5. **Should `AgentPool` still extend `BaseRegistry[NodeName, MessageNode]`?** diff --git a/docs/rfcs/draft/RFC-0039-acp-subagent-zed-protocol-upgrade.md b/docs/rfcs/draft/RFC-0039-acp-subagent-zed-protocol-upgrade.md index 875c414e7..1a2a8dc38 100644 --- a/docs/rfcs/draft/RFC-0039-acp-subagent-zed-protocol-upgrade.md +++ b/docs/rfcs/draft/RFC-0039-acp-subagent-zed-protocol-upgrade.md @@ -992,17 +992,17 @@ Phase 2 和 Phase 3 可并行执行,均依赖 Phase 1。 ### AgentPool 源码 -- `packages/agentpool/src/agentpool_server/acp_server/event_converter.py` — 核心 ACP 事件转换器(723 行,活跃) -- `packages/agentpool/src/agentpool_server/acp_server/handler.py` — 协议处理器(521 行) -- `packages/agentpool/src/agentpool_server/mixins.py` — `ProtocolEventConsumerMixin`(258 行,`_consumer_done_events` 在 line 60,`_consumer_task_refs` 在 line 61,`_after_consumer_loop` 在 line 91-100) -- `packages/agentpool/src/agentpool/agents/context.py` — `AgentRunContext.create_child_session()`(line 231-276) -- `packages/agentpool/src/agentpool_server/acp_server/session.py` — ACP session 管理(929 行) -- `packages/agentpool/src/acp/schema/tool_call.py` — `SubagentRunInfo` 定义 -- `packages/agentpool/src/acp/schema/base.py` — `AnnotatedObject` 基类,`field_meta` 字段 -- `packages/agentpool_toolsets/builtin/subagent_tools.py` — subagent tool 实现(3 处手动 SpawnSessionStart 之一) -- `packages/agentpool_toolsets/builtin/workers.py` — worker tool 实现(3 处之二) -- `packages/agentpool/src/agentpool/delegation/team.py` — team 实现(使用 yield 模式,不受 auto-emit 影响) -- `packages/agentpool/src/agentpool/delegation/teamrun.py` — sequential team 实现(同 team.py,使用 yield 模式) +- `packages/wolfharness/src/wolfharness_server/acp_server/event_converter.py` — 核心 ACP 事件转换器(723 行,活跃) +- `packages/wolfharness/src/wolfharness_server/acp_server/handler.py` — 协议处理器(521 行) +- `packages/wolfharness/src/wolfharness_server/mixins.py` — `ProtocolEventConsumerMixin`(258 行,`_consumer_done_events` 在 line 60,`_consumer_task_refs` 在 line 61,`_after_consumer_loop` 在 line 91-100) +- `packages/wolfharness/src/wolfharness/agents/context.py` — `AgentRunContext.create_child_session()`(line 231-276) +- `packages/wolfharness/src/wolfharness_server/acp_server/session.py` — ACP session 管理(929 行) +- `packages/wolfharness/src/acp/schema/tool_call.py` — `SubagentRunInfo` 定义 +- `packages/wolfharness/src/acp/schema/base.py` — `AnnotatedObject` 基类,`field_meta` 字段 +- `packages/wolfharness_toolsets/builtin/subagent_tools.py` — subagent tool 实现(3 处手动 SpawnSessionStart 之一) +- `packages/wolfharness_toolsets/builtin/workers.py` — worker tool 实现(3 处之二) +- `packages/wolfharness/src/wolfharness/delegation/team.py` — team 实现(使用 yield 模式,不受 auto-emit 影响) +- `packages/wolfharness/src/wolfharness/delegation/teamrun.py` — sequential team 实现(同 team.py,使用 yield 模式) ### 相关 RFC diff --git a/docs/rfcs/draft/RFC-0041-loop-run-separation.md b/docs/rfcs/draft/RFC-0041-loop-run-separation.md index 9cacd9057..094eb11af 100644 --- a/docs/rfcs/draft/RFC-0041-loop-run-separation.md +++ b/docs/rfcs/draft/RFC-0041-loop-run-separation.md @@ -104,7 +104,7 @@ pydantic-ai (installed at `.venv/lib/python3.13/site-packages/pydantic_ai/`) pro **Critical iteration requirement**: `AgentRun.next(node)` must be used instead of bare `async for node in agent_run` — the latter skips all capability hooks including `PendingMessageDrainCapability`'s drain logic. The current `RunExecutor` already uses `next(node)` correctly. -**Note on naming**: pydantic-ai's `AgentRun` is an internal implementation detail of a single reactive cycle. In this RFC, we call the reactive cycle a **Turn** and use `agent_run` as a local variable name inside `NativeTurn.execute()`. There is no naming conflict because `AgentRun` is never exposed to agentpool users. +**Note on naming**: pydantic-ai's `AgentRun` is an internal implementation detail of a single reactive cycle. In this RFC, we call the reactive cycle a **Turn** and use `agent_run` as a local variable name inside `NativeTurn.execute()`. There is no naming conflict because `AgentRun` is never exposed to wolfharness users. ### anyio Primitives Available @@ -121,7 +121,7 @@ pydantic-ai (installed at `.venv/lib/python3.13/site-packages/pydantic_ai/`) pro - **RFC-0029** (2026-04-26): Introduced `inject_prompt()`/`queue_prompt()` with `asyncio.Event` notification for agent reactivation between `run_stream()` calls. This was the first attempt at "idle" semantics — the agent provides the signal, the caller provides the reactivation loop. - **RFC-0037** (2026-06-15): Proposed unifying `steer()`/`followup()` by mapping to pydantic-ai's `enqueue()` for native agents. Recognized the dual-system redundancy but kept the per-RunHandle lifecycle. - **`introduce-anyio-structured-concurrency`** OpenSpec change (completed): Established the CancelScope hierarchy (pool→session→agent run→subagent run). `RunHandle._cleanup_run()` sets `complete_event` in `anyio.CancelScope(shield=True)`. -- **ACP v2 Prompt Lifecycle RFD** (by @benbrandt): Proposes `session/prompt` returning on accept (fire-and-forget), with turn lifecycle communicated via `state_change` notifications. **PR #1261** (`session/inject` by @kennethsinder) adds `mode: "queue" | "steer"` — directly mapping to agentpool's `followup()` / `steer()`. +- **ACP v2 Prompt Lifecycle RFD** (by @benbrandt): Proposes `session/prompt` returning on accept (fire-and-forget), with turn lifecycle communicated via `state_change` notifications. **PR #1261** (`session/inject` by @kennethsinder) adds `mode: "queue" | "steer"` — directly mapping to wolfharness's `followup()` / `steer()`. ### Glossary @@ -351,7 +351,7 @@ The agent run never exits `async with agent.iter()` — it loops within the grap - Idle lives entirely within pydantic-ai's graph model — all capability hooks fire correctly - `after_node_run` redirect is an established pattern (`PendingMessageDrainCapability` already uses it) -- No agentpool-level Run concept needed — the graph itself persists +- No wolfharness-level Run concept needed — the graph itself persists **Disadvantages** @@ -966,7 +966,7 @@ class NativeTurn(Turn): | Method | Source / Implementation | |--------|------------------------| | `self._agent._build_deps(run_ctx)` | Wraps existing `self._agent.get_context(input_provider=..., run_ctx=run_ctx)` — to be extracted as a method on `NativeAgent` during Phase 1 implementation | -| `self._agent._is_terminal_tool(tool_name)` | Delegates to `agentpool.tools.base.is_terminal_tool()` — to be wrapped as a method on `NativeAgent` during Phase 1 | +| `self._agent._is_terminal_tool(tool_name)` | Delegates to `wolfharness.tools.base.is_terminal_tool()` — to be wrapped as a method on `NativeAgent` during Phase 1 | | `ChatMessage.from_model_message(last)` | Constructs `ChatMessage` from a `ModelMessage` — to be implemented as a classmethod during Phase 1 (current code uses `ChatMessage.from_run_result()` at run_executor.py L330; this is a simplification) | | `convert_acp_to_model_messages(acp_messages)` | Converts ACP message format to `list[ModelMessage]` — to be implemented in `agents/acp_agent/` during Phase 2 | | `ACPTurn._map_acp_event(event)` | Maps ACP stream events to `RichAgentStreamEvent` — to be implemented in Phase 2, delegates to `EventMapper` where event types overlap | diff --git a/docs/rfcs/draft/RFC-0042-unified-lifecycle-architecture.md b/docs/rfcs/draft/RFC-0042-unified-lifecycle-architecture.md index e5c774f61..7df9931c1 100644 --- a/docs/rfcs/draft/RFC-0042-unified-lifecycle-architecture.md +++ b/docs/rfcs/draft/RFC-0042-unified-lifecycle-architecture.md @@ -204,7 +204,7 @@ AgentPool has 5 different entry points for agent execution, each with its own li | `agent.run_stream()` | `agents/base_agent.py` | Same, but yields events | | `SessionController.receive_request()` | `orchestrator/session_controller.py` | `RunHandle` (RFC-0041) | | `BackgroundTaskProvider` | `tool_impls/workers/` | Manual task management | -| `WatchCommand` | `agentpool_commands/` | CLI-managed loop | +| `WatchCommand` | `wolfharness_commands/` | CLI-managed loop | No shared abstraction connects them. Adding a new execution mode (e.g., channel gateway) requires building an entirely new entry point. @@ -492,8 +492,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, Protocol, runtime_checkable if TYPE_CHECKING: - from agentpool.orchestrator.turn import Turn - from agentpool.agents.events import RichAgentStreamEvent + from wolfharness.orchestrator.turn import Turn + from wolfharness.agents.events import RichAgentStreamEvent class RunLoop: """Core execution loop. Built on RFC-0041's restructured RunHandle. @@ -1622,14 +1622,14 @@ class MessageQueueTransport(EventTransport): type: message_queue backend: redis_streams # or nats_jetstream, kafka url: "redis://localhost:6379" - topic_prefix: "agentpool" + topic_prefix: "wolfharness" """ def __init__( self, backend: str, # "redis_streams" | "nats_jetstream" | "kafka" url: str, - topic_prefix: str = "agentpool", + topic_prefix: str = "wolfharness", ) -> None: self._backend = self._create_backend(backend, url) ... diff --git a/docs/rfcs/draft/RFC-0050-agentwolf-v1-foundation-architecture.md b/docs/rfcs/draft/RFC-0050-agentwolf-v1-foundation-architecture.md index 980eac2af..83704e2b6 100644 --- a/docs/rfcs/draft/RFC-0050-agentwolf-v1-foundation-architecture.md +++ b/docs/rfcs/draft/RFC-0050-agentwolf-v1-foundation-architecture.md @@ -60,14 +60,14 @@ The codebase has 9 top-level packages under `src/`: | Package | Role | |---------|------| -| `agentpool/` | Core agent framework (agents, delegation, messaging, tools, models, prompts, storage, hooks, observability) | -| `agentpool_config/` | YAML schema definitions (config models, separated for clean imports) | -| `agentpool_server/` | Protocol servers (ACP, OpenCode, AG-UI, OpenAI API, MCP) | -| `agentpool_toolsets/` | Reusable toolset implementations (builtin, MCP discovery, specialized) | -| `agentpool_storage/` | Storage providers (SQL, Zed, Claude, OpenCode) | -| `agentpool_cli/` | Command-line interface | -| `agentpool_commands/` | Command implementations | -| `agentpool_prompts/` | Prompt templates | +| `wolfharness/` | Core agent framework (agents, delegation, messaging, tools, models, prompts, storage, hooks, observability) | +| `wolfharness_config/` | YAML schema definitions (config models, separated for clean imports) | +| `wolfharness_server/` | Protocol servers (ACP, OpenCode, AG-UI, OpenAI API, MCP) | +| `wolfharness_toolsets/` | Reusable toolset implementations (builtin, MCP discovery, specialized) | +| `wolfharness_storage/` | Storage providers (SQL, Zed, Claude, OpenCode) | +| `wolfharness_cli/` | Command-line interface | +| `wolfharness_commands/` | Command implementations | +| `wolfharness_prompts/` | Prompt templates | | `acp/` | ACP protocol implementation (client, agent, schema, bridge, transports) | ### Cross-Framework Research Summary @@ -476,7 +476,7 @@ host: storage: provider: sql - database: "sqlite:///agentpool.db" + database: "sqlite:///wolfharness.db" observability: logfire: {enabled: true} @@ -2170,7 +2170,7 @@ Each phase is independently deployable: 3. **RunScope propagation**: Should RunScope be explicitly passed to every method, or stored in a context variable (contextvars)? (Recommendation: context variable for ergonomics, explicitly passed at layer boundaries.) -4. **Package naming**: Should v1.0 use `agentwolf_*` or keep `agentpool_*` package names? (Recommendation: keep `agentpool_*` for backward compatibility; `agentwolf` is an internal codename.) +4. **Package naming**: Should v1.0 use `agentwolf_*` or keep `wolfharness_*` package names? (Recommendation: keep `wolfharness_*` for backward compatibility; `agentwolf` is an internal codename.) 5. **AgentFactory pre-compilation**: Should teams/graphs always be pre-compiled, or allow lazy compilation for dynamic topologies? (Recommendation: pre-compile by default, `factory_mode: lazy` opt-in for dynamic use cases.) diff --git a/docs/rfcs/draft/RFC-0052-restore-skill-capabilities.md b/docs/rfcs/draft/RFC-0052-restore-skill-capabilities.md index 6d458985a..11364ee69 100644 --- a/docs/rfcs/draft/RFC-0052-restore-skill-capabilities.md +++ b/docs/rfcs/draft/RFC-0052-restore-skill-capabilities.md @@ -62,7 +62,7 @@ Each step's spec approval was siloed. The top-level `specs/` directory was updat ### Evidence of Problem -1. **ACP server logs** (`~/Library/Logs/agentpool/acp.log`): Agent errors with `'FunctionTool' object has no attribute '__name__'` when trying to load skills. +1. **ACP server logs** (`~/Library/Logs/wolfharness/acp.log`): Agent errors with `'FunctionTool' object has no attribute '__name__'` when trying to load skills. 2. **User report**: Agent cannot access `load_skill` tool or resolve `skill://` URIs after M3 refactor. diff --git a/docs/rfcs/draft/RFC-0053-pre-m4-protocol-debt-cleanup.md b/docs/rfcs/draft/RFC-0053-pre-m4-protocol-debt-cleanup.md index ab0be7bda..4f331de17 100644 --- a/docs/rfcs/draft/RFC-0053-pre-m4-protocol-debt-cleanup.md +++ b/docs/rfcs/draft/RFC-0053-pre-m4-protocol-debt-cleanup.md @@ -112,8 +112,8 @@ graph TD ``` - `grep -rn 'hooks_fired' src/` → 21 results across 4 files -- `grep -rn '# type: ignore' src/agentpool/orchestrator/run.py` → 8 results -- `grep -rn 'state\.pool\.' src/agentpool_server/opencode_server/` → 68 results +- `grep -rn '# type: ignore' src/wolfharness/orchestrator/run.py` → 8 results +- `grep -rn 'state\.pool\.' src/wolfharness_server/opencode_server/` → 68 results - `grep -rn '_mcp_snapshot\|_session_connection_pool' src/` → results in agent.py + session.py - `RunHandle.start()` → ~373 SLOC with `# noqa: PLR0915` @@ -147,7 +147,7 @@ graph TD ### Non-Goals (Out of Scope — Deferred) -6. AgentWolf rename (`agentpool` → `agentwolf`) — deferred to final phase +6. AgentWolf rename (`wolfharness` → `agentwolf`) — deferred to final phase 7. M4 implementation itself (ConfigRegistry, HostRegistry, RunScope routing) 8. ACP protocol version switch or proxy chain refactor 9. `session_pool_integration.py` file split (1,453 LOC) — optional, can defer @@ -156,11 +156,11 @@ graph TD ### Success Criteria - [ ] `grep -rn 'hooks_fired' src/` returns 0 -- [ ] `grep -rn 'type: ignore\[attr-defined\]' src/agentpool/orchestrator/run.py` returns 0 +- [ ] `grep -rn 'type: ignore\[attr-defined\]' src/wolfharness/orchestrator/run.py` returns 0 - [ ] `grep -rn '_mcp_snapshot\|_session_connection_pool' src/` returns 0 - [ ] `grep -rn 'RunStatus' src/` returns 0 - [ ] `grep -rn 'host_context.pool' src/` returns 0 -- [ ] `grep -rn '_replaying' src/agentpool/orchestrator/run.py` returns 0 +- [ ] `grep -rn '_replaying' src/wolfharness/orchestrator/run.py` returns 0 - [ ] `uv run pytest` — all tests pass - [ ] `uv run ruff check src/` — no new lint errors - [ ] `uv run --no-group docs mypy src/` — no new type errors @@ -869,7 +869,7 @@ TBD ### External Resources -- [PR #144: refactor/agentwolf v1](https://github.com/Leoyzen/agentpool/pull/144) +- [PR #144: refactor/agentwolf v1](https://github.com/Leoyzen/wolfharness/pull/144) ### Appendix diff --git a/docs/rfcs/draft/RFC-0055-dynamic-team-mode.md b/docs/rfcs/draft/RFC-0055-dynamic-team-mode.md index 8f7360811..7b1ffe174 100644 --- a/docs/rfcs/draft/RFC-0055-dynamic-team-mode.md +++ b/docs/rfcs/draft/RFC-0055-dynamic-team-mode.md @@ -121,7 +121,7 @@ This workflow cannot be expressed as a static DAG because: ### Evidence -- GitHub Discussion #160 (Leoyzen/agentpool) documents the architectural proposal and received community feedback +- GitHub Discussion #160 (Leoyzen/wolfharness) documents the architectural proposal and received community feedback - The comparative survey (`docs/survey/multi-agent-orchestration/`) identified LLM-driven team creation as the dominant pattern in production frameworks (Qwen Code, OMO) - Three real-world scenarios (industrial diagnosis, manual translation, sales assistant) were analyzed and all require dynamic team composition @@ -490,8 +490,8 @@ graph TB # capabilities/team_comm_capability.py from __future__ import annotations from collections.abc import Sequence -from agentpool.capabilities.abstract_capability import AbstractCapability -from agentpool.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.capabilities.abstract_capability import AbstractCapability +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability class TeamCommCapability(FunctionToolsetCapability[AgentDepsT]): """Exposes team communication tools to agents. @@ -1037,7 +1037,7 @@ team_mode: lead_eligible: [orchestrator] # Storage (tempdir-based, not .omo/) - base_dir: /tmp/agentpool/teams + base_dir: /tmp/wolfharness/teams ttl_hours: 72 # Bounds (guardrails for LLM-driven team creation) @@ -1251,8 +1251,8 @@ No specific regulatory requirements. Team state is ephemeral coordination data, - **Scope**: FileTeamState + TeamModeConfig - **Deliverables**: - `capabilities/file_team_state.py` — File I/O for inbox/tasks/blackboard - - `agentpool_config/team_mode.py` — Config model - - `agentpool_config/manifest.py` — +1 field (team_mode) + - `wolfharness_config/team_mode.py` — Config model + - `wolfharness_config/manifest.py` — +1 field (team_mode) - TTL cleanup background task - **Dependencies**: None - **Testing**: Unit test file operations, concurrent access, TTL cleanup @@ -1413,8 +1413,8 @@ None. All reviewers converged on Option 2 after analysis of the BT precedent and - Multi-Agent Orchestration Survey - Survey: Comparison Matrix - Survey: Recommendations -- [GitHub Discussion #160: Team Agent 架构设计](https://github.com/Leoyzen/agentpool/discussions/160) -- [Qwen Code Deep Dive (Discussion #160 comment)](https://github.com/Leoyzen/agentpool/discussions/160#discussioncomment-17637183) +- [GitHub Discussion #160: Team Agent 架构设计](https://github.com/Leoyzen/wolfharness/discussions/160) +- [Qwen Code Deep Dive (Discussion #160 comment)](https://github.com/Leoyzen/wolfharness/discussions/160#discussioncomment-17637183) - [RFC-0042: Unified Lifecycle Architecture](./RFC-0042-unified-lifecycle-architecture.md) - [RFC-0037: Unify Steer and Followup](./RFC-0037-unify-steer-followup.md) @@ -1450,10 +1450,10 @@ None. All reviewers converged on Option 2 after analysis of the BT precedent and |------|------|-----------| | `capabilities/team_comm_capability.py` | New — Capability (direct SessionPool access + defaults hook) | ~380 | | `capabilities/file_team_state.py` | New — File I/O | ~250 | -| `agentpool_config/team_mode.py` | New — Config model | ~80 | +| `wolfharness_config/team_mode.py` | New — Config model | ~80 | | `capabilities/agent_context.py` | Modified — +1 field (team_mode_config) | ~1 | | `orchestrator/run.py` | Modified — +1 line (team_mode_config injection) | ~1 | -| `agentpool_config/manifest.py` | Modified — +1 field (team_mode) | ~2 | +| `wolfharness_config/manifest.py` | Modified — +1 field (team_mode) | ~2 | | **Total new** | 3 files | ~680 | | **Total modified** | 3 files | ~4 lines | diff --git a/docs/rfcs/implemented/RFC-0001-workers-teams-session-management.md b/docs/rfcs/implemented/RFC-0001-workers-teams-session-management.md index 8a1c559cd..a862802af 100644 --- a/docs/rfcs/implemented/RFC-0001-workers-teams-session-management.md +++ b/docs/rfcs/implemented/RFC-0001-workers-teams-session-management.md @@ -652,12 +652,12 @@ If issues are discovered: ### Related Documents -- Subagent implementation: `src/agentpool_toolsets/builtin/subagent_tools.py` -- Workers implementation: `src/agentpool_toolsets/builtin/workers.py` -- Team implementation: `src/agentpool/delegation/team.py` -- TeamRun implementation: `src/agentpool/delegation/teamrun.py` -- Event definitions: `src/agentpool/agents/events/events.py` -- Session ID generation: `src/agentpool/utils/identifiers.py` +- Subagent implementation: `src/wolfharness_toolsets/builtin/subagent_tools.py` +- Workers implementation: `src/wolfharness_toolsets/builtin/workers.py` +- Team implementation: `src/wolfharness/delegation/team.py` +- TeamRun implementation: `src/wolfharness/delegation/teamrun.py` +- Event definitions: `src/wolfharness/agents/events/events.py` +- Session ID generation: `src/wolfharness/utils/identifiers.py` ### External Resources diff --git a/docs/rfcs/implemented/RFC-0002-extended-tool-definition.md b/docs/rfcs/implemented/RFC-0002-extended-tool-definition.md index 6005c177b..21e377432 100644 --- a/docs/rfcs/implemented/RFC-0002-extended-tool-definition.md +++ b/docs/rfcs/implemented/RFC-0002-extended-tool-definition.md @@ -9,7 +9,7 @@ last_updated: 2026-02-02 --- ## Overview -This RFC details the implementation of extended `Tool` definitions in `agentpool` to support features from `pydantic-ai`'s `Tool` class—specifically `prepare`, `function_schema`, `name`, and `description`—and the unification of tool conversion logic using `Tool.from_schema` for enhanced validation capabilities. +This RFC details the implementation of extended `Tool` definitions in `wolfharness` to support features from `pydantic-ai`'s `Tool` class—specifically `prepare`, `function_schema`, `name`, and `description`—and the unification of tool conversion logic using `Tool.from_schema` for enhanced validation capabilities. ## Background & Context AgentPool's `Tool` abstraction serves as a bridge between multiple protocols. Previously, conversion to `pydantic-ai` tools was fragmented: @@ -20,7 +20,7 @@ AgentPool's `Tool` abstraction serves as a bridge between multiple protocols. Pr ## Problem Statement 1. **Validation Gap**: The previous `SchemaWrapper` implementation for schema overrides did not support `validate_json`, causing runtime errors when PydanticAI attempted to validate tool arguments from JSON. 2. **Divergent Paths**: Tools with custom schemas used a different code path than standard tools, leading to feature disparity (e.g., missing `prepare` support). -3. **Context Type Hazard**: `pydantic-ai` expects `RunContext`, while `agentpool` internal tools often depend on `AgentContext`. Using `pydantic_ai.function_schema` on functions with `AgentContext` failed due to type inspection issues with abstract base classes. +3. **Context Type Hazard**: `pydantic-ai` expects `RunContext`, while `wolfharness` internal tools often depend on `AgentContext`. Using `pydantic_ai.function_schema` on functions with `AgentContext` failed due to type inspection issues with abstract base classes. ## Implementation Details diff --git a/docs/rfcs/implemented/RFC-0003-pydantic-ai-history-processors-integration.md b/docs/rfcs/implemented/RFC-0003-pydantic-ai-history-processors-integration.md index 26929fa6c..21e464126 100644 --- a/docs/rfcs/implemented/RFC-0003-pydantic-ai-history-processors-integration.md +++ b/docs/rfcs/implemented/RFC-0003-pydantic-ai-history-processors-integration.md @@ -17,9 +17,9 @@ related_rfcs: [RFC-0002] ## Overview -This RFC proposes adding support for pydantic-ai's `history_processors` mechanism to agentpool's native agent configuration. History processors are callables that transform message history before it's sent to the model, enabling advanced conversation management patterns like context-aware filtering, summarization, token budget management, and custom message selection logic. +This RFC proposes adding support for pydantic-ai's `history_processors` mechanism to wolfharness's native agent configuration. History processors are callables that transform message history before it's sent to the model, enabling advanced conversation management patterns like context-aware filtering, summarization, token budget management, and custom message selection logic. -The integration aims to provide a clean pass-through to pydantic-ai's native history processing capabilities with minimal code changes, allowing users to leverage the full power of pydantic-ai's history processor ecosystem directly from agentpool's YAML configuration. +The integration aims to provide a clean pass-through to pydantic-ai's native history processing capabilities with minimal code changes, allowing users to leverage the full power of pydantic-ai's history processor ecosystem directly from wolfharness's YAML configuration. ## Table of Contents @@ -43,21 +43,21 @@ The integration aims to provide a clean pass-through to pydantic-ai's native his ### Current State -**agentpool's History Management:** +**wolfharness's History Management:** AgentPool currently manages conversation history through: -1. **MessageHistory** (`src/agentpool/messaging/message_history.py`): +1. **MessageHistory** (`src/wolfharness/messaging/message_history.py`): - Stores conversation state in `chat_messages: ChatMessageList` - Applies `MemoryConfig` limits (`max_tokens`, `max_messages`) - Loads history from storage via `SessionQuery` -2. **CompactionPipeline** (`src/agentpool/messaging/compaction.py`): +2. **CompactionPipeline** (`src/wolfharness/messaging/compaction.py`): - Pipeline-based system with predefined steps (FilterThinking, TruncateToolOutputs, KeepLastMessages, etc.) - Configurable via YAML with presets (`minimal`, `balanced`, `summarizing`) - Applied during message history preparation -3. **NativeAgent** (`src/agentpool/agents/native_agent/agent.py`): +3. **NativeAgent** (`src/wolfharness/agents/native_agent/agent.py`): - Wraps pydantic-ai's `Agent` class - Creates `agentlet` via `get_agentlet()` which instantiates pydantic-ai Agent - Currently does NOT pass `history_processors` parameter to pydantic-ai Agent @@ -93,7 +93,7 @@ Key characteristics: - **RFC-0002** established the pattern for extending pydantic-ai features by passing through configuration parameters (e.g., `prepare` hooks, `function_schema`) - AgentPool's `CompactionPipeline` was designed before pydantic-ai had native history processors, leading to overlapping functionality -- Current agentpool users must write custom hooks or CompactionSteps to achieve what pydantic-ai's history processors can do natively +- Current wolfharness users must write custom hooks or CompactionSteps to achieve what pydantic-ai's history processors can do natively ### Glossary @@ -103,7 +103,7 @@ Key characteristics: | CompactionPipeline | AgentPool's internal message transformation system | | MessageHistory | AgentPool's conversation state manager | | RunContext | pydantic-ai's runtime context object providing access to deps, usage, model info | -| Agentlet | Internal pydantic-ai Agent instance created by agentpool's `get_agentlet()` | +| Agentlet | Internal pydantic-ai Agent instance created by wolfharness's `get_agentlet()` | --- @@ -147,7 +147,7 @@ AgentPool lacks a native way to configure pydantic-ai's history processors, forc ### Non-Goals (Out of Scope) 1. Replacing or deprecating CompactionPipeline (it remains for existing users) -2. Creating agentpool-specific history processor abstractions (use pydantic-ai's directly) +2. Creating wolfharness-specific history processor abstractions (use pydantic-ai's directly) 3. **Inline code execution for history processors** - deferred to future RFC with security design 4. History processor validation beyond basic import path verification and callable check 5. Runtime debugging of history processor execution (rely on pydantic-ai's built-in logs) @@ -199,7 +199,7 @@ agents: **Implementation:** 1. Add `history_processors: list[str] | None` field to `MemoryConfig` -2. In `NativeAgent.get_agentlet()`, resolve import paths to callables using existing `import_callable()` from `agentpool.utils.importing` +2. In `NativeAgent.get_agentlet()`, resolve import paths to callables using existing `import_callable()` from `wolfharness.utils.importing` 3. Cache resolved processors on the agent instance to avoid repeated resolution 4. Pass processors to pydantic-ai Agent's `history_processors` parameter 5. Validate processors are callable at import time @@ -209,7 +209,7 @@ agents: - **Full pydantic-ai compatibility**: Supports all 4 signatures natively - **Type safe**: Uses pydantic-ai's type checking - **No reinvention**: Uses battle-tested pydantic-ai processor execution logic -- **Clean separation**: Processor logic lives in pydantic-ai, not agentpool +- **Clean separation**: Processor logic lives in pydantic-ai, not wolfharness - **Performance**: One-time import resolution per agent instance (cached) - **Security**: No inline code execution, only import paths @@ -480,7 +480,7 @@ async with agentlet.iter(prompts, message_history=[m for run in history_list for #### 1. MemoryConfig Extension -**Location**: `src/agentpool_config/session.py` +**Location**: `src/wolfharness_config/session.py` ```python class MemoryConfig(Schema): @@ -515,7 +515,7 @@ class MemoryConfig(Schema): #### 2. Processor Resolution and Caching -**Location**: `src/agentpool/agents/native_agent/agent.py` +**Location**: `src/wolfharness/agents/native_agent/agent.py` ```python class Agent[TDeps = None, OutputDataT = str](BaseAgent[TDeps, OutputDataT]): @@ -532,7 +532,7 @@ class Agent[TDeps = None, OutputDataT = str](BaseAgent[TDeps, OutputDataT]): input_provider: InputProvider | None, ) -> PydanticAgent[AgentContext[TDeps], AgentOutputType]: """Create pydantic-ai agent from current state.""" - from agentpool.utils.importing import import_callable + from wolfharness.utils.importing import import_callable # ... existing tool wrapping code ... @@ -733,7 +733,7 @@ The rollback is straightforward because changes are purely additive. ## Testing Requirements -### Unit Tests (`agentpool/tests/test_history_processors.py`) +### Unit Tests (`wolfharness/tests/test_history_processors.py`) **Configuration Validation** - [ ] Empty history_processors list is valid @@ -857,7 +857,7 @@ def context_aware_sync( - [pydantic-ai History Processors Guide](https://ai.pydantic.dev/history-processors/) - [pydantic-ai RunContext API](https://ai.pydantic.dev/run-context/) -- [AgentPool Configuration Docs](https://million-mo.github.io/agentpool/YAML%20Configuration/session_configuration/) +- [AgentPool Configuration Docs](https://million-mo.github.io/wolfharness/YAML%20Configuration/session_configuration/) ### Appendix diff --git a/docs/rfcs/implemented/RFC-0008-dynamic-skills-injection.md b/docs/rfcs/implemented/RFC-0008-dynamic-skills-injection.md index 35e24537d..95d18f2a8 100644 --- a/docs/rfcs/implemented/RFC-0008-dynamic-skills-injection.md +++ b/docs/rfcs/implemented/RFC-0008-dynamic-skills-injection.md @@ -47,7 +47,7 @@ This RFC proposes a new approach for automatic skills injection into agent syste AgentPool now has RFC-0007's dynamic instruction infrastructure: -1. **Instruction Function Types** (`src/agentpool/prompts/instructions.py`): +1. **Instruction Function Types** (`src/wolfharness/prompts/instructions.py`): ```python InstructionFunc = ( SimpleInstruction | AgentContextInstruction | @@ -55,7 +55,7 @@ AgentPool now has RFC-0007's dynamic instruction infrastructure: ) ``` -2. **ResourceProvider Extension** (`src/agentpool/resource_providers/base.py`): +2. **ResourceProvider Extension** (`src/wolfharness/resource_providers/base.py`): ```python class ResourceProvider: async def get_instructions(self) -> list[InstructionFunc]: @@ -63,7 +63,7 @@ AgentPool now has RFC-0007's dynamic instruction infrastructure: return [] ``` -3. **NativeAgent Integration** (`src/agentpool/agents/native_agent/agent.py`): +3. **NativeAgent Integration** (`src/wolfharness/agents/native_agent/agent.py`): ```python async def get_agentlet(self, ...): # Collect instructions from all providers @@ -79,7 +79,7 @@ AgentPool now has RFC-0007's dynamic instruction infrastructure: ) ``` -4. **Context Wrapping** (`src/agentpool/utils/context_wrapping.py`): +4. **Context Wrapping** (`src/wolfharness/utils/context_wrapping.py`): - `wrap_instruction()` adapts instruction functions to pydantic-ai's `(RunContext) -> str` signature - Automatically injects AgentContext and/or RunContext based on function signature @@ -90,7 +90,7 @@ RFC-0005 proposed **static skill injection** into agent system prompts through m #### RFC-0005 Skill.format_for_injection() ```python -# src/agentpool/skills/skill.py +# src/wolfharness/skills/skill.py class Skill: def format_for_injection( self, @@ -115,7 +115,7 @@ class Skill: self._load_content() return f"### {self.name}\n\n{self.description}\n\n{self._instructions}" -# src/agentpool/agents/sys_prompts.py +# src/wolfharness/agents/sys_prompts.py class SystemPrompts: def __init__( self, @@ -234,7 +234,7 @@ agents: **Description** -Create a dedicated `SkillsInstructionProvider` class (`src/agentpool/resource_providers/skills_instruction.py`) that implements RFC-0007's dynamic instruction mechanism. This approach: +Create a dedicated `SkillsInstructionProvider` class (`src/wolfharness/resource_providers/skills_instruction.py`) that implements RFC-0007's dynamic instruction mechanism. This approach: - Keeps concern separated from tool provision (`SkillsTools` remains unchanged). - Uses **structured XML format** for skill representation. - Supports runtime selection and formatting based on `AgentContext`. @@ -268,7 +268,7 @@ class SkillsConfig(BaseModel): ```python -# src/agentpool_config/toolsets.py (extend existing SkillsToolsConfig) +# src/wolfharness_config/toolsets.py (extend existing SkillsToolsConfig) class SkillsToolsConfig(ToolsetConfig): """Configuration for skills tools toolset. @@ -556,7 +556,7 @@ Option 1 is recommended because: #### 1. SkillsInstructionProvider (NEW) -**File**: `src/agentpool/resource_providers/skills_instruction.py` +**File**: `src/wolfharness/resource_providers/skills_instruction.py` Dedicated ResourceProvider for skills injection via RFC-0007's `get_instructions()`. Keeps concerns separated from SkillsTools. @@ -567,13 +567,13 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Literal, cast from xml.sax.saxutils import escape -from agentpool.agents.context import AgentContext -from agentpool.log import get_logger -from agentpool.resource_providers import ResourceProvider +from wolfharness.agents.context import AgentContext +from wolfharness.log import get_logger +from wolfharness.resource_providers import ResourceProvider if TYPE_CHECKING: - from agentpool.prompts.instructions import InstructionFunc - from agentpool.skills.registry import SkillsRegistry + from wolfharness.prompts.instructions import InstructionFunc + from wolfharness.skills.registry import SkillsRegistry logger = get_logger(__name__) @@ -712,7 +712,7 @@ class SkillsInstructionProvider(ResourceProvider): #### 2. SkillsTools (UNCHANGED) -**File**: `src/agentpool_toolsets/builtin/skills.py` +**File**: `src/wolfharness_toolsets/builtin/skills.py` Existing tools provider remains unchanged. Provides: - `load_skill` tool @@ -722,7 +722,7 @@ No modifications needed for RFC-0008. #### 2. Configuration Models -**File**: `src/agentpool_config/skills.py` +**File**: `src/wolfharness_config/skills.py` ```python class SkillsInstructionConfig(BaseModel): @@ -745,7 +745,7 @@ class SkillsConfig(BaseModel): #### 3. Toolset Configuration -**File**: `src/agentpool_config/toolsets.py` (extend) +**File**: `src/wolfharness_config/toolsets.py` (extend) ```python class SkillsToolsetConfig(ToolsetConfig): @@ -760,7 +760,7 @@ class SkillsToolsetConfig(ToolsetConfig): #### 4. AgentPool Integration -**File**: `src/agentpool/delegation/pool.py` (extend) +**File**: `src/wolfharness/delegation/pool.py` (extend) ```python class AgentPool: @@ -783,12 +783,12 @@ class AgentPool: ### Data Model Changes **New Files**: -- `src/agentpool/resource_providers/skills_instruction.py` - Dedicated instruction provider +- `src/wolfharness/resource_providers/skills_instruction.py` - Dedicated instruction provider - `tests/resource_providers/test_skills_instruction.py` - Tests for skills instruction provider **Modified Files**: -- `src/agentpool_config/skills.py` - Add `SkillsInstructionConfig` -- `src/agentpool/delegation/pool.py` - Integrate skills injection provider +- `src/wolfharness_config/skills.py` - Add `SkillsInstructionConfig` +- `src/wolfharness/delegation/pool.py` - Integrate skills injection provider ### Configuration Examples @@ -933,7 +933,7 @@ agents: - **Scope**: Create `SkillsInstructionProvider` - **Deliverables**: - - `src/agentpool/resource_providers/skills_instruction.py`: + - `src/wolfharness/resource_providers/skills_instruction.py`: - Implement `get_instructions()` returning dynamic XML generators - Implement XML formatting with proper character escaping - Unit tests for instruction generation logic @@ -942,8 +942,8 @@ agents: - **Scope**: Add configuration models - **Deliverables**: - - Update `agentpool_config/skills.py` with `SkillsInstructionConfig` - - Update `agentpool_config/toolsets.py` to support injection overrides + - Update `wolfharness_config/skills.py` with `SkillsInstructionConfig` + - Update `wolfharness_config/toolsets.py` to support injection overrides - Config validation tests ### Phase 3: AgentPool Integration (Day 2) @@ -999,8 +999,8 @@ agents: ## References - RFC-0007: Dynamic Instructions for Resource Providers -- `src/agentpool/resource_providers/base.py` -- `src/agentpool/prompts/instructions.py` +- `src/wolfharness/resource_providers/base.py` +- `src/wolfharness/prompts/instructions.py` **Consequences**: - Positive: Cleaner architecture, better extensibility - Positive: Aligns with RFC-0007 @@ -1014,6 +1014,6 @@ agents: - RFC-0005: Skills Injection into System Prompts (SUPERSEDED) - RFC-0007: Dynamic Instructions for Resource Providers -- `src/agentpool/prompts/instructions.py` -- `src/agentpool/resource_providers/base.py` -- `src/agentpool/utils/context_wrapping.py` +- `src/wolfharness/prompts/instructions.py` +- `src/wolfharness/resource_providers/base.py` +- `src/wolfharness/utils/context_wrapping.py` diff --git a/docs/rfcs/implemented/RFC-0014-spawn-session-events.md b/docs/rfcs/implemented/RFC-0014-spawn-session-events.md index ded0bc7af..8ebf57971 100644 --- a/docs/rfcs/implemented/RFC-0014-spawn-session-events.md +++ b/docs/rfcs/implemented/RFC-0014-spawn-session-events.md @@ -213,7 +213,7 @@ async def _process_subagent_event(self, subagent_event, ctx): ### Phase 1: Event Definition (0.5 day) -**File**: `src/agentpool/agents/events/events.py` +**File**: `src/wolfharness/agents/events/events.py` ```python @dataclass(kw_only=True) @@ -238,7 +238,7 @@ type RichAgentStreamEvent[OutputDataT] = ( ### Phase 2: Subagent Tools Integration (1 day) -**File**: `src/agentpool_toolsets/builtin/subagent_tools.py` +**File**: `src/wolfharness_toolsets/builtin/subagent_tools.py` Update `_stream_task()` function: @@ -307,7 +307,7 @@ async def _stream_task( ### Phase 3: Event Processor Update (2 days) -**File**: `src/agentpool_server/opencode_server/event_processor.py` +**File**: `src/wolfharness_server/opencode_server/event_processor.py` Add handler with duplicate session protection: @@ -445,5 +445,5 @@ if isinstance(event, SpawnSessionStart): ## References - [RFC-0013: Subagent Event Unification](./RFC-0013-subagent-event-unification.md) -- [AgentPool Events](./src/agentpool/agents/events/events.py) -- [Subagent Tools](./src/agentpool_toolsets/builtin/subagent_tools.py) +- [AgentPool Events](./src/wolfharness/agents/events/events.py) +- [Subagent Tools](./src/wolfharness_toolsets/builtin/subagent_tools.py) diff --git a/docs/rfcs/implemented/RFC-0020-mcp-skills-resources-provider.md b/docs/rfcs/implemented/RFC-0020-mcp-skills-resources-provider.md index 140b17011..929fde302 100644 --- a/docs/rfcs/implemented/RFC-0020-mcp-skills-resources-provider.md +++ b/docs/rfcs/implemented/RFC-0020-mcp-skills-resources-provider.md @@ -53,20 +53,20 @@ This RFC specifies the **MCP Skills Resources Provider Protocol** implementation ### Current State -**ResourceProvider Infrastructure** (`src/agentpool/resource_providers/`): +**ResourceProvider Infrastructure** (`src/wolfharness/resource_providers/`): - `ResourceProvider` base class with `get_skills()`, `get_skill_instructions()` methods - `skills_changed` signal for change notifications - `AggregatingResourceProvider` for combining multiple providers - `MCPResourceProvider` for MCP server integration (needs skill support) - **Gap**: No local filesystem skill provider; skills not integrated with ResourceProvider -**Skills System** (`src/agentpool/skills/`): +**Skills System** (`src/wolfharness/skills/`): - Skills are defined in `SKILL.md` files with YAML frontmatter - `SkillsRegistry` auto-discovers skills from directories - `SkillsInstructionProvider` injects skills into prompts (not a ResourceProvider) - **Gap**: Skills not exposed through ResourceProvider interface -**MCP Integration** (`src/agentpool/mcp_server/`): +**MCP Integration** (`src/wolfharness/mcp_server/`): - `MCPResourceProvider` exposes MCP tools, prompts, resources - MCP servers can expose skills in two ways: 1. **Via Prompts**: Traditional MCP prompts (e.g., `github-copilot/code-review`) @@ -276,7 +276,7 @@ This RFC specifies the **MCP Skills Resources Provider Protocol** implementation #### 1. Exception Hierarchy ```python -# src/agentpool/skills/exceptions.py +# src/wolfharness/skills/exceptions.py class SkillError(Exception): """Base exception for skill-related errors.""" @@ -317,7 +317,7 @@ class ProviderError(SkillError): #### 2. LocalResourceProvider (New) ```python -# src/agentpool/resource_providers/local.py +# src/wolfharness/resource_providers/local.py from __future__ import annotations @@ -327,12 +327,12 @@ from typing import TYPE_CHECKING, Self from upathtools import UPath -from agentpool.log import get_logger -from agentpool.resource_providers import ResourceChangeEvent, ResourceProvider -from agentpool.resource_providers.resource_info import ResourceInfo -from agentpool.skills.exceptions import ReferenceNotFoundError, SecurityError, SkillNotFoundError -from agentpool.skills.registry import SkillsRegistry -from agentpool.skills.skill import Skill +from wolfharness.log import get_logger +from wolfharness.resource_providers import ResourceChangeEvent, ResourceProvider +from wolfharness.resource_providers.resource_info import ResourceInfo +from wolfharness.skills.exceptions import ReferenceNotFoundError, SecurityError, SkillNotFoundError +from wolfharness.skills.registry import SkillsRegistry +from wolfharness.skills.skill import Skill if TYPE_CHECKING: from types import TracebackType @@ -582,7 +582,7 @@ MCP servers can expose skills in two ways: 2. **Via Resources (Skill Protocol)**: Using `skill://` URI scheme with SKILL.md files ```python -# src/agentpool/resource_providers/mcp_provider.py (extension) +# src/wolfharness/resource_providers/mcp_provider.py (extension) # Add to existing MCPResourceProvider class: @@ -610,7 +610,7 @@ async def get_skills(self) -> list[Skill]: async def _get_prompt_skills(self) -> list[Skill]: """Get MCP prompts mapped to skills.""" - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill prompts = await self.get_prompts() skills: list[Skill] = [] @@ -660,7 +660,7 @@ async def _get_resource_skills(self) -> list[Skill]: See: https://gofastmcp.com/servers/providers/skills """ - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill resources = await self.get_resources() skills: list[Skill] = [] @@ -947,7 +947,7 @@ async def read_reference(self, skill_name: str, ref_path: str) -> str: #### 4. Skill URI Resolver ```python -# src/agentpool/skills/uri_resolver.py +# src/wolfharness/skills/uri_resolver.py from __future__ import annotations @@ -955,10 +955,10 @@ from dataclasses import dataclass from typing import TYPE_CHECKING from urllib.parse import unquote -from agentpool.skills.exceptions import SkillNotFoundError, SecurityError +from wolfharness.skills.exceptions import SkillNotFoundError, SecurityError if TYPE_CHECKING: - from agentpool.resource_providers import AggregatingResourceProvider + from wolfharness.resource_providers import AggregatingResourceProvider @dataclass(frozen=True) @@ -1302,15 +1302,15 @@ class ResolvedSkill: #### 5. Updated load_skill Tool ```python -# src/agentpool_toolsets/builtin/skills.py +# src/wolfharness_toolsets/builtin/skills.py from __future__ import annotations from typing import Literal -from agentpool.agents.context import AgentContext -from agentpool.resource_providers import StaticResourceProvider -from agentpool.skills.exceptions import SkillNotFoundError, SecurityError, ReferenceNotFoundError +from wolfharness.agents.context import AgentContext +from wolfharness.resource_providers import StaticResourceProvider +from wolfharness.skills.exceptions import SkillNotFoundError, SecurityError, ReferenceNotFoundError SKILL_USAGE_GUIDANCE = """ @@ -1390,7 +1390,7 @@ async def load_skill( Returns: The skill instructions or reference content """ - from agentpool.log import get_logger + from wolfharness.log import get_logger logger = get_logger(__name__) @@ -1521,7 +1521,7 @@ class SkillsTools(StaticResourceProvider): ### Integration with AgentPool ```python -# src/agentpool/delegation/pool.py +# src/wolfharness/delegation/pool.py class AgentPool: """Updated AgentPool with unified skill access via ResourceProvider.""" @@ -1533,9 +1533,9 @@ class AgentPool: Creates LocalResourceProvider within SkillsManager, then combines with MCP providers via AggregatingResourceProvider. """ - from agentpool.resource_providers.local import LocalResourceProvider - from agentpool.resource_providers import AggregatingResourceProvider - from agentpool.skills.uri_resolver import SkillURIResolver + from wolfharness.resource_providers.local import LocalResourceProvider + from wolfharness.resource_providers import AggregatingResourceProvider + from wolfharness.skills.uri_resolver import SkillURIResolver # 1. Extend existing SkillsManager with ResourceProvider if hasattr(self, 'skills') and self.skills: @@ -1603,7 +1603,7 @@ class AgentPool: ### SkillsManager Extension ```python -# src/agentpool/skills/manager.py (extension) +# src/wolfharness/skills/manager.py (extension) class SkillsManager: """Extended to expose ResourceProvider interface.""" @@ -1613,7 +1613,7 @@ class SkillsManager: await self.registry.__aenter__() # Create and enter LocalResourceProvider - from agentpool.resource_providers.local import LocalResourceProvider + from wolfharness.resource_providers.local import LocalResourceProvider self._resource_provider = LocalResourceProvider( name="local", @@ -1722,9 +1722,9 @@ When an MCP server uses the [FastMCP Skills Provider protocol](https://gofastmcp ### Phase 1: Core Infrastructure (Week 1) -1. **Exception classes** (`src/agentpool/skills/exceptions.py`) -2. **URI resolver** (`src/agentpool/skills/uri_resolver.py`) -3. **LocalResourceProvider** (`src/agentpool/resource_providers/local.py`) +1. **Exception classes** (`src/wolfharness/skills/exceptions.py`) +2. **URI resolver** (`src/wolfharness/skills/uri_resolver.py`) +3. **LocalResourceProvider** (`src/wolfharness/resource_providers/local.py`) 4. **Tests** for URI parsing and resolution ### Phase 2: MCP Integration (Week 2) @@ -1833,9 +1833,9 @@ Based on comprehensive review by Metis, Momus, and Oracle during initial develop - [x] Architecture review passed (Oracle) - [x] RFC quality review passed (Momus) - [x] Implementation readiness review passed (Metis) -- [x] Exception hierarchy (`src/agentpool/skills/exceptions.py`) -- [x] URI resolver (`src/agentpool/skills/uri_resolver.py`) -- [x] LocalResourceProvider (`src/agentpool/resource_providers/local.py`) +- [x] Exception hierarchy (`src/wolfharness/skills/exceptions.py`) +- [x] URI resolver (`src/wolfharness/skills/uri_resolver.py`) +- [x] LocalResourceProvider (`src/wolfharness/resource_providers/local.py`) - [x] MCPResourceProvider skill methods - [x] AggregatingResourceProvider skill aggregation - [x] Updated load_skill tool with URI support diff --git a/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md b/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md index c437c8699..3e135f1b0 100644 --- a/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md +++ b/docs/rfcs/implemented/RFC-0021-agent-concurrent-execution-safety.md @@ -540,7 +540,7 @@ async def _run_stream_once(self, run_ctx: AgentRunContext, prompts, ...): 4. **Verify Test Failure**: Ensure concurrent tests fail before fix (proves test validity) **Files Modified**: -- `src/agentpool/agents/native_agent.py` (fix finally block) +- `src/wolfharness/agents/native_agent.py` (fix finally block) **Deliverable**: - Existing bug fixed @@ -552,14 +552,14 @@ async def _run_stream_once(self, run_ctx: AgentRunContext, prompts, ...): ### Phase 1: Core Context Creation (Days 1-2) **Tasks**: -1. Create `AgentRunContext` dataclass in `agentpool/agents/context.py` +1. Create `AgentRunContext` dataclass in `wolfharness/agents/context.py` 2. Add `run_ctx` parameter to internal methods 3. Migrate `_cancelled` and `_current_stream_task` to context 4. Run tests after each migration step **Files Modified**: -- `src/agentpool/agents/context.py` (add AgentRunContext) -- `src/agentpool/agents/base_agent.py` (core changes) +- `src/wolfharness/agents/context.py` (add AgentRunContext) +- `src/wolfharness/agents/base_agent.py` (core changes) **Deliverable**: Basic concurrent calls work for simple scenarios @@ -575,9 +575,9 @@ async def _run_stream_once(self, run_ctx: AgentRunContext, prompts, ...): 5. Run full test suite after each step **Files Modified**: -- `src/agentpool/agents/base_agent.py` -- `src/agentpool/agents/events/event_emitter.py` -- `src/agentpool/agents/context.py` +- `src/wolfharness/agents/base_agent.py` +- `src/wolfharness/agents/events/event_emitter.py` +- `src/wolfharness/agents/context.py` **Deliverable**: All mutable state isolated @@ -603,7 +603,7 @@ async def _run_stream_once(self, run_ctx: AgentRunContext, prompts, ...): ### Dependencies - None external -- Internal: All changes within `agentpool.agents` module +- Internal: All changes within `wolfharness.agents` module ## 10. Open Questions @@ -697,7 +697,7 @@ python tests/agents/run_concurrent_tests.py pytest tests/agents/test_concurrent_safety.py -v # With coverage -pytest tests/agents/test_concurrent_safety.py --cov=src/agentpool/ +pytest tests/agents/test_concurrent_safety.py --cov=src/wolfharness/ # Stress test only pytest tests/agents/test_concurrent_safety.py -m slow -v @@ -747,10 +747,10 @@ async def test_concurrent_event_isolation(): ### Code Locations Key files referenced in this RFC: -- `src/agentpool/agents/base_agent.py` - BaseAgent implementation -- `src/agentpool/agents/native_agent/agent.py` - NativeAgent implementation -- `src/agentpool/agents/context.py` - AgentContext and (new) AgentRunContext -- `src/agentpool/agents/events/event_emitter.py` - Event emission +- `src/wolfharness/agents/base_agent.py` - BaseAgent implementation +- `src/wolfharness/agents/native_agent/agent.py` - NativeAgent implementation +- `src/wolfharness/agents/context.py` - AgentContext and (new) AgentRunContext +- `src/wolfharness/agents/events/event_emitter.py` - Event emission --- @@ -783,10 +783,10 @@ tests/agents/test_concurrent_safety.py::test_native_agent_concurrent PASSED ### Files Modified -- `src/agentpool/agents/context.py` - AgentRunContext dataclass -- `src/agentpool/agents/base_agent.py` - State migration to context -- `src/agentpool/agents/native_agent/agent.py` - Bug fixes and context usage -- `src/agentpool/agents/events/event_emitter.py` - Context-based event queue +- `src/wolfharness/agents/context.py` - AgentRunContext dataclass +- `src/wolfharness/agents/base_agent.py` - State migration to context +- `src/wolfharness/agents/native_agent/agent.py` - Bug fixes and context usage +- `src/wolfharness/agents/events/event_emitter.py` - Context-based event queue - All agent subclasses updated for context compatibility ### Migration Guide for Custom Subclasses diff --git a/docs/rfcs/implemented/RFC-0023-session-scoped-interrupt-routing.md b/docs/rfcs/implemented/RFC-0023-session-scoped-interrupt-routing.md index a74c0a97e..3671f61aa 100644 --- a/docs/rfcs/implemented/RFC-0023-session-scoped-interrupt-routing.md +++ b/docs/rfcs/implemented/RFC-0023-session-scoped-interrupt-routing.md @@ -645,9 +645,9 @@ If the server later chooses to wait for stream completion before broadcasting id 7. Update `is_cancelled()` to check per-run context for streaming runs **Files Modified**: -- `src/agentpool/agents/context.py` (add `iteration_task` field) -- `src/agentpool/agents/base_agent.py` (registry, interrupt routing) -- `src/agentpool/agents/native_agent/agent.py` (use `run_ctx.iteration_task`) +- `src/wolfharness/agents/context.py` (add `iteration_task` field) +- `src/wolfharness/agents/base_agent.py` (registry, interrupt routing) +- `src/wolfharness/agents/native_agent/agent.py` (use `run_ctx.iteration_task`) **Deliverable**: Core registry works, existing tests updated @@ -662,8 +662,8 @@ If the server later chooses to wait for stream completion before broadcasting id 4. Audit `ClaudeCodeAgent`, `CodexAgent`, and converter/helper call graphs for removed instance-state reads **Files Modified**: -- `src/agentpool/agents/acp_agent/acp_agent.py` -- `src/agentpool/agents/agui_agent/agui_agent.py` +- `src/wolfharness/agents/acp_agent/acp_agent.py` +- `src/wolfharness/agents/agui_agent/agui_agent.py` - Other subclasses as needed **Deliverable**: All subclasses use per-run context @@ -680,8 +680,8 @@ If the server later chooses to wait for stream completion before broadcasting id 5. Optional follow-up: if waiting for stream completion is implemented, use a per-session task map instead of a global task slot **Files Modified**: -- `src/agentpool_server/opencode_server/routes/session_routes.py` -- `src/agentpool_server/opencode_server/routes/message_routes.py` +- `src/wolfharness_server/opencode_server/routes/session_routes.py` +- `src/wolfharness_server/opencode_server/routes/message_routes.py` **Deliverable**: OpenCode abort flow uses session-scoped routing diff --git a/docs/rfcs/implemented/RFC-0026-per-session-agent-isolation.md b/docs/rfcs/implemented/RFC-0026-per-session-agent-isolation.md index 4ca91970f..c36588351 100644 --- a/docs/rfcs/implemented/RFC-0026-per-session-agent-isolation.md +++ b/docs/rfcs/implemented/RFC-0026-per-session-agent-isolation.md @@ -23,7 +23,7 @@ This RFC proposes the minimal change to enable concurrent multi-client access: r ## Problem Statement -When multiple OpenCode clients connect to the same `agentpool serve-opencode` server, only the first can process messages. All others are blocked by a global `asyncio.Lock` held for the entire duration of LLM inference (10–120 seconds per response). +When multiple OpenCode clients connect to the same `wolfharness serve-opencode` server, only the first can process messages. All others are blocked by a global `asyncio.Lock` held for the entire duration of LLM inference (10–120 seconds per response). **Root cause**: `ServerState.agent` is a single `BaseAgent` instance shared across all sessions. `bind_agent_to_session()` mutates `agent.session_id` and `agent._input_provider` per request. A global `agent_lock` serializes access, but blocks everything during inference. @@ -36,7 +36,7 @@ When multiple OpenCode clients connect to the same `agentpool serve-opencode` se agent_lock: asyncio.Lock = field(default_factory=asyncio.Lock) ``` -**Impact**: Teams cannot share an agentpool server. Each user must run their own instance. +**Impact**: Teams cannot share an wolfharness server. Each user must run their own instance. ## Goals & Non-Goals @@ -343,7 +343,7 @@ Self-contained change. Revert by restoring `state.agent` + `state.agent_lock` + ### Key Source Files -- `packages/agentpool/src/agentpool_server/opencode_server/state.py` — `ServerState`, `agent_lock`, `bind_agent_to_session()` -- `packages/agentpool/src/agentpool_server/opencode_server/routes/session_routes.py` — Session CRUD -- `packages/agentpool/src/agentpool_server/opencode_server/routes/message_routes.py` — Message handling -- `packages/agentpool/src/agentpool/agents/base_agent.py` — `BaseAgent`, shared mutable state +- `packages/wolfharness/src/wolfharness_server/opencode_server/state.py` — `ServerState`, `agent_lock`, `bind_agent_to_session()` +- `packages/wolfharness/src/wolfharness_server/opencode_server/routes/session_routes.py` — Session CRUD +- `packages/wolfharness/src/wolfharness_server/opencode_server/routes/message_routes.py` — Message handling +- `packages/wolfharness/src/wolfharness/agents/base_agent.py` — `BaseAgent`, shared mutable state diff --git a/docs/rfcs/implemented/RFC-0031-acp-per-session-agent-isolation.md b/docs/rfcs/implemented/RFC-0031-acp-per-session-agent-isolation.md index 30f52ac28..58c806961 100644 --- a/docs/rfcs/implemented/RFC-0031-acp-per-session-agent-isolation.md +++ b/docs/rfcs/implemented/RFC-0031-acp-per-session-agent-isolation.md @@ -86,7 +86,7 @@ The ACP server must adapt this pattern to its different architecture (ACP protoc ### The Problem -When multiple ACP clients connect to the same `agentpool serve-acp` server, all sessions share a single `BaseAgent` instance. This causes: +When multiple ACP clients connect to the same `wolfharness serve-acp` server, all sessions share a single `BaseAgent` instance. This causes: 1. **Conversation contamination**: Session A's messages appear in Session B's context window because `agent.conversation` is shared 2. **Input provider conflicts**: `ACPSession.__post_init__` sets `agent._input_provider = self.input_provider` for **all** agents in the pool (lines 220-221 of `session.py`), overwriting the previous session's input provider @@ -113,7 +113,7 @@ When multiple ACP clients connect to the same `agentpool serve-acp` server, all - **Risk**: Teams cannot share an ACP server (e.g., via WebSocket transport). Each concurrent session corrupts others' conversations. - **Risk**: Session resumption (`load_session`, `resume_session`) loads the wrong conversation history because the shared agent only has one `conversation` object. - **Risk**: Permission requests and user elicitations route to the wrong session because `agent._input_provider` is overwritten. -- **Cost**: Users must run separate `agentpool serve-acp` processes per client, defeating the purpose of the WebSocket/multi-client transport. +- **Cost**: Users must run separate `wolfharness serve-acp` processes per client, defeating the purpose of the WebSocket/multi-client transport. --- @@ -366,7 +366,7 @@ class AgentPoolACPAgent: def __post_init__(self) -> None: # ... existing init ... # NEW: Cache agent config for per-session creation - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig if self.agent_pool.main_agent and self.agent_pool.main_agent.name in self.agent_pool.manifest.agents: cfg = self.agent_pool.manifest.agents[self.agent_pool.main_agent.name] if isinstance(cfg, NativeAgentConfig): @@ -449,7 +449,7 @@ class AgentPoolACPAgent: agent_config = next(iter(self.agent_pool.manifest.agents.values())) if agent_config is not None: - from agentpool_config.context import ConfigContextManager + from wolfharness_config.context import ConfigContextManager with ConfigContextManager(agent_config.config_file_path): agent = agent_config.get_agent( @@ -1087,8 +1087,8 @@ After updating the RFC based on Round 2 findings, Oracle and Metis performed a f ### Key Source Files -- `src/agentpool_server/acp_server/server.py` — `ACPServer`, pool lifecycle -- `src/agentpool_server/acp_server/acp_agent.py` — `AgentPoolACPAgent`, protocol methods -- `src/agentpool_server/acp_server/session_manager.py` — `ACPSessionManager`, session tracking -- `src/agentpool_server/acp_server/session.py` — `ACPSession`, prompt processing -- `src/agentpool_server/opencode_server/state.py` — Reference: OpenCode's `_session_agents`, `get_or_create_agent()` +- `src/wolfharness_server/acp_server/server.py` — `ACPServer`, pool lifecycle +- `src/wolfharness_server/acp_server/acp_agent.py` — `AgentPoolACPAgent`, protocol methods +- `src/wolfharness_server/acp_server/session_manager.py` — `ACPSessionManager`, session tracking +- `src/wolfharness_server/acp_server/session.py` — `ACPSession`, prompt processing +- `src/wolfharness_server/opencode_server/state.py` — Reference: OpenCode's `_session_agents`, `get_or_create_agent()` diff --git a/docs/rfcs/implemented/RFC-0033-mcp-over-acp-transport.md b/docs/rfcs/implemented/RFC-0033-mcp-over-acp-transport.md index 03d087439..252ed93a4 100644 --- a/docs/rfcs/implemented/RFC-0033-mcp-over-acp-transport.md +++ b/docs/rfcs/implemented/RFC-0033-mcp-over-acp-transport.md @@ -18,9 +18,9 @@ related_rfcs: ## Overview -本 RFC 提议在 agentpool 中增加对 MCP-over-ACP 传输协议的支持,允许 ACP 客户端通过现有 ACP 连接注入 MCP 工具服务,而无需单独的 stdio 进程或 HTTP 端口。实现后,agentpool 可作为 ACP Agent 向客户端声明 `mcpCapabilities.acp: true`,并在 session 初始化阶段**主动**通过 `mcp/connect` 向客户端请求建立连接(Agent→Client),通过 `mcp/message` 双向转发工具调用,在 session 结束时通过 `mcp/disconnect` 通知客户端清理(Agent→Client),完成完整的 MCP 工具调用生命周期。 +本 RFC 提议在 wolfharness 中增加对 MCP-over-ACP 传输协议的支持,允许 ACP 客户端通过现有 ACP 连接注入 MCP 工具服务,而无需单独的 stdio 进程或 HTTP 端口。实现后,wolfharness 可作为 ACP Agent 向客户端声明 `mcpCapabilities.acp: true`,并在 session 初始化阶段**主动**通过 `mcp/connect` 向客户端请求建立连接(Agent→Client),通过 `mcp/message` 双向转发工具调用,在 session 结束时通过 `mcp/disconnect` 通知客户端清理(Agent→Client),完成完整的 MCP 工具调用生命周期。 -该特性能够显著扩大 agentpool 的适用场景:客户端可注入项目感知工具(如代码感知的搜索工具)、沙箱环境(如 WASM 运行时)也能提供 MCP 工具,同时消除了通过"旁路"传输带来的沙箱逃逸风险。 +该特性能够显著扩大 wolfharness 的适用场景:客户端可注入项目感知工具(如代码感知的搜索工具)、沙箱环境(如 WASM 运行时)也能提供 MCP 工具,同时消除了通过"旁路"传输带来的沙箱逃逸风险。 ## Table of Contents @@ -43,7 +43,7 @@ related_rfcs: ### Current State -agentpool 当前通过 ACP 协议对外提供 Agent 能力。在 `session/new` 和 `session/load` 请求中,客户端可传入 MCP 服务器配置(`mcp_servers`),agentpool 支持三种 MCP 传输类型: +wolfharness 当前通过 ACP 协议对外提供 Agent 能力。在 `session/new` 和 `session/load` 请求中,客户端可传入 MCP 服务器配置(`mcp_servers`),wolfharness 支持三种 MCP 传输类型: | 类型 | 实现文件 | 状态 | |------|----------|------| @@ -56,7 +56,7 @@ agentpool 当前通过 ACP 协议对外提供 Agent 能力。在 `session/new` ### Historical Context -- [RFC-0030](../draft/RFC-0030-acp-streamable-http-websocket-transport.md) 扩展了 agentpool 对 Streamable HTTP / WebSocket 的 ACP 传输层支持,为本 RFC 奠定了传输基础设施基础。 +- [RFC-0030](../draft/RFC-0030-acp-streamable-http-websocket-transport.md) 扩展了 wolfharness 对 Streamable HTTP / WebSocket 的 ACP 传输层支持,为本 RFC 奠定了传输基础设施基础。 - ACP 协议官方 RFD [mcp-over-acp](../../../../agent-client-protocol/docs/rfds/mcp-over-acp.mdx) 定义了完整规范,包括消息格式、连接复用和 Bridging 策略。 ### Prerequisites @@ -69,7 +69,7 @@ agentpool 当前通过 ACP 协议对外提供 Agent 能力。在 `session/new` | Term | Definition | |------|------------| -| ACP | Agent Client Protocol,agentpool 使用的 Agent-客户端双向通信协议 | +| ACP | Agent Client Protocol,wolfharness 使用的 Agent-客户端双向通信协议 | | MCP | Model Context Protocol,LLM 使用工具的标准协议 | | MCP-over-ACP | 通过 ACP 连接通道传输 MCP 消息的扩展 | | `acpId` | 客户端在 `session/new` 中为 ACP-transport MCP Server 生成的唯一标识 | @@ -96,13 +96,13 @@ ACP 客户端(IDE、编辑器、代理中间层)希望向 Agent 注入与当 ### Evidence - ACP 官方 RFD [mcp-over-acp](../../../../agent-client-protocol/docs/rfds/mcp-over-acp.mdx) 明确指出该问题并提出了规范解决方案 -- agentpool 的 `convert_acp_mcp_server_to_config()` 在遇到未知 MCP server 类型时直接 `assert_never`(即崩溃),无任何扩展入口 +- wolfharness 的 `convert_acp_mcp_server_to_config()` 在遇到未知 MCP server 类型时直接 `assert_never`(即崩溃),无任何扩展入口 - `McpCapabilities` 中无 `acp` 字段,无法向客户端声明支持能力 ### Impact of Inaction - **功能缺失**:无法支持 ACP 客户端的 MCP 工具注入场景(如 Zed 插件、WASM 沙箱工具) -- **生态兼容性**:ACP 参考实现(Rust SDK sacp-conductor)已实现 Bridging,agentpool 无法与其互操作 +- **生态兼容性**:ACP 参考实现(Rust SDK sacp-conductor)已实现 Bridging,wolfharness 无法与其互操作 - **扩展受阻**:未来的 proxy-chain 场景(RFC 参见 [proxy-chains RFD](../../../../agent-client-protocol/docs/rfds/))依赖 MCP-over-ACP --- @@ -111,25 +111,25 @@ ACP 客户端(IDE、编辑器、代理中间层)希望向 Agent 注入与当 ### Goals (In Scope) -1. agentpool 作为 ACP Agent 在 `InitializeResponse` 中声明 `mcpCapabilities.acp: true` +1. wolfharness 作为 ACP Agent 在 `InitializeResponse` 中声明 `mcpCapabilities.acp: true` 2. 支持在 `session/new`、`session/load`、`session/fork`、`session/resume` 请求中接收 `type: "acp"` 的 MCP Server 声明 3. 实现 `mcp/connect` 消息处理:在 session 初始化阶段接收 `acpId`,返回唯一 `connectionId` 4. 实现双向 `mcp/message` 消息转发:将 Agent 发出的 MCP 工具调用路由至提供该 Server 的 ACP 客户端,并将结果原路返回 5. 实现 `mcp/disconnect` 消息处理:在 session 结束时清理连接状态 6. 支持同一 MCP Server 的多路连接复用(每次 `mcp/connect` 返回独立 `connectionId`) -7. **双向配置转换**:同步更新正向转换(`converters.py`)和反向转换(`acp_converters.py`),确保 agentpool 作为 ACP 客户端时代码不崩溃 +7. **双向配置转换**:同步更新正向转换(`converters.py`)和反向转换(`acp_converters.py`),确保 wolfharness 作为 ACP 客户端时代码不崩溃 ### Non-Goals (Out of Scope) 1. **Bridging(可选,后续 RFC)**:将 ACP-transport MCP Server 透明转为 stdio/HTTP shim 不在本 RFC 范围内 -2. **客户端侧实现**:本 RFC 仅覆盖 agentpool(Agent 侧)实现,不涉及客户端 SDK 变更 +2. **客户端侧实现**:本 RFC 仅覆盖 wolfharness(Agent 侧)实现,不涉及客户端 SDK 变更 3. **MCP Server 能力缓存**:不在本 RFC 中实现跨连接的工具列表缓存 4. **认证与鉴权扩展**:MCP-over-ACP 沿用与 ACP 相同的信任模型,不新增额外认证机制 ### Success Criteria - [ ] `InitializeResponse` 包含 `mcpCapabilities.acp: true` -- [ ] 客户端传入 `type: "acp"` MCP Server 后,agentpool 在 session 初始化阶段**主动**发送 `mcp/connect`(Agent→Client),客户端返回 `connectionId` +- [ ] 客户端传入 `type: "acp"` MCP Server 后,wolfharness 在 session 初始化阶段**主动**发送 `mcp/connect`(Agent→Client),客户端返回 `connectionId` - [ ] Agent 可通过 `connectionId` 发起 `mcp/message` 工具调用并收到结果 - [ ] `mcp/disconnect` 正确清理连接(Agent 主动向 Client 发送) - [ ] 现有 stdio/SSE/HTTP MCP 功能无回归(通过已有测试集验证) @@ -155,7 +155,7 @@ ACP 客户端(IDE、编辑器、代理中间层)希望向 Agent 注入与当 **Description** -在 agentpool 的 ACP server 层直接实现完整的 MCP-over-ACP 协议处理器: +在 wolfharness 的 ACP server 层直接实现完整的 MCP-over-ACP 协议处理器: - 扩展 `McpCapabilities` schema 添加 `acp` 字段 - 新增 `AcpMcpServer` schema 类型 @@ -210,11 +210,11 @@ ACP 客户端(IDE、编辑器、代理中间层)希望向 Agent 注入与当 **Description** -不修改 agentpool 核心,而是在 agentpool 外层增加一个 Conductor/Proxy 进程:对外声明支持 `mcpCapabilities.acp`,接收到 ACP-transport MCP Server 声明后,生成一个本地 stdio shim 进程,将 ACP channel 消息转换为 stdio MCP 消息,再传给 agentpool(以普通 stdio MCP Server 形式传入)。 +不修改 wolfharness 核心,而是在 wolfharness 外层增加一个 Conductor/Proxy 进程:对外声明支持 `mcpCapabilities.acp`,接收到 ACP-transport MCP Server 声明后,生成一个本地 stdio shim 进程,将 ACP channel 消息转换为 stdio MCP 消息,再传给 wolfharness(以普通 stdio MCP Server 形式传入)。 **Advantages** -- agentpool 核心代码改动最小 +- wolfharness 核心代码改动最小 - shim 进程可独立部署和测试 - 与 ACP RFD 中描述的 Bridging 模式对齐 @@ -230,7 +230,7 @@ ACP 客户端(IDE、编辑器、代理中间层)希望向 Agent 注入与当 | Criterion | Rating | Notes | |-----------|--------|-------| | 协议合规性 | ⭐⭐⭐ | 从外部看符合规范,但内部绕过了核心目标 | -| 向后兼容性 | ⭐⭐⭐⭐⭐ | 对 agentpool 核心无侵入 | +| 向后兼容性 | ⭐⭐⭐⭐⭐ | 对 wolfharness 核心无侵入 | | 实现复杂度 | ⭐⭐⭐ | shim 进程本身复杂度与 Option 1 相当 | | 可扩展性 | ⭐⭐ | Bridging 作为唯一实现路径,无原生支持扩展点 | | 可测试性 | ⭐⭐⭐ | 进程边界使集成测试更复杂 | @@ -254,7 +254,7 @@ ACP 客户端(IDE、编辑器、代理中间层)希望向 Agent 注入与当 **Description** -等待 ACP 官方 Rust SDK(sacp-conductor)稳定后,通过集成官方 bridge 实现支持,agentpool 自身不做任何改动。 +等待 ACP 官方 Rust SDK(sacp-conductor)稳定后,通过集成官方 bridge 实现支持,wolfharness 自身不做任何改动。 **Advantages** @@ -333,7 +333,7 @@ Option 1 是唯一能够完整实现 ACP RFD 目标的方案: └──────────────────────────┬──────────────────────────────────────┘ │ ACP Channel (JSON-RPC over stdio/WS) ┌──────────────────────────▼──────────────────────────────────────┐ -│ agentpool ACP Server (AgentPoolACPAgent / acp_agent.py) │ +│ wolfharness ACP Server (AgentPoolACPAgent / acp_agent.py) │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ AcpMcpConnectionManager │ │ @@ -359,7 +359,7 @@ Option 1 是唯一能够完整实现 ACP RFD 目标的方案: ### Message Flow ``` -Client agentpool Agent (LLM) +Client wolfharness Agent (LLM) │ │ │ │── session/new ───────────▶│ │ │ mcp_servers: [{ │ │ @@ -440,7 +440,7 @@ ClientMethod = Literal[ #### 2. AcpMcpConnectionManager -新增 `agentpool_server/acp_server/acp_mcp_manager.py`: +新增 `wolfharness_server/acp_server/acp_mcp_manager.py`: ```python from __future__ import annotations @@ -573,7 +573,7 @@ async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any #### 4. fastmcp ClientTransport 实现 -**新增 `agentpool_server/acp_server/acp_mcp_transport.py`**: +**新增 `wolfharness_server/acp_server/acp_mcp_transport.py`**: ```python from fastmcp.client.transports import ClientTransport @@ -650,7 +650,7 @@ def mcp_config_to_acp(config: MCPServerConfig) -> McpServer: #### 7. AcpMCPServerConfig(新增) -在 `agentpool_config/mcp_server.py` 中新增: +在 `wolfharness_config/mcp_server.py` 中新增: ```python class AcpMCPServerConfig(MCPServerConfig): @@ -671,7 +671,7 @@ MCPServerConfig = Annotated[ #### 8. MCPResourceProvider 扩展 -在 `agentpool/mcp_server/provider.py` 中更新 `transport_type`: +在 `wolfharness/mcp_server/provider.py` 中更新 `transport_type`: ```python @property @@ -738,9 +738,9 @@ ACPSession (per-agent-session) | Threat | Impact | Likelihood | Mitigation | |--------|--------|------------|------------| -| 客户端注入恶意 MCP 工具 | High | Medium | 与现有 MCP 信任模型一致:agentpool 不对 MCP 工具内容进行额外验证,工具调用权限由用户在会话级别授权 | +| 客户端注入恶意 MCP 工具 | High | Medium | 与现有 MCP 信任模型一致:wolfharness 不对 MCP 工具内容进行额外验证,工具调用权限由用户在会话级别授权 | | `connectionId` 伪造/碰撞 | Medium | Low | `connectionId` 由客户端生成,Agent 仅验证其是否存在于本地连接表;使用完整 `uuid4().hex`(128-bit 熵),不可预测 | -| `mcp/message` 参数注入 | Low | Low | 透明转发,agentpool 不解析工具参数内容;安全责任由 MCP Server(客户端侧)承担 | +| `mcp/message` 参数注入 | Low | Low | 透明转发,wolfharness 不解析工具参数内容;安全责任由 MCP Server(客户端侧)承担 | | 连接未清理导致内存泄漏 | Medium | Low | 绑定 Agent 生命周期,Agent 关闭时强制 `close_all()` | ### Security Measures @@ -775,7 +775,7 @@ MCP-over-ACP 遵循与现有 MCP 传输相同的信任模型:工具调用由 A - 新增 `AcpMcpServer` schema - 扩展 `AgentMethod`/`ClientMethod` - 更新 `AgentCapabilities.create()` - - 更新 `MCPServerConfig` union(`agentpool_config/mcp_server.py`) + - 更新 `MCPServerConfig` union(`wolfharness_config/mcp_server.py`) - 更新 `MCPResourceProvider.transport_type` 返回类型 - **Deliverables**:Schema 变更 PR,附单元测试 - **Dependencies**:Pre-Phase 0 通过 @@ -839,8 +839,8 @@ MCP-over-ACP 遵循与现有 MCP 传输相同的信任模型:工具调用由 A - **Rationale**:命名一致性优先,虽略显冗长但明确无歧义 2. **pydantic-ai MCP transport 接入点** - - **Decision**:不直接对接 pydantic-ai,而是实现 `fastmcp.ClientTransport`,通过 `fastmcp.Client` 接入 agentpool 现有的 `MCPClient` - - **Rationale**:agentpool 实际使用 fastmcp(而非 pydantic-ai 的 MCP 抽象)管理 MCP 连接。`ClientTransport` 是正确的扩展点 + - **Decision**:不直接对接 pydantic-ai,而是实现 `fastmcp.ClientTransport`,通过 `fastmcp.Client` 接入 wolfharness 现有的 `MCPClient` + - **Rationale**:wolfharness 实际使用 fastmcp(而非 pydantic-ai 的 MCP 抽象)管理 MCP 连接。`ClientTransport` 是正确的扩展点 3. **`mcp/message` 的 method 归属** - **Decision**:`mcp/message` 注册在 `ClientMethod` 中(通过 `_` prefix 机制实现双向路由) @@ -858,7 +858,7 @@ MCP-over-ACP 遵循与现有 MCP 传输相同的信任模型:工具调用由 A - Status: **已解决** — 通过 `client.ext_method()` 主动发起,`ext_method()` 被动接收 `_mcp/message` 2. **fastmcp pinned 版本是否支持自定义 ClientTransport** - - Context:需确认 agentpool 当前锁定的 fastmcp 版本是否暴露 `ClientTransport` 接口 + - Context:需确认 wolfharness 当前锁定的 fastmcp 版本是否暴露 `ClientTransport` 接口 - Owner: 本 RFC 实现者 - Status: **Blocker for Pre-Phase 0** @@ -919,11 +919,11 @@ MCP-over-ACP 遵循与现有 MCP 传输相同的信任模型:工具调用由 A | `mcp/connect` method | `acp/schema/messages.py` | ClientMethod(Agent→Client) | | `mcp/disconnect` method | `acp/schema/messages.py` | ClientMethod(Agent→Client) | | `mcp/message` method | `acp/schema/messages.py` | ClientMethod(双向,通过 `_` prefix) | -| `AcpMcpServer` handler | `agentpool_server/acp_server/converters.py` | 转换逻辑(正向) | -| `AcpMcpServer` reverse handler | `agentpool_server/acp_server/acp_converters.py` | 转换逻辑(反向) | -| `AcpMCPServerConfig` | `agentpool_config/mcp_server.py` | Config 类 | -| `AcpMcpConnectionManager` | `agentpool_server/acp_server/acp_mcp_manager.py` | 核心逻辑 | -| `AcpMcpTransport` | `agentpool_server/acp_server/acp_mcp_transport.py` | fastmcp Transport | +| `AcpMcpServer` handler | `wolfharness_server/acp_server/converters.py` | 转换逻辑(正向) | +| `AcpMcpServer` reverse handler | `wolfharness_server/acp_server/acp_converters.py` | 转换逻辑(反向) | +| `AcpMCPServerConfig` | `wolfharness_config/mcp_server.py` | Config 类 | +| `AcpMcpConnectionManager` | `wolfharness_server/acp_server/acp_mcp_manager.py` | 核心逻辑 | +| `AcpMcpTransport` | `wolfharness_server/acp_server/acp_mcp_transport.py` | fastmcp Transport | #### B. `assert_never` 穷尽匹配更新清单 @@ -931,12 +931,12 @@ MCP-over-ACP 遵循与现有 MCP 传输相同的信任模型:工具调用由 A | 文件 | 函数/属性 | 操作 | |------|----------|------| -| `agentpool_server/acp_server/converters.py` | `convert_acp_mcp_server_to_config()` | 新增 `case AcpMcpServer()` | -| `agentpool_server/acp_server/acp_converters.py` | `mcp_config_to_acp()` | 新增 `case AcpMCPServerConfig()` | -| `agentpool/mcp_server/provider.py` | `transport_type` property | 新增 `"acp"` 到 return `Literal` | -| `agentpool/mcp_server/client.py` | `MCPClient._get_client()` | 新增 `case AcpMCPServerConfig()` | -| `agentpool_config/mcp_server.py` | `MCPServerConfig` union | 新增 `AcpMCPServerConfig` | -| `agentpool_config/mcp_server.py` | `parse_mcp_servers_json()` | 新增 `"acp"` transport 分支 | +| `wolfharness_server/acp_server/converters.py` | `convert_acp_mcp_server_to_config()` | 新增 `case AcpMcpServer()` | +| `wolfharness_server/acp_server/acp_converters.py` | `mcp_config_to_acp()` | 新增 `case AcpMCPServerConfig()` | +| `wolfharness/mcp_server/provider.py` | `transport_type` property | 新增 `"acp"` 到 return `Literal` | +| `wolfharness/mcp_server/client.py` | `MCPClient._get_client()` | 新增 `case AcpMCPServerConfig()` | +| `wolfharness_config/mcp_server.py` | `MCPServerConfig` union | 新增 `AcpMCPServerConfig` | +| `wolfharness_config/mcp_server.py` | `parse_mcp_servers_json()` | 新增 `"acp"` transport 分支 | #### C. 前置条件修复清单 diff --git a/docs/rfcs/implemented/RFC-0054-v2-message-id-infrastructure.md b/docs/rfcs/implemented/RFC-0054-v2-message-id-infrastructure.md index fc6834da1..cc23ae153 100644 --- a/docs/rfcs/implemented/RFC-0054-v2-message-id-infrastructure.md +++ b/docs/rfcs/implemented/RFC-0054-v2-message-id-infrastructure.md @@ -835,7 +835,7 @@ All open questions have been resolved: - OpenSpec Change: v2-message-id-infrastructure - [RFC-0050: AgentWolf v1 Foundation Architecture](../draft/RFC-0050-agentwolf-v1-foundation-architecture.md) - [RFC-0042: Unified Lifecycle Architecture](../draft/RFC-0042-unified-lifecycle-architecture.md) -- [GitHub Issue #154: Extend Feedback type with messageId](https://github.com/Leoyzen/agentpool/issues/154) +- [GitHub Issue #154: Extend Feedback type with messageId](https://github.com/Leoyzen/wolfharness/issues/154) ### External Resources @@ -862,6 +862,6 @@ All open questions have been resolved: | `orchestrator/session_pool.py` | Public API pass-through | Low — optional parameter | | `agents/native_agent/turn.py` | message_id propagation | Low — set field on events | | `agents/acp_agent/acp_converters.py` | Preserve incoming message_id | Low — conditional fallback | -| `agentpool_server/acp_server/event_converter.py` | Read from events | Low — mechanical | -| `agentpool_server/opencode_server/` | Read from events | Low — audit + update | +| `wolfharness_server/acp_server/event_converter.py` | Read from events | Low — mechanical | +| `wolfharness_server/opencode_server/` | Read from events | Low — audit + update | | `agui_server/` + `openai_api_server/` | Audit | Low — audit + update if needed | diff --git a/docs/tool-display-capability.example.yaml b/docs/tool-display-capability.example.yaml index 49a3a1966..ffb03c860 100644 --- a/docs/tool-display-capability.example.yaml +++ b/docs/tool-display-capability.example.yaml @@ -47,7 +47,7 @@ agents: --- # 示例 3:子能力已自发射 —— 仅改名,关闭注入避免重复 # fsspec 等工具集已在内部 emit DiffContentItem(见 -# src/agentpool_toolsets/fsspec_toolset/toolset.py:575),装饰器只负责 +# src/wolfharness_toolsets/fsspec_toolset/toolset.py:575),装饰器只负责 # 改名,emit_diff: false。 agents: fs-writer: diff --git a/docs/tutorials/agent-usage.md b/docs/tutorials/agent-usage.md index 72d940e0f..99f4db20b 100644 --- a/docs/tutorials/agent-usage.md +++ b/docs/tutorials/agent-usage.md @@ -13,7 +13,7 @@ AgentPool provides multiple agent implementations with a shared interface. These Create and use an agent: ```python -from agentpool import Agent +from wolfharness import Agent from pydantic import BaseModel diff --git a/docs/tutorials/basic-concepts.md b/docs/tutorials/basic-concepts.md index b6e58e128..61603fa74 100644 --- a/docs/tutorials/basic-concepts.md +++ b/docs/tutorials/basic-concepts.md @@ -12,7 +12,7 @@ AgentPool uses a unified **node** abstraction for all message-processing entitie ````python exec="true" -from agentpool.messaging import MessageNode +from wolfharness.messaging import MessageNode node = mk.MkClassDiagram(MessageNode, mode="subclasses") print(node) ```` diff --git a/docs/tutorials/dependencies.md b/docs/tutorials/dependencies.md index 7921986d0..111402fe1 100644 --- a/docs/tutorials/dependencies.md +++ b/docs/tutorials/dependencies.md @@ -37,6 +37,6 @@ AgentPool installs optional features via extras. Some commonly used ones: For a full dependency tree, run: ```bash -pip show agentpool -pipdeptree -p agentpool +pip show wolfharness +pipdeptree -p wolfharness ``` \ No newline at end of file diff --git a/docs/tutorials/example-configurations.md b/docs/tutorials/example-configurations.md index b61a055db..cad15439b 100644 --- a/docs/tutorials/example-configurations.md +++ b/docs/tutorials/example-configurations.md @@ -21,7 +21,7 @@ Use the agent via an ACP client of your choice. Or programmatically: ```python -from agentpool import AgentPool +from wolfharness import AgentPool async with AgentPool("agents.yml") as pool: agent = pool.get_agent("url_opener") @@ -48,7 +48,7 @@ Create an agent that interacts with the file system: Use the file manager: ```python -from agentpool import Agent +from wolfharness import Agent async with AgentPool("agents.yml" as pool: agent = pool.get_agent("file_manager") diff --git a/docs/tutorials/example_simple.yml b/docs/tutorials/example_simple.yml index 5675312ef..acb877d87 100644 --- a/docs/tutorials/example_simple.yml +++ b/docs/tutorials/example_simple.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: url_opener: type: native diff --git a/docs/tutorials/example_structured.yml b/docs/tutorials/example_structured.yml index 2276d10bb..0ebee8b4e 100644 --- a/docs/tutorials/example_structured.yml +++ b/docs/tutorials/example_structured.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json responses: CodeReview: response_schema: diff --git a/docs/tutorials/example_tools.yml b/docs/tutorials/example_tools.yml index 08e5df0bc..552330407 100644 --- a/docs/tutorials/example_tools.yml +++ b/docs/tutorials/example_tools.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: file_manager: type: native diff --git a/docs/tutorials/examples/create_docs/config.yml b/docs/tutorials/examples/create_docs/config.yml index 41178c317..2f74c03e4 100644 --- a/docs/tutorials/examples/create_docs/config.yml +++ b/docs/tutorials/examples/create_docs/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: file_scanner: type: native diff --git a/docs/tutorials/examples/create_docs/index.md b/docs/tutorials/examples/create_docs/index.md index f2c3621d3..4f385764a 100644 --- a/docs/tutorials/examples/create_docs/index.md +++ b/docs/tutorials/examples/create_docs/index.md @@ -32,7 +32,7 @@ This demonstrates different ways agents can collaborate: ```python # /// script -# dependencies = ["agentpool", "mypy"] +# dependencies = ["wolfharness", "mypy"] # /// @@ -52,8 +52,8 @@ from pathlib import Path from mypy import api import rich -from agentpool import Agent, AgentPool, AgentsManifest -from agentpool.docs.utils import run +from wolfharness import Agent, AgentPool, AgentsManifest +from wolfharness.docs.utils import run # set your OpenAI API key here @@ -84,11 +84,11 @@ async def main() -> None: scanner.connect_to(writer) # Start async docs generation (the writer will start working in async fashion) - await scanner.run('List all Python files in "src/agentpool/agent"') + await scanner.run('List all Python files in "src/wolfharness/agent"') assert isinstance(scanner, Agent) # Use error checker as tool (this blocks until complete) scanner.register_worker(checker) - prompt = 'Check types for all Python files in "src/agentpool/agent"' + prompt = 'Check types for all Python files in "src/wolfharness/agent"' result = await scanner.run(prompt) rich.print(f"Type checking result:\n{result.data}") @@ -103,7 +103,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json agents: file_scanner: type: native diff --git a/docs/tutorials/examples/create_docs/main.py b/docs/tutorials/examples/create_docs/main.py index 49e632e5b..0f3bc134c 100644 --- a/docs/tutorials/examples/create_docs/main.py +++ b/docs/tutorials/examples/create_docs/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool", "mypy"] +# dependencies = ["wolfharness", "mypy"] # /// @@ -19,8 +19,8 @@ from mypy import api import rich -from agentpool import Agent, AgentPool, AgentsManifest -from agentpool.docs.utils import run +from wolfharness import Agent, AgentPool, AgentsManifest +from wolfharness.docs.utils import run # set your OpenAI API key here @@ -51,11 +51,11 @@ async def main() -> None: scanner.connect_to(writer) # Start async docs generation (the writer will start working in async fashion) - await scanner.run('List all Python files in "src/agentpool/agent"') + await scanner.run('List all Python files in "src/wolfharness/agent"') assert isinstance(scanner, Agent) # Use error checker as tool (this blocks until complete) scanner.register_worker(checker) - prompt = 'Check types for all Python files in "src/agentpool/agent"' + prompt = 'Check types for all Python files in "src/wolfharness/agent"' result = await scanner.run(prompt) rich.print(f"Type checking result:\n{result.data}") diff --git a/docs/tutorials/examples/crewai_flow/config.yml b/docs/tutorials/examples/crewai_flow/config.yml index e8cfb3cbc..1cc4f9ff3 100644 --- a/docs/tutorials/examples/crewai_flow/config.yml +++ b/docs/tutorials/examples/crewai_flow/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: city_picker: type: native diff --git a/docs/tutorials/examples/crewai_flow/index.md b/docs/tutorials/examples/crewai_flow/index.md index 4cf311048..a08c5d6a5 100644 --- a/docs/tutorials/examples/crewai_flow/index.md +++ b/docs/tutorials/examples/crewai_flow/index.md @@ -14,7 +14,7 @@ This example demonstrates a CrewAI-like workflow pattern implemented in AgentPoo ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Adaption of a CrewAI-like flow.""" @@ -23,9 +23,9 @@ from __future__ import annotations import os -from agentpool import Agent, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run -from agentpool.running import node_function, run_nodes_async +from wolfharness import Agent, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run +from wolfharness.running import node_function, run_nodes_async # set your OpenAI API key here @@ -62,7 +62,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json agents: city_picker: type: native diff --git a/docs/tutorials/examples/crewai_flow/main.py b/docs/tutorials/examples/crewai_flow/main.py index 7f0535415..d5fdc8c97 100644 --- a/docs/tutorials/examples/crewai_flow/main.py +++ b/docs/tutorials/examples/crewai_flow/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Adaption of a CrewAI-like flow.""" @@ -8,9 +8,9 @@ import os -from agentpool import Agent, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run -from agentpool.running import node_function, run_nodes_async +from wolfharness import Agent, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run +from wolfharness.running import node_function, run_nodes_async # set your OpenAI API key here diff --git a/docs/tutorials/examples/download_agents/config.yml b/docs/tutorials/examples/download_agents/config.yml index 123b634a9..afd6dabeb 100644 --- a/docs/tutorials/examples/download_agents/config.yml +++ b/docs/tutorials/examples/download_agents/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json storage: # List of storage providers (can use multiple) providers: @@ -23,7 +23,7 @@ agents: 3. Never stop believing in your team! 🎉 tools: - type: agent_cli # Need to know who to cheer for! - - agentpool_docs/examples.download_agents.main.cheer + - wolfharness_docs/examples.download_agents.main.cheer file_getter_1: type: native diff --git a/docs/tutorials/examples/download_agents/index.md b/docs/tutorials/examples/download_agents/index.md index 30c801f35..47c2ff0db 100644 --- a/docs/tutorials/examples/download_agents/index.md +++ b/docs/tutorials/examples/download_agents/index.md @@ -42,7 +42,7 @@ This demonstrates: ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -63,13 +63,13 @@ from dataclasses import dataclass import os from typing import TYPE_CHECKING, Any -from agentpool import AgentPool, AgentsManifest -from agentpool.agents.events import RichAgentStreamEvent -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.agents.events import RichAgentStreamEvent +from wolfharness.docs.utils import get_config_path, is_pyodide, run if TYPE_CHECKING: - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext # set your OpenAI API key here @@ -112,7 +112,7 @@ async def run_example() -> None: manifest = AgentsManifest.from_file(config_path) async def event_handler(ctx: AgentContext[Any], event: RichAgentStreamEvent[Any]) -> None: - from agentpool.agents.events import ToolCallProgressEvent + from wolfharness.agents.events import ToolCallProgressEvent if isinstance(event, ToolCallProgressEvent): print(f"Progress: {event.progress}/{event.total} - {event.message}") @@ -156,7 +156,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json storage: # List of storage providers (can use multiple) providers: @@ -181,7 +181,7 @@ agents: 3. Never stop believing in your team! 🎉 tools: - type: agent_cli # Need to know who to cheer for! - - agentpool_docs/examples.download_agents.main.cheer + - wolfharness_docs/examples.download_agents.main.cheer file_getter_1: type: native diff --git a/docs/tutorials/examples/download_agents/main.py b/docs/tutorials/examples/download_agents/main.py index 54aea6643..450ce307e 100644 --- a/docs/tutorials/examples/download_agents/main.py +++ b/docs/tutorials/examples/download_agents/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -20,13 +20,13 @@ import os from typing import TYPE_CHECKING, Any -from agentpool import AgentPool, AgentsManifest -from agentpool.agents.events import RichAgentStreamEvent -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run if TYPE_CHECKING: - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext + from wolfharness.agents.events import RichAgentStreamEvent # set your OpenAI API key here @@ -69,7 +69,7 @@ async def run_example() -> None: manifest = AgentsManifest.from_file(config_path) async def event_handler(ctx: AgentContext[Any], event: RichAgentStreamEvent[Any]) -> None: - from agentpool.agents.events import ToolCallProgressEvent + from wolfharness.agents.events import ToolCallProgressEvent if isinstance(event, ToolCallProgressEvent): print(f"Progress: {event.progress}/{event.total} - {event.message}") diff --git a/docs/tutorials/examples/download_workers/config.yml b/docs/tutorials/examples/download_workers/config.yml index 9801b29d7..e324df355 100644 --- a/docs/tutorials/examples/download_workers/config.yml +++ b/docs/tutorials/examples/download_workers/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: overseer: type: native diff --git a/docs/tutorials/examples/download_workers/index.md b/docs/tutorials/examples/download_workers/index.md index 6e6189751..a4f078893 100644 --- a/docs/tutorials/examples/download_workers/index.md +++ b/docs/tutorials/examples/download_workers/index.md @@ -24,7 +24,7 @@ Key features: ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -41,8 +41,8 @@ from __future__ import annotations import os import time -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run PROMPT = "Download this file using both agent tools available to you: http://speedtest.tele2.net/10MB.zip" @@ -83,7 +83,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json agents: overseer: type: native diff --git a/docs/tutorials/examples/download_workers/main.py b/docs/tutorials/examples/download_workers/main.py index 438f9c9ed..b84cf7aef 100644 --- a/docs/tutorials/examples/download_workers/main.py +++ b/docs/tutorials/examples/download_workers/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -16,8 +16,8 @@ import os import time -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run PROMPT = "Download this file using both agent tools available to you: http://speedtest.tele2.net/10MB.zip" diff --git a/docs/tutorials/examples/human_interaction/config.yml b/docs/tutorials/examples/human_interaction/config.yml index eaf97c547..efa530977 100644 --- a/docs/tutorials/examples/human_interaction/config.yml +++ b/docs/tutorials/examples/human_interaction/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: assistant: type: native diff --git a/docs/tutorials/examples/human_interaction/index.md b/docs/tutorials/examples/human_interaction/index.md index 582e5d1c7..1e5674fa9 100644 --- a/docs/tutorials/examples/human_interaction/index.md +++ b/docs/tutorials/examples/human_interaction/index.md @@ -43,7 +43,7 @@ This demonstrates how to: ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -59,8 +59,8 @@ from __future__ import annotations import os -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run # set your OpenAI API key here @@ -97,7 +97,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json agents: assistant: type: native diff --git a/docs/tutorials/examples/human_interaction/main.py b/docs/tutorials/examples/human_interaction/main.py index b39d680cf..18101a45c 100644 --- a/docs/tutorials/examples/human_interaction/main.py +++ b/docs/tutorials/examples/human_interaction/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -15,8 +15,8 @@ import os -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run # set your OpenAI API key here diff --git a/docs/tutorials/examples/index.md b/docs/tutorials/examples/index.md index 9a4a46fbe..a1c8c9379 100644 --- a/docs/tutorials/examples/index.md +++ b/docs/tutorials/examples/index.md @@ -89,4 +89,4 @@ Each example includes: - **Code examples**: Python code demonstrating usage patterns - **Explanations**: Detailed descriptions of concepts and approaches -You can find the full source code for these examples in the [GitHub repository](https://github.com/Million-mo/agentpool/tree/main/examples). +You can find the full source code for these examples in the [GitHub repository](https://github.com/Million-mo/wolfharness/tree/main/examples). diff --git a/docs/tutorials/examples/mcp_sampling_elicitation/demo.py b/docs/tutorials/examples/mcp_sampling_elicitation/demo.py index 36e4f9c7b..cd952c05b 100644 --- a/docs/tutorials/examples/mcp_sampling_elicitation/demo.py +++ b/docs/tutorials/examples/mcp_sampling_elicitation/demo.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Demo: Agent using MCP server with code fixer (sampling + elicitation).""" @@ -10,8 +10,8 @@ import anyio -from agentpool import Agent -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness import Agent +from wolfharness_config.mcp_server import StdioMCPServerConfig async def main() -> None: diff --git a/docs/tutorials/examples/mcp_sampling_elicitation/index.md b/docs/tutorials/examples/mcp_sampling_elicitation/index.md index e45e4b688..848fba5f8 100644 --- a/docs/tutorials/examples/mcp_sampling_elicitation/index.md +++ b/docs/tutorials/examples/mcp_sampling_elicitation/index.md @@ -94,7 +94,7 @@ if __name__ == "__main__": ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Demo: Agent using MCP server with code fixer (sampling + elicitation).""" @@ -105,8 +105,8 @@ from pathlib import Path import anyio -from agentpool import Agent -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness import Agent +from wolfharness_config.mcp_server import StdioMCPServerConfig async def main() -> None: diff --git a/docs/tutorials/examples/mcp_servers_yaml/config.yml b/docs/tutorials/examples/mcp_servers_yaml/config.yml index 4ecf8494d..6216d4b90 100644 --- a/docs/tutorials/examples/mcp_servers_yaml/config.yml +++ b/docs/tutorials/examples/mcp_servers_yaml/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json mcp_servers: - "uvx mcp-server-git" diff --git a/docs/tutorials/examples/mcp_servers_yaml/index.md b/docs/tutorials/examples/mcp_servers_yaml/index.md index 2a2611b9e..a68c9bc69 100644 --- a/docs/tutorials/examples/mcp_servers_yaml/index.md +++ b/docs/tutorials/examples/mcp_servers_yaml/index.md @@ -27,7 +27,7 @@ This example demonstrates how to configure MCP servers directly in YAML and use ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -44,8 +44,8 @@ from __future__ import annotations import os -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run PROMPT = "Get the latest commit hash!" @@ -81,15 +81,15 @@ if __name__ == "__main__": ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Example: Two agents working together to explore git commit history.""" from __future__ import annotations -from agentpool import Agent, Team -from agentpool.docs.utils import run +from wolfharness import Agent, Team +from wolfharness.docs.utils import run PICKER = """ @@ -151,7 +151,7 @@ includes the following details: ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json mcp_servers: - "uvx mcp-server-git" diff --git a/docs/tutorials/examples/mcp_servers_yaml/main_py.py b/docs/tutorials/examples/mcp_servers_yaml/main_py.py index e0bc3c004..815bbe67d 100644 --- a/docs/tutorials/examples/mcp_servers_yaml/main_py.py +++ b/docs/tutorials/examples/mcp_servers_yaml/main_py.py @@ -1,13 +1,13 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Example: Two agents working together to explore git commit history.""" from __future__ import annotations -from agentpool import Agent, Team -from agentpool.docs.utils import run +from wolfharness import Agent, Team +from wolfharness.docs.utils import run PICKER = """ diff --git a/docs/tutorials/examples/mcp_servers_yaml/main_yaml.py b/docs/tutorials/examples/mcp_servers_yaml/main_yaml.py index d8f77314c..42854b2f6 100644 --- a/docs/tutorials/examples/mcp_servers_yaml/main_yaml.py +++ b/docs/tutorials/examples/mcp_servers_yaml/main_yaml.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -16,8 +16,8 @@ import os -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run PROMPT = "Get the latest commit hash!" diff --git a/docs/tutorials/examples/mcp_skills/config.yml b/docs/tutorials/examples/mcp_skills/config.yml index 6677a7755..0816a8aab 100644 --- a/docs/tutorials/examples/mcp_skills/config.yml +++ b/docs/tutorials/examples/mcp_skills/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json # Example: MCP-Exposed Skills # # This example demonstrates how to use skills exposed by MCP servers. diff --git a/docs/tutorials/examples/mcp_skills/index.md b/docs/tutorials/examples/mcp_skills/index.md index 82390e723..1bb8abe6a 100644 --- a/docs/tutorials/examples/mcp_skills/index.md +++ b/docs/tutorials/examples/mcp_skills/index.md @@ -233,14 +233,14 @@ def get_pdf_example(filename: str) -> bytes: ```bash # List all available skills (local + MCP) -agentpool run mcp_skills/skill_discoverer "List all available skills" +wolfharness run mcp_skills/skill_discoverer "List all available skills" # Load a specific MCP skill -agentpool run mcp_skills/mcp_skill_user \ +wolfharness run mcp_skills/mcp_skill_user \ "Load the skill://mcp-server-with-prompts/code-review skill" # Auto-route to a skill by short name -agentpool run mcp_skills/mcp_skill_user \ +wolfharness run mcp_skills/mcp_skill_user \ "Load the code-review skill" ``` @@ -290,7 +290,7 @@ Reference not found: 'skill://server/skill/file.md' ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json # Example: MCP-Exposed Skills # # This example demonstrates how to use skills exposed by MCP servers. diff --git a/docs/tutorials/examples/model_comparison/config.yml b/docs/tutorials/examples/model_comparison/config.yml index ae4feb4de..60b3553d7 100644 --- a/docs/tutorials/examples/model_comparison/config.yml +++ b/docs/tutorials/examples/model_comparison/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: overseer: type: native diff --git a/docs/tutorials/examples/model_comparison/index.md b/docs/tutorials/examples/model_comparison/index.md index f0074cf7a..ed237a9a8 100644 --- a/docs/tutorials/examples/model_comparison/index.md +++ b/docs/tutorials/examples/model_comparison/index.md @@ -14,7 +14,7 @@ This example demonstrates comparing different AI models using parallel teams in ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -31,8 +31,8 @@ from __future__ import annotations import os -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run # set your OpenAI API key here @@ -81,7 +81,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json agents: overseer: type: native diff --git a/docs/tutorials/examples/model_comparison/main.py b/docs/tutorials/examples/model_comparison/main.py index 71c4ca0d1..641af2b37 100644 --- a/docs/tutorials/examples/model_comparison/main.py +++ b/docs/tutorials/examples/model_comparison/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -16,8 +16,8 @@ import os -from agentpool import AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run # set your OpenAI API key here diff --git a/docs/tutorials/examples/pytest_style/config.yml b/docs/tutorials/examples/pytest_style/config.yml index a5a96cfd0..eb26d0238 100644 --- a/docs/tutorials/examples/pytest_style/config.yml +++ b/docs/tutorials/examples/pytest_style/config.yml @@ -1,3 +1,3 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json # This example creates agents programmatically - no YAML config needed agents: {} diff --git a/docs/tutorials/examples/pytest_style/index.md b/docs/tutorials/examples/pytest_style/index.md index a1412d863..34b17706c 100644 --- a/docs/tutorials/examples/pytest_style/index.md +++ b/docs/tutorials/examples/pytest_style/index.md @@ -39,7 +39,7 @@ This provides a clean, declarative way to orchestrate multi-agent workflows, sim ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -58,12 +58,12 @@ from __future__ import annotations import os from typing import TYPE_CHECKING -from agentpool.docs.utils import get_config_path, is_pyodide, run -from agentpool.running import node_function, run_nodes_async +from wolfharness.docs.utils import get_config_path, is_pyodide, run +from wolfharness.running import node_function, run_nodes_async if TYPE_CHECKING: - from agentpool import Agent + from wolfharness import Agent # set your OpenAI API key here os.environ["OPENAI_API_KEY"] = os.environ.get("OPENAI_API_KEY", "your_api_key_here") @@ -113,7 +113,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json # This example creates agents programmatically - no YAML config needed agents: {} ``` diff --git a/docs/tutorials/examples/pytest_style/main.py b/docs/tutorials/examples/pytest_style/main.py index c987487b7..364ea1738 100644 --- a/docs/tutorials/examples/pytest_style/main.py +++ b/docs/tutorials/examples/pytest_style/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// @@ -18,12 +18,12 @@ import os from typing import TYPE_CHECKING -from agentpool.docs.utils import get_config_path, is_pyodide, run -from agentpool.running import node_function, run_nodes_async +from wolfharness.docs.utils import get_config_path, is_pyodide, run +from wolfharness.running import node_function, run_nodes_async if TYPE_CHECKING: - from agentpool import Agent + from wolfharness import Agent # set your OpenAI API key here os.environ["OPENAI_API_KEY"] = os.environ.get("OPENAI_API_KEY", "your_api_key_here") diff --git a/docs/tutorials/examples/round_robin/config.yml b/docs/tutorials/examples/round_robin/config.yml index d36071475..bf53e79e6 100644 --- a/docs/tutorials/examples/round_robin/config.yml +++ b/docs/tutorials/examples/round_robin/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json prompts: system_prompts: word_chain: diff --git a/docs/tutorials/examples/round_robin/index.md b/docs/tutorials/examples/round_robin/index.md index 1b0b5b83e..f3f3a3a1b 100644 --- a/docs/tutorials/examples/round_robin/index.md +++ b/docs/tutorials/examples/round_robin/index.md @@ -33,7 +33,7 @@ You can add various conditions to control the conversation: ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Run round-robin example demonstrating cyclic communication pattern.""" @@ -42,8 +42,8 @@ from __future__ import annotations import os -from agentpool.__main__ import run_command # type: ignore[attr-defined] -from agentpool.docs.utils import get_config_path, is_pyodide +from wolfharness.__main__ import run_command # type: ignore[attr-defined] +from wolfharness.docs.utils import get_config_path, is_pyodide # set your OpenAI API key here @@ -69,7 +69,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json prompts: system_prompts: word_chain: diff --git a/docs/tutorials/examples/round_robin/main.py b/docs/tutorials/examples/round_robin/main.py index d4d4f77da..aded0b50a 100644 --- a/docs/tutorials/examples/round_robin/main.py +++ b/docs/tutorials/examples/round_robin/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Run round-robin example demonstrating cyclic communication pattern.""" @@ -8,8 +8,8 @@ import os -from agentpool.__main__ import run_command # type: ignore[attr-defined] -from agentpool.docs.utils import get_config_path, is_pyodide +from wolfharness.__main__ import run_command # type: ignore[attr-defined] +from wolfharness.docs.utils import get_config_path, is_pyodide # set your OpenAI API key here diff --git a/docs/tutorials/examples/skill_uri_loading/config.yml b/docs/tutorials/examples/skill_uri_loading/config.yml index 5dc2b58bb..f3f826921 100644 --- a/docs/tutorials/examples/skill_uri_loading/config.yml +++ b/docs/tutorials/examples/skill_uri_loading/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json # Example: Loading Skills by URI # # This example demonstrates how to load skills using the skill:// URI scheme. diff --git a/docs/tutorials/examples/skill_uri_loading/index.md b/docs/tutorials/examples/skill_uri_loading/index.md index 15d02bae3..9b27bf59f 100644 --- a/docs/tutorials/examples/skill_uri_loading/index.md +++ b/docs/tutorials/examples/skill_uri_loading/index.md @@ -101,16 +101,16 @@ Available skills: ```bash # List available skills -agentpool run skill_uri_loading/skill_lister "List all available skills" +wolfharness run skill_uri_loading/skill_lister "List all available skills" # Load a skill by short name -agentpool run skill_uri_loading/skill_loader "Load the greeting skill" +wolfharness run skill_uri_loading/skill_loader "Load the greeting skill" # Load with arguments -agentpool run skill_uri_loading/skill_loader 'Load greeting with "Alice Company formal"' +wolfharness run skill_uri_loading/skill_loader 'Load greeting with "Alice Company formal"' # Load by full URI -agentpool run skill_uri_loading/skill_loader "Load skill://local/greeting" +wolfharness run skill_uri_loading/skill_loader "Load skill://local/greeting" ``` ## URI Format Reference @@ -127,7 +127,7 @@ See [Skill URI Usage](../../../how-to/configuration/skill-uri-usage.md) for comp ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json # Example: Loading Skills by URI # # This example demonstrates how to load skills using the skill:// URI scheme. diff --git a/docs/tutorials/examples/skill_with_references/config.yml b/docs/tutorials/examples/skill_with_references/config.yml index 3be790611..ec87af5b6 100644 --- a/docs/tutorials/examples/skill_with_references/config.yml +++ b/docs/tutorials/examples/skill_with_references/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json # Example: Skills with References # # This example demonstrates how to create skills with supporting reference files. diff --git a/docs/tutorials/examples/skill_with_references/index.md b/docs/tutorials/examples/skill_with_references/index.md index c826c32db..78e7649ff 100644 --- a/docs/tutorials/examples/skill_with_references/index.md +++ b/docs/tutorials/examples/skill_with_references/index.md @@ -166,15 +166,15 @@ skills/testing/ ```bash # Load the main skill -agentpool run skill_with_references/documentation_helper \ +wolfharness run skill_with_references/documentation_helper \ "Load the documentation-style-guide skill" # Load a reference file -agentpool run skill_with_references/documentation_helper \ +wolfharness run skill_with_references/documentation_helper \ "Load the structure reference from documentation-style-guide" # Explore all references -agentpool run skill_with_references/reference_explorer \ +wolfharness run skill_with_references/reference_explorer \ "Show me all references for documentation-style-guide" ``` @@ -195,7 +195,7 @@ agentpool run skill_with_references/reference_explorer \ ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json # Example: Skills with References # # This example demonstrates how to create skills with supporting reference files. diff --git a/docs/tutorials/examples/structured_response/config.yml b/docs/tutorials/examples/structured_response/config.yml index 322956203..ced956715 100644 --- a/docs/tutorials/examples/structured_response/config.yml +++ b/docs/tutorials/examples/structured_response/config.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json responses: YamlResult: response_schema: diff --git a/docs/tutorials/examples/structured_response/index.md b/docs/tutorials/examples/structured_response/index.md index a2ba1d5e0..f9f228aa5 100644 --- a/docs/tutorials/examples/structured_response/index.md +++ b/docs/tutorials/examples/structured_response/index.md @@ -44,7 +44,7 @@ This demonstrates: ```python # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Example of structured responses defined both in code and YAML.""" @@ -53,8 +53,8 @@ import os from schemez import Schema -from agentpool import Agent, AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import Agent, AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run # set your OpenAI API key here @@ -107,7 +107,7 @@ if __name__ == "__main__": ### `config.yml` ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/wolfharness/refs/heads/main/schema/config-schema.json responses: YamlResult: response_schema: diff --git a/docs/tutorials/examples/structured_response/main.py b/docs/tutorials/examples/structured_response/main.py index bf8568421..c1b126348 100644 --- a/docs/tutorials/examples/structured_response/main.py +++ b/docs/tutorials/examples/structured_response/main.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Example of structured responses defined both in code and YAML.""" @@ -8,8 +8,8 @@ from schemez import Schema -from agentpool import Agent, AgentPool, AgentsManifest -from agentpool.docs.utils import get_config_path, is_pyodide, run +from wolfharness import Agent, AgentPool, AgentsManifest +from wolfharness.docs.utils import get_config_path, is_pyodide, run # set your OpenAI API key here diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md index bc9d7458e..125cfb7c8 100644 --- a/docs/tutorials/index.md +++ b/docs/tutorials/index.md @@ -12,14 +12,14 @@ icon: material/school Simple installation ```bash -uv tool install agentpool +uv tool install wolfharness ``` Multiple extras ```bash -uv tool install agentpool[default, coding] +uv tool install wolfharness[default, coding] ``` ## Available Extras @@ -50,9 +50,9 @@ uv tool install agentpool[default, coding] No installation needed - run directly with uvx: ```bash -uvx --python 3.13 agentpool@latest serve-acp +uvx --python 3.13 wolfharness@latest serve-acp # or -uvx --python 3.13 agentpool@latest serve-acp path/to/agents.yml +uvx --python 3.13 wolfharness@latest serve-acp path/to/agents.yml ``` diff --git a/docs/tutorials/quickstart.md b/docs/tutorials/quickstart.md index dc14eed7f..53b0382d6 100644 --- a/docs/tutorials/quickstart.md +++ b/docs/tutorials/quickstart.md @@ -8,14 +8,14 @@ icon: material/rocket-launch ## ACP Integration (Recommended) -The fastest way to get started is through the **Agent Client Protocol (ACP)**, which integrates agentpool directly into your IDE. +The fastest way to get started is through the **Agent Client Protocol (ACP)**, which integrates wolfharness directly into your IDE. ### One-Line Setup No installation needed - run directly with uvx: ```bash -uvx --python 3.13 agentpool@latest serve-acp agents.yml +uvx --python 3.13 wolfharness@latest serve-acp agents.yml ``` ### IDE Configuration (Zed) @@ -29,7 +29,7 @@ Add to your Zed `settings.json`: "command": "uvx", "args": [ "--python", "3.13", - "agentpool@latest", + "wolfharness@latest", "serve-acp", "path/to/your/agents.yml" ], @@ -65,13 +65,13 @@ Initialize and manage configurations: ```bash # Create starter configuration -agentpool init agents.yml +wolfharness init agents.yml # Add to your configurations -agentpool add agents.yml +wolfharness add agents.yml # Start chatting -agentpool chat assistant +wolfharness chat assistant ``` ## Configured Agents @@ -92,7 +92,7 @@ agents: Use it in code: ```python -from agentpool import AgentPool +from wolfharness import AgentPool async def main(): async with AgentPool("agents.yml") as pool: @@ -110,7 +110,7 @@ if __name__ == "__main__": Quick model interactions without configuration: ```python -from agentpool import run_with_model, run_with_model_sync +from wolfharness import run_with_model, run_with_model_sync # Async usage async def main(): diff --git a/duties.py b/duties.py index 3a70922df..566ff268e 100644 --- a/duties.py +++ b/duties.py @@ -78,13 +78,13 @@ def serve(ctx, *args: str): ctx.run("uv run zensical build") ctx.run("uv run python scripts/reorder_nav.py") # Serve the static site directly (zensical serve would rebuild and undo reorder) - # Create a temp structure to match zensical's URL scheme: /agentpool/ + # Create a temp structure to match zensical's URL scheme: /wolfharness/ import tempfile with tempfile.TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) - (tmp_path / "agentpool").symlink_to(Path("site").resolve()) - print(f"Serving at http://localhost:{port}/agentpool/") + (tmp_path / "wolfharness").symlink_to(Path("site").resolve()) + print(f"Serving at http://localhost:{port}/wolfharness/") ctx.run(f"uv run python -m http.server {port} -d {tmpdir} -b localhost") else: args_str = " " + " ".join(args) if args else "" @@ -122,7 +122,7 @@ def _get_lint_targets(filepath: str | None) -> tuple[str, str, str | None]: return ( ".", "src/", - "src/agentpool/config_resources/*.yml docs/examples/**/config.yml", + "src/wolfharness/config_resources/*.yml docs/examples/**/config.yml", ) path = Path(filepath) @@ -169,7 +169,7 @@ def lint(ctx, filepath: str | None = None): # noqa: D417 # Full project lint - run jsonschema on all config files ctx.run( "uv run check-jsonschema --schemafile schema/config-schema.json " - "src/agentpool/config_resources/*.yml " + "src/wolfharness/config_resources/*.yml " "docs/examples/**/config.yml" ) @@ -198,7 +198,7 @@ def lint_check(ctx, filepath: str | None = None): # noqa: D417 # Full project lint - run jsonschema on all config files ctx.run( "uv run check-jsonschema --schemafile schema/config-schema.json " - "src/agentpool/config_resources/*.yml " + "src/wolfharness/config_resources/*.yml " "docs/examples/**/config.yml" ) @@ -270,7 +270,7 @@ def smoke_test(ctx, timeout: int = 10): # noqa: D417 # Run serve-acp from the wheel in an isolated environment result = subprocess.run( # noqa: PLW1510 - ["timeout", str(timeout), "uvx", "--from", str(wheel), "agentpool", "serve-acp"], + ["timeout", str(timeout), "uvx", "--from", str(wheel), "wolfharness", "serve-acp"], capture_output=True, text=True, ) @@ -308,4 +308,4 @@ def opencode_server(ctx, *args: str): duty opencode-server --port 8080 # Start on custom port """ args_str = " " + " ".join(args) if args else "" - ctx.run(f"uv run agentpool serve-opencode{args_str}") + ctx.run(f"uv run wolfharness serve-opencode{args_str}") diff --git a/examples/__init__.py b/examples/__init__.py index 22fb0f8bc..fd7e22311 100644 --- a/examples/__init__.py +++ b/examples/__init__.py @@ -1 +1 @@ -"""Examples demonstrating agentpool features and usage patterns.""" +"""Examples demonstrating wolfharness features and usage patterns.""" diff --git a/examples/a2a_server_example.py b/examples/a2a_server_example.py index fb6a44350..0e39d910b 100644 --- a/examples/a2a_server_example.py +++ b/examples/a2a_server_example.py @@ -12,8 +12,8 @@ import anyio -from agentpool import Agent, AgentPool -from agentpool_server.a2a_server import A2AServer +from wolfharness import Agent, AgentPool +from wolfharness_server.a2a_server import A2AServer async def main() -> None: diff --git a/examples/aggregating_server_with_agui.py b/examples/aggregating_server_with_agui.py index 351e7b5e8..b36bd7113 100644 --- a/examples/aggregating_server_with_agui.py +++ b/examples/aggregating_server_with_agui.py @@ -12,8 +12,8 @@ import anyio -from agentpool import Agent, AgentPool -from agentpool_server import A2AServer, AggregatingServer, AGUIServer +from wolfharness import Agent, AgentPool +from wolfharness_server import A2AServer, AggregatingServer, AGUIServer async def main() -> None: diff --git a/examples/agui_server_example.py b/examples/agui_server_example.py index c5c872ac5..e94551ef4 100644 --- a/examples/agui_server_example.py +++ b/examples/agui_server_example.py @@ -10,8 +10,8 @@ import anyio -from agentpool import Agent, AgentPool -from agentpool_server.agui_server import AGUIServer +from wolfharness import Agent, AgentPool +from wolfharness_server.agui_server import AGUIServer async def main() -> None: diff --git a/examples/codex_with_mcp_injection.py b/examples/codex_with_mcp_injection.py index 1bbb6c9f9..6912512f3 100644 --- a/examples/codex_with_mcp_injection.py +++ b/examples/codex_with_mcp_injection.py @@ -14,7 +14,7 @@ async def example_with_http_mcp_server(): # Suppose we have a ToolManagerBridge running on port 8000 # (In practice, you'd start the bridge first and get the URL) mcp_servers = { - "agentpool-tools": HttpMcpServer( + "wolfharness-tools": HttpMcpServer( url="http://localhost:8000/mcp", # Optional: if the MCP server requires authentication bearer_token_env_var="AGENTPOOL_MCP_TOKEN", @@ -83,7 +83,7 @@ async def example_with_multiple_mcp_servers(): print() -async def example_integration_with_agentpool(): +async def example_integration_with_wolfharness(): """Example: How this would integrate with AgentPool's ToolManagerBridge. This is conceptual - shows how you'd use a ToolManagerBridge's MCP server @@ -94,7 +94,7 @@ async def example_integration_with_agentpool(): # await bridge.start() # mcp_config = bridge.get_claude_mcp_server_config() # Simulating what that would return: - mcp_config = {"agentpool-tools": {"type": "http", "url": "http://localhost:8765/mcp"}} + mcp_config = {"wolfharness-tools": {"type": "http", "url": "http://localhost:8765/mcp"}} # Convert to CodexClient format mcp_servers = { name: HttpMcpServer(url=config["url"]) @@ -126,6 +126,6 @@ async def example_integration_with_agentpool(): # asyncio.run(example_with_multiple_mcp_servers()) # Example 4: Integration with AgentPool - # asyncio.run(example_integration_with_agentpool()) + # asyncio.run(example_integration_with_wolfharness()) print("Examples ready to run - uncomment the ones you want to try!") diff --git a/examples/ctx_zip_style/http_approach.py b/examples/ctx_zip_style/http_approach.py index 95857147a..0fccb7daf 100644 --- a/examples/ctx_zip_style/http_approach.py +++ b/examples/ctx_zip_style/http_approach.py @@ -5,12 +5,12 @@ cloud sandboxes like E2B that can't reach localhost. """ -from agentpool.resource_providers import StaticResourceProvider -from agentpool.resource_providers.codemode import RemoteCodeModeResourceProvider from exxec_config import LocalExecutionEnvironmentConfig +from wolfharness.resource_providers import StaticResourceProvider +from wolfharness.resource_providers.codemode import RemoteCodeModeResourceProvider -from agentpool import Agent -from agentpool.tools.base import Tool +from wolfharness import Agent +from wolfharness.tools.base import Tool def add_numbers(x: int, y: int) -> int: diff --git a/examples/ctx_zip_style/mcp_approach.py b/examples/ctx_zip_style/mcp_approach.py index 37c7544e2..bf70cd483 100644 --- a/examples/ctx_zip_style/mcp_approach.py +++ b/examples/ctx_zip_style/mcp_approach.py @@ -5,11 +5,11 @@ cloud sandboxes like E2B that can't reach localhost. """ -from agentpool.resource_providers.codemode.remote_mcp_execution import RemoteMCPExecutor import anyio from exxec_config import LocalExecutionEnvironmentConfig +from wolfharness.resource_providers.codemode.remote_mcp_execution import RemoteMCPExecutor -from agentpool.tools.base import Tool +from wolfharness.tools.base import Tool def add_numbers(x: int, y: int) -> int: diff --git a/examples/opencode_question_demo.py b/examples/opencode_question_demo.py index f0586654f..f67088211 100644 --- a/examples/opencode_question_demo.py +++ b/examples/opencode_question_demo.py @@ -9,9 +9,9 @@ from mcp import types -from agentpool import Agent -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider -from agentpool_server.opencode_server.state import ServerState +from wolfharness import Agent +from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider +from wolfharness_server.opencode_server.state import ServerState async def demo_single_select(): diff --git a/inspect_mcp.py b/inspect_mcp.py index cc693279f..4fb81e07c 100644 --- a/inspect_mcp.py +++ b/inspect_mcp.py @@ -4,9 +4,9 @@ import sys -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool/src") +sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") -from agentpool.resource_providers.mcp_provider import MCPResourceProvider +from wolfharness.resource_providers.mcp_provider import MCPResourceProvider async def main(): diff --git a/migrations/README_USAGE.md b/migrations/README_USAGE.md index a0657aaad..fafaca450 100644 --- a/migrations/README_USAGE.md +++ b/migrations/README_USAGE.md @@ -1,6 +1,6 @@ -# Database Migrations for agentpool +# Database Migrations for wolfharness -This directory contains Alembic migrations for the agentpool database schema. +This directory contains Alembic migrations for the wolfharness database schema. ## Overview @@ -73,7 +73,7 @@ alembic downgrade -1 ### Database URL The migration system uses the database URL from: 1. `DATABASE_URL` environment variable -2. Default: `sqlite:///./agentpool.db` (configured in `alembic.ini`) +2. Default: `sqlite:///./wolfharness.db` (configured in `alembic.ini`) ### Environment Setup The `migrations/env.py` file is configured to: @@ -105,7 +105,7 @@ The initial migration (`5ffc5f0266a1`) already includes these new field names. I ### Model Changes When modifying SQLModel models: -1. Make the change in `src/agentpool_storage/sql_provider/models.py` +1. Make the change in `src/wolfharness_storage/sql_provider/models.py` 2. Create a migration: `python scripts/db_migrate.py create "Description" --autogenerate` 3. Review the generated migration file 4. Test the migration locally diff --git a/migrations/__init__.py b/migrations/__init__.py index 901409711..444a23936 100644 --- a/migrations/__init__.py +++ b/migrations/__init__.py @@ -1 +1 @@ -"""Database migrations for agentpool.""" +"""Database migrations for wolfharness.""" diff --git a/migrations/env.py b/migrations/env.py index d40c99658..665bcb089 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -10,7 +10,7 @@ from sqlalchemy.ext.asyncio import async_engine_from_config from sqlmodel import SQLModel -from agentpool_storage.sql_provider.models import * # noqa: F403 +from wolfharness_storage.sql_provider.models import * # noqa: F403 if TYPE_CHECKING: @@ -32,7 +32,7 @@ # Set database URL from environment variable if available if not config.get_main_option("sqlalchemy.url"): - database_url = os.getenv("DATABASE_URL", "sqlite:///./agentpool.db") + database_url = os.getenv("DATABASE_URL", "sqlite:///./wolfharness.db") config.set_main_option("sqlalchemy.url", database_url) diff --git a/migrations/versions/2f915b1f62bd_add_title_to_conversation.py b/migrations/versions/2f915b1f62bd_add_title_to_conversation.py index e1fd81827..7c1f68c90 100644 --- a/migrations/versions/2f915b1f62bd_add_title_to_conversation.py +++ b/migrations/versions/2f915b1f62bd_add_title_to_conversation.py @@ -14,7 +14,7 @@ import sqlalchemy as sa import sqlmodel.sql.sqltypes -from agentpool_storage.sql_provider.models import UTCDateTime +from wolfharness_storage.sql_provider.models import UTCDateTime if TYPE_CHECKING: diff --git a/migrations/versions/5ffc5f0266a1_initial_migration_with_renamed_token_.py b/migrations/versions/5ffc5f0266a1_initial_migration_with_renamed_token_.py index ee0c6ebdd..4a5fb261c 100644 --- a/migrations/versions/5ffc5f0266a1_initial_migration_with_renamed_token_.py +++ b/migrations/versions/5ffc5f0266a1_initial_migration_with_renamed_token_.py @@ -14,7 +14,7 @@ import sqlalchemy as sa import sqlmodel.sql.sqltypes -from agentpool_storage.sql_provider.models import UTCDateTime +from wolfharness_storage.sql_provider.models import UTCDateTime if TYPE_CHECKING: diff --git a/migrations/versions/__init__.py b/migrations/versions/__init__.py index b48d332fe..7f7bc0bc0 100644 --- a/migrations/versions/__init__.py +++ b/migrations/versions/__init__.py @@ -1 +1 @@ -"""Migration versions for agentpool database schema.""" +"""Migration versions for wolfharness database schema.""" diff --git a/mkdocs.yml b/mkdocs.yml index 5c63e9d0e..25f4f01c8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,9 +1,9 @@ site_name: "AgentPool" site_description: "Pydantic-AI based Multi-Agent Framework with YAML-based Agents, Teams, Workflows & Extended ACP / AGUI integration" -repo_url: "https://github.com/Leoyzen/agentpool/" -site_url: https://leoyzen.github.io/agentpool/ +repo_url: "https://github.com/Leoyzen/wolfharness/" +site_url: https://leoyzen.github.io/wolfharness/ site_author: "Leoyzen" -copyright: Copyright © 2025 Leoyzen (forked from phil65/agentpool) +copyright: Copyright © 2025 Leoyzen (forked from phil65/wolfharness) edit_uri: edit/main/docs/ theme: @@ -133,7 +133,7 @@ extra: - icon: fontawesome/brands/github link: https://github.com/Leoyzen - icon: fontawesome/brands/python - link: https://pypi.org/project/agentpool/ + link: https://pypi.org/project/wolfharness/ nav: - Home: index.md @@ -265,7 +265,7 @@ nav: - task: reference/cli/task.md - ACP Meta Fields: reference/acp-meta-field-reference.md - API: - - AgentPool: reference/api/agentpool.md + - AgentPool: reference/api/wolfharness.md - Agent: reference/api/agent.md - MessageNode: reference/api/messagenode.md - EventBus & SessionController: reference/api/eventbus.md @@ -284,7 +284,7 @@ nav: - Telemetry: explanation/telemetry.md - Python Features: explanation/python-features.md - Import Patterns: explanation/import-patterns.md - - Extending AgentPool: explanation/extending-agentpool.md + - Extending AgentPool: explanation/extending-wolfharness.md - Common Patterns: explanation/common-patterns.md - Special Considerations: explanation/special-considerations.md - Usage Examples: explanation/usage-examples.md diff --git a/openspec/changes/rename-agentpool-to-wolfharness/.openspec.yaml b/openspec/changes/rename-agentpool-to-wolfharness/.openspec.yaml new file mode 100644 index 000000000..84cfc1245 --- /dev/null +++ b/openspec/changes/rename-agentpool-to-wolfharness/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-08-06 diff --git a/openspec/changes/rename-agentpool-to-wolfharness/README.md b/openspec/changes/rename-agentpool-to-wolfharness/README.md new file mode 100644 index 000000000..1d028d221 --- /dev/null +++ b/openspec/changes/rename-agentpool-to-wolfharness/README.md @@ -0,0 +1,3 @@ +# rename-agentpool-to-wolfharness + +Mechanical rename of the project from AgentPool (agentpool) to WolfHarness (wolfharness): package, Python namespace, CLI, docs, CI. diff --git a/openspec/changes/rename-agentpool-to-wolfharness/design.md b/openspec/changes/rename-agentpool-to-wolfharness/design.md new file mode 100644 index 000000000..a03ef6ebb --- /dev/null +++ b/openspec/changes/rename-agentpool-to-wolfharness/design.md @@ -0,0 +1,70 @@ +# Design — rename-agentpool-to-wolfharness + +## Overview + +A one-shot mechanical rename of the project from **AgentPool** (`agentpool`) to **WolfHarness** (`wolfharness`). This supersedes the earlier `agentwolf` rename attempt (PR #324, closed, never merged to `main`). + +## Approach + +Reuse the well-tested mechanical-rename script pattern that already existed in the repository (`scripts/rename_to_agentwolf.py`), adapted for the `wolfharness` target. The rename is executed on a branch based on `origin/main` (which never contained the `agentwolf` package code, so there are no `agentwolf` package remnants to clean up). + +### Rename script (`scripts/rename_to_wolfharness.py`) + +Based on the prior `rename_to_agentwolf.py`, modified: + +1. **Directory renames** — `src/agentpool*` → `src/wolfharness*` for all 10 source packages: + - `src/agentpool` → `src/wolfharness` + - `src/agentpool_bot` → `src/wolfharness_bot` + - `src/agentpool_cli` → `src/wolfharness_cli` + - `src/agentpool_commands` → `src/wolfharness_commands` + - `src/agentpool_config` → `src/wolfharness_config` + - `src/agentpool_prompts` → `src/wolfharness_prompts` + - `src/agentpool_server` → `src/wolfharness_server` + - `src/agentpool_storage` → `src/wolfharness_storage` + - `src/agentpool_sync` → `src/wolfharness_sync` + - `src/agentpool_toolsets` → `src/wolfharness_toolsets` + +2. **Content replacements** — ordered longest-first to avoid partial-write collisions: + - `agentpool_config` → `wolfharness_config` + - `agentpool_server` → `wolfharness_server` + - `agentpool_toolsets` → `wolfharness_toolsets` + - `agentpool_storage` → `wolfharness_storage` + - `agentpool_cli` → `wolfharness_cli` + - `agentpool_commands` → `wolfharness_commands` + - `agentpool_prompts` → `wolfharness_prompts` + - `agentpool_sync` → `wolfharness_sync` + - `agentpool_bot` → `wolfharness_bot` + - `agentpool` → `wolfharness` + +3. **Excluded directories** — `.git`, `.venv`, caches, `node_modules`, `.codegraph`, `openspec/changes` (preserve historical spec docs), `.omo`. + +### Manual follow-ups (not covered by the script) + +The script's `FILE_PATTERNS` only covers `*.py`, `*.toml`, `*.yml`, `*.yaml`, `*.md`, `*.cfg`, `*.txt`, `*.rst`, `*.json`. Post-script manual fixes handled: + +- `agentpool.spec` → `wolfharness.spec` (file rename via `git mv` + content replacement). +- `alembic.ini` — `sqlalchemy.url = sqlite:///./agentpool.db` → `wolfharness.db`. +- `Dockerfile` — `ENTRYPOINT ["agentpool", ...]` → `wolfharness`. +- `docs/reference/js/run_code_config.js` — `agentpool` → `wolfharness`. +- `scripts/restart_opencode_server.sh` — `agentpool` → `wolfharness`. +- `src/wolfharness_server/opencode_server/.rules` — log paths `~/.local/state/agentpool/...` → `wolfharness`. +- Remove obsolete `scripts/rename_to_agentwolf.py`. + +### Ruff line-length fixes + +The longer `wolfharness` name pushed a few lines over the 100-char limit. Fixed by reflowing docstrings, splitting string constructions, and adding the missing `# noqa: E402` to one import in `session_controller.py` (matching the existing deliberate circular-import pattern). + +## Verification + +- `uv sync --all-extras` — resolves and builds `wolfharness==2.9.5`. +- `uv run ruff check src/ tests/` — passes. +- `uv run ruff format src/ tests/ --check` — passes. +- `uv run pytest -m unit` — results unchanged from `origin/main` baseline. + +## Out of scope + +- The GitHub repository rename `Leoyzen/agentpool` → `Leoyzen/wolfharness` (repository-settings operation, done after this PR lands). +- First `wolfharness` PyPI release and deprecation notice on the final `agentpool` release (publishing steps). +- Migration guide for users (follow-up). +- Historical `agentwolf` references in archived RFCs and archived openspec changes (document the superseded interim name; left intact). +- `uv.lock`'s `agentpool` dependency (a separate third-party PyPI package pulled by `mknodes`, not the project's own package; left intact). \ No newline at end of file diff --git a/openspec/changes/rename-agentpool-to-wolfharness/proposal.md b/openspec/changes/rename-agentpool-to-wolfharness/proposal.md new file mode 100644 index 000000000..1dff48593 --- /dev/null +++ b/openspec/changes/rename-agentpool-to-wolfharness/proposal.md @@ -0,0 +1,38 @@ +## Why + +The project is currently named **AgentPool** (`agentpool`). The new brand is **WolfHarness** (`wolfharness`). A prior rename attempt to the interim name **AgentWolf** (`agentwolf`) was never merged to `main` (PR #324 was closed). This change renames the project mechanically from `agentpool` to `wolfharness` across the repository, package distribution, Python namespace, CLI, documentation, and CI. + +## What Changes + +- **BREAKING**: Rename the PyPI package `agentpool` → `wolfharness` in `pyproject.toml` (`name`, `[project.scripts]`, entry points, URLs). +- **BREAKING**: Rename all `src/agentpool*` package directories to `src/wolfharness*` and update every Python import (`agentpool.*` → `wolfharness.*`). +- **BREAKING**: Rename the CLI entry point from `agentpool` → `wolfharness`. +- **BREAKING**: Rename the PyInstaller spec `agentpool.spec` → `wolfharness.spec` and update its metadata. +- Rename the Alembic database file `agentpool.db` → `wolfharness.db`. +- Rename Docker entrypoint, docs/js references, shell scripts, and `.rules` log paths from `agentpool` → `wolfharness`. +- Replace `AgentPool` → `WolfHarness` in README title, AGENTS.md, badges, and documentation links. +- Regenerate `uv.lock` so the package resolves as `wolfharness==2.9.5`. +- Add a `scripts/rename_to_wolfharness.py` mechanical-rename script (replaces the obsolete `scripts/rename_to_agentwolf.py`, which is removed). + +## Capabilities + +### New Capabilities + +- `wolfharness-rename`: The mechanical repository, package, namespace, CLI, docs, and CI rename from `agentpool` to `wolfharness`. + +### Modified Capabilities + +- `agent-pool`: The core orchestration capability's package namespace changes from `agentpool` to `wolfharness`. All import paths and module references throughout the framework update to the new namespace. No behavioral requirements change — only the naming surface. + +## Impact + +- `pyproject.toml`: package `name`, `[project.scripts]`, `[project.entry-points."wolfharness.capabilities"]`, project URLs. +- `src/agentpool*` → `src/wolfharness*`: all 10 source package directories and their files. +- Every `.py` file importing `agentpool.*` or `agentpool_server.*` etc. +- `tests/`: all test files referencing `agentpool` import paths. +- `docs/`: README, `docs/reference/js/run_code_config.js`, AGENTS.md references. +- `Dockerfile`, `alembic.ini`, `agentpool.spec` → `wolfharness.spec`, shell scripts, `.rules`. +- `.github/workflows/`: CI references to the package/CLI name. +- `uv.lock`: regenerated to resolve `wolfharness`. +- Historical `agentwolf` references in archived RFCs and archived openspec changes are intentionally left untouched (they document the superseded interim name). +- Historical `agentpool` references in `openspec/changes` (archived) are intentionally excluded per the rename script's design. \ No newline at end of file diff --git a/openspec/changes/rename-agentpool-to-wolfharness/specs/wolfharness-rename/spec.md b/openspec/changes/rename-agentpool-to-wolfharness/specs/wolfharness-rename/spec.md new file mode 100644 index 000000000..c4b2efb8e --- /dev/null +++ b/openspec/changes/rename-agentpool-to-wolfharness/specs/wolfharness-rename/spec.md @@ -0,0 +1,34 @@ +## ADDED Requirements + +### Requirement: Package and namespace renamed to wolfharness + +The PyPI package, Python import namespace, and CLI entry point SHALL be renamed from `agentpool` to `wolfharness`. All source packages under `src/wolfharness*` SHALL be importable via the `wolfharness` namespace. + +#### Scenario: Package builds and installs as wolfharness + +- **WHEN** `uv sync --all-extras` is run +- **THEN** the project SHALL resolve and build as `wolfharness==2.9.5` + +#### Scenario: Source directories renamed + +- **WHEN** the repository is inspected +- **THEN** `src/agentpool*` directories SHALL NOT exist +- **AND** `src/wolfharness*` directories SHALL exist for all 10 source packages (core, bot, cli, commands, config, prompts, server, storage, sync, toolsets) + +#### Scenario: CLI entry point renamed + +- **WHEN** the CLI is invoked +- **THEN** the `agentpool` command SHALL be replaced by `wolfharness` +- **AND** `wolfharness --version`, `wolfharness serve-acp`, `wolfharness serve-opencode`, `wolfharness serve-mcp`, `wolfharness serve-agui`, `wolfharness serve-api` SHALL be the canonical commands + +## MODIFIED Requirements + +### Requirement: AgentPool package namespace SHALL be renamed to wolfharness + +The core orchestration capability's package namespace SHALL change from `agentpool` to `wolfharness`. All import paths and module references throughout the framework SHALL update to the new namespace. No behavioral requirements change — only the naming surface. + +#### Scenario: Imports resolve under wolfharness namespace + +- **WHEN** a consumer imports `wolfharness` +- **THEN** the import SHALL resolve to the renamed package +- **AND** no `agentpool` import path SHALL remain in `src/`, `tests/`, or active configuration files \ No newline at end of file diff --git a/openspec/changes/rename-agentpool-to-wolfharness/tasks.md b/openspec/changes/rename-agentpool-to-wolfharness/tasks.md new file mode 100644 index 000000000..04be04661 --- /dev/null +++ b/openspec/changes/rename-agentpool-to-wolfharness/tasks.md @@ -0,0 +1,48 @@ +# Tasks — rename-agentpool-to-wolfharness + +## 1. Create the mechanical rename script + +- [x] 1.1 Adapt `scripts/rename_to_agentwolf.py` → `scripts/rename_to_wolfharness.py` (replace `agentpool`→`agentwolf` with `agentpool`→`wolfharness` in `SRC_DIRS_TO_RENAME` and `REPLACEMENTS`) +- [x] 1.2 Remove the obsolete `scripts/rename_to_agentwolf.py` + +## 2. Execute the directory rename + +- [x] 2.1 Run `python3 scripts/rename_to_wolfharness.py` (dry-run first to verify) +- [x] 2.2 Verify all 10 `src/agentpool*` directories renamed to `src/wolfharness*` +- [x] 2.3 Verify entry-point files exist post-rename (`wolfharness/__init__.py`, `wolfharness_cli/__init__.py`) + +## 3. Replace references in content + +- [x] 3.1 Run the reference-replacement step (1421 files updated) +- [x] 3.2 Rename `agentpool.spec` → `wolfharness.spec` via `git mv` and replace content references +- [x] 3.3 Update `alembic.ini` — `sqlite:///./agentpool.db` → `wolfharness.db` +- [x] 3.4 Update `Dockerfile` — `ENTRYPOINT ["agentpool", ...]` → `wolfharness` +- [x] 3.5 Update `docs/reference/js/run_code_config.js` — `agentpool` → `wolfharness` +- [x] 3.6 Update `scripts/restart_opencode_server.sh` — `agentpool` → `wolfharness` +- [x] 3.7 Update `src/wolfharness_server/opencode_server/.rules` — log paths → `wolfharness` +- [x] 3.8 Verify no residual `agentpool` references remain (excluding `openspec/changes` archives and the third-party `agentpool` PyPI dependency in `uv.lock`) + +## 4. Fix lint issues from the longer name + +- [x] 4.1 Reflow docstring in `src/wolfharness/agents/native_agent/process_history_capability.py` (E501) +- [x] 4.2 Reflow docstring in `src/wolfharness/capabilities/skill_manager_cap.py` (E501) +- [x] 4.3 Split line in `src/wolfharness/repomap/languages.py` (E501) +- [x] 4.4 Split line in `src/wolfharness_config/capabilities.py` (E501) +- [x] 4.5 Add missing `# noqa: E402` to `session_controller.py` import (matches existing circular-import pattern) +- [x] 4.6 Split line in `tests/servers/opencode_server/test_dedup.py` (E501) + +## 5. Regenerate lockfile + +- [x] 5.1 Run `uv sync --all-extras` to regenerate `uv.lock` and verify `wolfharness==2.9.5` builds + +## 6. Verify + +- [x] 6.1 `uv run ruff check src/ tests/` passes +- [x] 6.2 `uv run ruff format src/ tests/ --check` passes +- [ ] 6.3 `uv run pytest -m unit` results unchanged from `origin/main` baseline + +## 7. Deliverable + +- [x] 7.1 Commit the rename as a single atomic commit +- [ ] 7.2 Push branch and open PR against `origin/main` +- [ ] 7.3 Link PR to issue #357 and milestone 2 ("Rebrand: AgentPool → WolfHarness") \ No newline at end of file diff --git a/openspec/specs/acp-single-execution-path/spec.md b/openspec/specs/acp-single-execution-path/spec.md index b8b4ef410..86ae9f357 100644 --- a/openspec/specs/acp-single-execution-path/spec.md +++ b/openspec/specs/acp-single-execution-path/spec.md @@ -34,7 +34,7 @@ The dead code path in `acp_agent.py` (lines 656-698) that calls `session.process - **THEN** `_acp_sessions` SHALL be iterated for `ACPSession` cleanup, while lifecycle clearing SHALL be delegated to `SessionController` ### Requirement: RunFailedEvent SHALL have a single type definition -There SHALL be exactly one `RunFailedEvent` type, defined in `agentpool/agents/events/events.py` and published via `RunHandle.fail()` to the EventBus. The signal-based `BaseAgent.RunFailedEvent` inner class SHALL be removed. +There SHALL be exactly one `RunFailedEvent` type, defined in `wolfharness/agents/events/events.py` and published via `RunHandle.fail()` to the EventBus. The signal-based `BaseAgent.RunFailedEvent` inner class SHALL be removed. #### Scenario: Run failure reporting - **WHEN** a run fails during execution diff --git a/openspec/specs/graph-visualization/spec.md b/openspec/specs/graph-visualization/spec.md index 5d08258f9..c4df1797d 100644 --- a/openspec/specs/graph-visualization/spec.md +++ b/openspec/specs/graph-visualization/spec.md @@ -12,5 +12,5 @@ AgentPool SHALL enable Mermaid diagram generation for YAML-defined team and work - **THEN** `GraphBuilder` generates a Mermaid diagram showing the node chain #### Scenario: CLI diagram access -- **WHEN** user runs `agentpool visualize ` +- **WHEN** user runs `wolfharness visualize ` - **THEN** a Mermaid diagram is printed for the specified YAML team/workflow diff --git a/openspec/specs/ws-server-integration/spec.md b/openspec/specs/ws-server-integration/spec.md index a0543cfa9..b574fe963 100644 --- a/openspec/specs/ws-server-integration/spec.md +++ b/openspec/specs/ws-server-integration/spec.md @@ -26,18 +26,18 @@ The `serve()` function SHALL dispatch to `_serve_streamable_http()` when the tra - **THEN** the resulting server SHALL use `StreamableHTTPTransport` when starting the ACP agent ### Requirement: CLI --port and --host flags for serve-acp -The `agentpool serve-acp` CLI command SHALL accept `--port` and `--host` optional flags. When provided, these flags SHALL create a `StreamableHTTPTransport` with the specified values. When neither flag is provided, the CLI SHALL default to stdio transport. +The `wolfharness serve-acp` CLI command SHALL accept `--port` and `--host` optional flags. When provided, these flags SHALL create a `StreamableHTTPTransport` with the specified values. When neither flag is provided, the CLI SHALL default to stdio transport. #### Scenario: CLI with --port flag -- **WHEN** the user runs `agentpool serve-acp config.yml --port 8080` +- **WHEN** the user runs `wolfharness serve-acp config.yml --port 8080` - **THEN** the CLI SHALL create `StreamableHTTPTransport(port=8080)` and pass it to the ACP server #### Scenario: CLI with --port and --host flags -- **WHEN** the user runs `agentpool serve-acp config.yml --host 0.0.0.0 --port 9000` +- **WHEN** the user runs `wolfharness serve-acp config.yml --host 0.0.0.0 --port 9000` - **THEN** the CLI SHALL create `StreamableHTTPTransport(host="0.0.0.0", port=9000)` #### Scenario: CLI without transport flags -- **WHEN** the user runs `agentpool serve-acp config.yml` +- **WHEN** the user runs `wolfharness serve-acp config.yml` - **THEN** the CLI SHALL use the default stdio transport (no change from current behavior) ### Requirement: YAML config schema extension diff --git a/pyproject.toml b/pyproject.toml index 057f89134..b26d0dc52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ #:tombi schema.strict = false [project] -name = "agentpool" +name = "wolfharness" version = "2.9.5" description = "Pydantic-AI based Multi-Agent Framework with YAML-based Agents, Teams, Workflows & Extended ACP / AGUI integration" readme = "README.md" @@ -85,33 +85,33 @@ dependencies = [ ] [project.urls] -"Code coverage" = "https://app.codecov.io/gh/phil65/agentpool" -Discussions = "https://github.com/phil65/agentpool/discussions" -Documentation = "https://phil65.github.io/agentpool/" -Issues = "https://github.com/phil65/agentpool/issues" -Source = "https://github.com/phil65/agentpool" +"Code coverage" = "https://app.codecov.io/gh/phil65/wolfharness" +Discussions = "https://github.com/phil65/wolfharness/discussions" +Documentation = "https://phil65.github.io/wolfharness/" +Issues = "https://github.com/phil65/wolfharness/issues" +Source = "https://github.com/phil65/wolfharness" [project.scripts] acp-bridge = "acp.bridge.__main__:main" -agentpool = "agentpool_cli.__main__:cli" -agentpool-db = "agentpool_storage.sql_provider.cli:main" +wolfharness = "wolfharness_cli.__main__:cli" +wolfharness-db = "wolfharness_storage.sql_provider.cli:main" -[project.entry-points."agentpool.capabilities"] -mcp = "agentpool.capabilities.mcp_server_cap:McpServerCap" -function_tools = "agentpool.capabilities.function_toolset:FunctionToolsetCapability" -subagent = "agentpool.capabilities.subagent_capability:SubagentCapability" -skill = "agentpool.capabilities.skill_manager_cap:SkillManagerCap" -combined = "agentpool.capabilities.combined_toolset:CombinedToolsetCapability" -code_mode = "agentpool.capabilities.code_mode_capability:CodeModeCapability" -tool_display = "agentpool.capabilities.tool_display_capability:ToolDisplayCapability" -question = "agentpool.capabilities.question:QuestionCapability" -background_task = "agentpool.capabilities.background_task:BackgroundTaskCapability" +[project.entry-points."wolfharness.capabilities"] +mcp = "wolfharness.capabilities.mcp_server_cap:McpServerCap" +function_tools = "wolfharness.capabilities.function_toolset:FunctionToolsetCapability" +subagent = "wolfharness.capabilities.subagent_capability:SubagentCapability" +skill = "wolfharness.capabilities.skill_manager_cap:SkillManagerCap" +combined = "wolfharness.capabilities.combined_toolset:CombinedToolsetCapability" +code_mode = "wolfharness.capabilities.code_mode_capability:CodeModeCapability" +tool_display = "wolfharness.capabilities.tool_display_capability:ToolDisplayCapability" +question = "wolfharness.capabilities.question:QuestionCapability" +background_task = "wolfharness.capabilities.background_task:BackgroundTaskCapability" [project.entry-points."fsspec.specs"] acp = "acp.filesystem:ACPFileSystem" [project.entry-points."jinjarope.environment"] -agentpool = "agentpool.utils:setup_env" +wolfharness = "wolfharness.utils:setup_env" [project.entry-points."universal_pathlib.implementations"] acp = "acp.filesystem:ACPPath" @@ -231,35 +231,35 @@ skip_covered = true [tool.importlinter] root_packages = [ - "agentpool", - "agentpool_config", - "agentpool_server", - "agentpool_toolsets", - "agentpool_storage", - "agentpool_cli", - "agentpool_commands", - "agentpool_prompts", + "wolfharness", + "wolfharness_config", + "wolfharness_server", + "wolfharness_toolsets", + "wolfharness_storage", + "wolfharness_cli", + "wolfharness_commands", + "wolfharness_prompts", "acp", ] [[tool.importlinter.contracts]] name = "Server must not import from core app logic" type = "forbidden" -source_modules = ["agentpool_server"] -forbidden_modules = ["agentpool_commands", "agentpool_cli"] +source_modules = ["wolfharness_server"] +forbidden_modules = ["wolfharness_commands", "wolfharness_cli"] allow_indirect_imports = true [[tool.importlinter.contracts]] name = "Config must not import from core" type = "forbidden" -source_modules = ["agentpool_config"] +source_modules = ["wolfharness_config"] forbidden_modules = [ - "agentpool", - "agentpool_server", - "agentpool_toolsets", - "agentpool_storage", - "agentpool_commands", - "agentpool_cli" + "wolfharness", + "wolfharness_server", + "wolfharness_toolsets", + "wolfharness_storage", + "wolfharness_commands", + "wolfharness_cli" ] # Pre-existing direct violations are listed in ignore_imports; remove entries # as imports are fixed during Phase 7 refactoring. Indirect violations are @@ -267,91 +267,91 @@ forbidden_modules = [ # are untangled (see OpenSpec thin-wrapper-refactor Phase 7 tasks). allow_indirect_imports = true ignore_imports = [ - "agentpool_config.agentpool_tools -> agentpool.prompts.conversion_manager", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.agent_cli", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.bash", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.delete_path", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.download_file", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.execute_code", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.grep", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.list_directory", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.question", - "agentpool_config.agentpool_tools -> agentpool.tool_impls.read", - "agentpool_config.agentpool_tools -> agentpool.tools.base", - "agentpool_config.compaction -> agentpool.messaging.compaction", - "agentpool_config.conditions -> agentpool.talk.registry", - "agentpool_config.conditions -> agentpool.utils.inspection", - "agentpool_config.conditions -> agentpool.utils.time_utils", - "agentpool_config.durable -> agentpool.utils.parse_time", - "agentpool_config.event_handlers -> agentpool.agents.events", - "agentpool_config.event_handlers -> agentpool.agents.events.builtin_handlers", - "agentpool_config.event_handlers -> agentpool.agents.events.tts_handlers", - "agentpool_config.event_handlers -> agentpool.common_types", - "agentpool_config.event_handlers -> agentpool.utils.importing", - "agentpool_config.events -> agentpool.messaging.events", - "agentpool_config.forward_targets -> agentpool", - "agentpool_config.forward_targets -> agentpool.messaging", - "agentpool_config.forward_targets -> agentpool.utils.inspection", - "agentpool_config.forward_targets -> agentpool.utils.model_helpers", - "agentpool_config.forward_targets -> agentpool.utils.time_utils", - "agentpool_config.hooks -> agentpool.hooks", - "agentpool_config.hooks -> agentpool.hooks.base", - "agentpool_config.knowledge -> agentpool.prompts.prompts", - "agentpool_config.loaders -> agentpool.common_types", - "agentpool_config.loaders -> agentpool.mime_utils", - "agentpool_config.loaders -> agentpool.utils.importing", - "agentpool_config.nodes -> agentpool.common_types", - "agentpool_config.prompt_hubs -> agentpool_prompts.braintrust_hub", - "agentpool_config.prompt_hubs -> agentpool_prompts.fabric", - "agentpool_config.prompt_hubs -> agentpool_prompts.langfuse_hub", - "agentpool_config.prompt_hubs -> agentpool_prompts.promptlayer_provider", - "agentpool_config.prompts -> agentpool.log", - "agentpool_config.session -> agentpool.utils.parse_time", - "agentpool_config.session -> agentpool.utils.time_utils", - "agentpool_config.storage -> agentpool_storage.acp_provider", - "agentpool_config.storage -> agentpool_storage.base", - "agentpool_config.storage -> agentpool_storage.file_provider", - "agentpool_config.storage -> agentpool_storage.memory_provider", - "agentpool_config.storage -> agentpool_storage.opencode_provider", - "agentpool_config.storage -> agentpool_storage.sql_provider", - "agentpool_config.storage -> agentpool_storage.zed_provider", - "agentpool_config.task -> agentpool.agents", - "agentpool_config.task -> agentpool.prompts.prompts", - "agentpool_config.task -> agentpool.tools.base", - "agentpool_config.task -> agentpool.utils.model_capabilities", - "agentpool_config.tools -> agentpool.tools.base", - "agentpool_config.tools -> agentpool.utils.parse_time", - "agentpool_config.toolsets -> agentpool.prompts.conversion_manager", - "agentpool_config.toolsets -> agentpool.capabilities.combined_toolset", - "agentpool_config.toolsets -> agentpool.capabilities.filtered_toolset", - "agentpool_config.toolsets -> agentpool.capabilities.function_toolset", - "agentpool_config.toolsets -> agentpool.models.model_configs", - "agentpool_config.toolsets -> agentpool.utils.importing", - "agentpool_config.toolsets -> agentpool_toolsets.builtin", - "agentpool_config.toolsets -> agentpool_toolsets.builtin.code", - "agentpool_config.toolsets -> agentpool_toolsets.builtin.debug", - "agentpool_config.toolsets -> agentpool_toolsets.builtin.subagent_tools", - "agentpool_config.toolsets -> agentpool_toolsets.builtin.workers", - "agentpool_config.toolsets -> agentpool_toolsets.composio_toolset", - "agentpool_config.toolsets -> agentpool_toolsets.config_creation", - "agentpool_config.toolsets -> agentpool_toolsets.cron", - "agentpool_config.toolsets -> agentpool_toolsets.entry_points", - "agentpool_config.toolsets -> agentpool_toolsets.fsspec_toolset", - "agentpool_config.toolsets -> agentpool_toolsets.mcp_discovery.toolset", - "agentpool_config.toolsets -> agentpool_toolsets.notifications", - "agentpool_config.toolsets -> agentpool_toolsets.openapi", - "agentpool_config.toolsets -> agentpool_toolsets.search_toolset", - "agentpool_config.toolsets -> agentpool_toolsets.vfs_toolset", + "wolfharness_config.wolfharness_tools -> wolfharness.prompts.conversion_manager", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.agent_cli", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.bash", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.delete_path", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.download_file", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.execute_code", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.grep", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.list_directory", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.question", + "wolfharness_config.wolfharness_tools -> wolfharness.tool_impls.read", + "wolfharness_config.wolfharness_tools -> wolfharness.tools.base", + "wolfharness_config.compaction -> wolfharness.messaging.compaction", + "wolfharness_config.conditions -> wolfharness.talk.registry", + "wolfharness_config.conditions -> wolfharness.utils.inspection", + "wolfharness_config.conditions -> wolfharness.utils.time_utils", + "wolfharness_config.durable -> wolfharness.utils.parse_time", + "wolfharness_config.event_handlers -> wolfharness.agents.events", + "wolfharness_config.event_handlers -> wolfharness.agents.events.builtin_handlers", + "wolfharness_config.event_handlers -> wolfharness.agents.events.tts_handlers", + "wolfharness_config.event_handlers -> wolfharness.common_types", + "wolfharness_config.event_handlers -> wolfharness.utils.importing", + "wolfharness_config.events -> wolfharness.messaging.events", + "wolfharness_config.forward_targets -> wolfharness", + "wolfharness_config.forward_targets -> wolfharness.messaging", + "wolfharness_config.forward_targets -> wolfharness.utils.inspection", + "wolfharness_config.forward_targets -> wolfharness.utils.model_helpers", + "wolfharness_config.forward_targets -> wolfharness.utils.time_utils", + "wolfharness_config.hooks -> wolfharness.hooks", + "wolfharness_config.hooks -> wolfharness.hooks.base", + "wolfharness_config.knowledge -> wolfharness.prompts.prompts", + "wolfharness_config.loaders -> wolfharness.common_types", + "wolfharness_config.loaders -> wolfharness.mime_utils", + "wolfharness_config.loaders -> wolfharness.utils.importing", + "wolfharness_config.nodes -> wolfharness.common_types", + "wolfharness_config.prompt_hubs -> wolfharness_prompts.braintrust_hub", + "wolfharness_config.prompt_hubs -> wolfharness_prompts.fabric", + "wolfharness_config.prompt_hubs -> wolfharness_prompts.langfuse_hub", + "wolfharness_config.prompt_hubs -> wolfharness_prompts.promptlayer_provider", + "wolfharness_config.prompts -> wolfharness.log", + "wolfharness_config.session -> wolfharness.utils.parse_time", + "wolfharness_config.session -> wolfharness.utils.time_utils", + "wolfharness_config.storage -> wolfharness_storage.acp_provider", + "wolfharness_config.storage -> wolfharness_storage.base", + "wolfharness_config.storage -> wolfharness_storage.file_provider", + "wolfharness_config.storage -> wolfharness_storage.memory_provider", + "wolfharness_config.storage -> wolfharness_storage.opencode_provider", + "wolfharness_config.storage -> wolfharness_storage.sql_provider", + "wolfharness_config.storage -> wolfharness_storage.zed_provider", + "wolfharness_config.task -> wolfharness.agents", + "wolfharness_config.task -> wolfharness.prompts.prompts", + "wolfharness_config.task -> wolfharness.tools.base", + "wolfharness_config.task -> wolfharness.utils.model_capabilities", + "wolfharness_config.tools -> wolfharness.tools.base", + "wolfharness_config.tools -> wolfharness.utils.parse_time", + "wolfharness_config.toolsets -> wolfharness.prompts.conversion_manager", + "wolfharness_config.toolsets -> wolfharness.capabilities.combined_toolset", + "wolfharness_config.toolsets -> wolfharness.capabilities.filtered_toolset", + "wolfharness_config.toolsets -> wolfharness.capabilities.function_toolset", + "wolfharness_config.toolsets -> wolfharness.models.model_configs", + "wolfharness_config.toolsets -> wolfharness.utils.importing", + "wolfharness_config.toolsets -> wolfharness_toolsets.builtin", + "wolfharness_config.toolsets -> wolfharness_toolsets.builtin.code", + "wolfharness_config.toolsets -> wolfharness_toolsets.builtin.debug", + "wolfharness_config.toolsets -> wolfharness_toolsets.builtin.subagent_tools", + "wolfharness_config.toolsets -> wolfharness_toolsets.builtin.workers", + "wolfharness_config.toolsets -> wolfharness_toolsets.composio_toolset", + "wolfharness_config.toolsets -> wolfharness_toolsets.config_creation", + "wolfharness_config.toolsets -> wolfharness_toolsets.cron", + "wolfharness_config.toolsets -> wolfharness_toolsets.entry_points", + "wolfharness_config.toolsets -> wolfharness_toolsets.fsspec_toolset", + "wolfharness_config.toolsets -> wolfharness_toolsets.mcp_discovery.toolset", + "wolfharness_config.toolsets -> wolfharness_toolsets.notifications", + "wolfharness_config.toolsets -> wolfharness_toolsets.openapi", + "wolfharness_config.toolsets -> wolfharness_toolsets.search_toolset", + "wolfharness_config.toolsets -> wolfharness_toolsets.vfs_toolset", ] [[tool.importlinter.contracts]] name = "ACP package must not import from server" type = "forbidden" source_modules = ["acp"] -forbidden_modules = ["agentpool_server", "agentpool_commands", "agentpool_cli"] -# No direct violations exist; all violations are indirect (acp -> agentpool.utils +forbidden_modules = ["wolfharness_server", "wolfharness_commands", "wolfharness_cli"] +# No direct violations exist; all violations are indirect (acp -> wolfharness.utils # -> ... -> server). Indirect checks are disabled via allow_indirect_imports -# until the downstream agentpool.common_types -> agentpool_server chain is fixed +# until the downstream wolfharness.common_types -> wolfharness_server chain is fixed # (see OpenSpec thin-wrapper-refactor Phase 7 tasks). allow_indirect_imports = true @@ -442,11 +442,11 @@ ignore_missing_imports = true [[tool.mypy.overrides]] module = [ - "agentpool_bot.channels.telegram", - "agentpool_bot.channels.slack", - "agentpool_toolsets.composio_toolset", - "agentpool_prompts.langfuse_hub", - "agentpool_prompts.braintrust_hub", + "wolfharness_bot.channels.telegram", + "wolfharness_bot.channels.slack", + "wolfharness_toolsets.composio_toolset", + "wolfharness_prompts.langfuse_hub", + "wolfharness_prompts.braintrust_hub", ] disable_error_code = [ "type-arg", @@ -520,14 +520,14 @@ constraint-dependencies = ["extism-sys<1.13.0"] [tool.uv.build-backend] module-name = [ - "agentpool", - "agentpool_config", - "agentpool_cli", - "agentpool_commands", - "agentpool_storage", - "agentpool_prompts", - "agentpool_server", - "agentpool_toolsets", + "wolfharness", + "wolfharness_config", + "wolfharness_cli", + "wolfharness_commands", + "wolfharness_storage", + "wolfharness_prompts", + "wolfharness_server", + "wolfharness_toolsets", "acp", ] wheel-exclude = [ diff --git a/reproduce_skill_bug.py b/reproduce_skill_bug.py index a7d94785e..ae5c80018 100644 --- a/reproduce_skill_bug.py +++ b/reproduce_skill_bug.py @@ -1,6 +1,6 @@ """Quick red flag test: verify skill reference loading against local MCP scratchpad. -Run from packages/agentpool directory: +Run from packages/wolfharness directory: uv run python reproduce_skill_bug.py """ @@ -10,11 +10,11 @@ import sys -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool/src") +sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") -from agentpool.resource_providers.mcp_provider import MCPResourceProvider +from wolfharness.resource_providers.mcp_provider import MCPResourceProvider -from agentpool.skills.uri_resolver import SkillURIResolver +from wolfharness.skills.uri_resolver import SkillURIResolver async def main(): diff --git a/ruff.toml b/ruff.toml index cbc66ce28..591197d8d 100644 --- a/ruff.toml +++ b/ruff.toml @@ -143,24 +143,24 @@ max-complexity = 15 "TC001", ] # Examples don't need TYPE_CHECKING imports # Team comm tools: ctx parameter is pydantic-ai internal, not described in docstrings -"src/agentpool/capabilities/team_comm_capability.py" = ["D417"] +"src/wolfharness/capabilities/team_comm_capability.py" = ["D417"] # Viking tools: ctx is pydantic-ai internal, BLE001 is intentional for tool error handling -"src/agentpool/capabilities/viking/tools.py" = [ +"src/wolfharness/capabilities/viking/tools.py" = [ "D417", "BLE001", "TRY300", "PLR0915" ] # Viking capability: BLE001 for safe SkillResource defaults, TRY300 for early-return pattern -"src/agentpool/capabilities/viking/__init__.py" = ["BLE001", "TRY300"] +"src/wolfharness/capabilities/viking/__init__.py" = ["BLE001", "TRY300"] # Viking instructions: E501 for markdown table lines -"src/agentpool/capabilities/viking/instructions.py" = ["E501"] +"src/wolfharness/capabilities/viking/instructions.py" = ["E501"] # Resource resolver/capability: PLR0911 for multi-return routing logic, TRY300 for early-return pattern -"src/agentpool/capabilities/resource_resolver.py" = ["PLR0911"] -"src/agentpool/capabilities/resource_capability.py" = ["PLR0911", "TRY300"] +"src/wolfharness/capabilities/resource_resolver.py" = ["PLR0911"] +"src/wolfharness/capabilities/resource_capability.py" = ["PLR0911", "TRY300"] # Background task capability: PLR0915/PLR0911 for complex lifecycle/tool methods, TRY301 for re-raise pattern -"src/agentpool/capabilities/background_task/capability.py" = ["PLR0915", "TRY301"] -"src/agentpool/capabilities/background_task/manager.py" = ["PLR0915", "PLR0911"] +"src/wolfharness/capabilities/background_task/capability.py" = ["PLR0915", "TRY301"] +"src/wolfharness/capabilities/background_task/manager.py" = ["PLR0915", "PLR0911"] # Question tool tests: RUF001 — fullwidth CJK punctuation is intentional test data # for verifying CJK text parsing in the question_for_user XML "tests/tool_impls/question/test_question_for_user.py" = ["RUF001"] diff --git a/schema/config-schema.json b/schema/config-schema.json index 2950b61a2..97f3d8463 100644 --- a/schema/config-schema.json +++ b/schema/config-schema.json @@ -988,7 +988,7 @@ }, "AGUIAgentConfig": { "additionalProperties": false, - "description": "Configuration for AG-UI protocol agents.\n\nAG-UI agents connect to remote HTTP endpoints that implement the AG-UI protocol,\nenabling integration of any AG-UI compatible server into the agentpool pool.\n\nExample:\n ```yaml\n agents:\n remote_assistant:\n type: agui\n endpoint: http://localhost:8000/agent/run\n timeout: 30.0\n headers:\n X-API-Key: ${API_KEY}\n tools:\n - import_path: mymodule.my_tool_function\n\n managed_agent:\n endpoint: http://localhost:8765/agent/run\n startup_command: \"uv run ag-ui-server config.yml\"\n startup_delay: 3.0\n tools:\n - import_path: package.tool_one\n - import_path: package.tool_two\n ```", + "description": "Configuration for AG-UI protocol agents.\n\nAG-UI agents connect to remote HTTP endpoints that implement the AG-UI protocol,\nenabling integration of any AG-UI compatible server into the wolfharness pool.\n\nExample:\n ```yaml\n agents:\n remote_assistant:\n type: agui\n endpoint: http://localhost:8000/agent/run\n timeout: 30.0\n headers:\n X-API-Key: ${API_KEY}\n tools:\n - import_path: mymodule.my_tool_function\n\n managed_agent:\n endpoint: http://localhost:8765/agent/run\n startup_command: \"uv run ag-ui-server config.yml\"\n startup_delay: 3.0\n tools:\n - import_path: package.tool_one\n - import_path: package.tool_two\n ```", "properties": { "name": { "default": null, @@ -1836,7 +1836,7 @@ "service_name": { "default": null, "examples": [ - "agentpool", + "wolfharness", "my-ai-service", "production-bot" ], @@ -2730,7 +2730,7 @@ "service_name": { "default": null, "examples": [ - "agentpool", + "wolfharness", "my-ai-service", "production-bot" ], @@ -3728,7 +3728,7 @@ "service_name": { "default": null, "examples": [ - "agentpool", + "wolfharness", "my-ai-service", "production-bot" ], @@ -5889,7 +5889,7 @@ ], "default": null, "description": "System prompt for the agent.\n Can be a string or list of strings/prompt configs", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/system_prompts_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/system_prompts_configuration/", "examples": [ "You are a helpful assistant.", [ @@ -6190,7 +6190,7 @@ "type": "boolean" }, "tools": { - "description": "Tools and toolsets to expose to this Claude Code agent via MCP bridge.\n\nSupports both single tools and toolsets. These will be started as an\nin-process MCP server and made available to Claude Code.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/tool_configuration/", + "description": "Tools and toolsets to expose to this Claude Code agent via MCP bridge.\n\nSupports both single tools and toolsets. These will be started as an\nin-process MCP server and made available to Claude Code.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/tool_configuration/", "examples": [ [ { @@ -6581,7 +6581,7 @@ }, "ClaudeStorageConfig": { "additionalProperties": false, - "description": "Claude Code native storage format configuration.\n\nReads/writes to Claude Code's native JSONL format in ~/.claude/projects/.\nUseful for sharing conversation history between agentpool and Claude Code CLI.", + "description": "Claude Code native storage format configuration.\n\nReads/writes to Claude Code's native JSONL format in ~/.claude/projects/.\nUseful for sharing conversation history between wolfharness and Claude Code CLI.", "properties": { "type": { "const": "claude", @@ -7237,7 +7237,7 @@ }, "CodexAgentConfig": { "additionalProperties": false, - "description": "Configuration for Codex app-server agent.\n\nWraps the Codex app-server via its JSON-RPC protocol, enabling\nintegration with agentpool's agent ecosystem.", + "description": "Configuration for Codex app-server agent.\n\nWraps the Codex app-server via its JSON-RPC protocol, enabling\nintegration with wolfharness's agent ecosystem.", "properties": { "name": { "default": null, @@ -8674,7 +8674,7 @@ }, "CronToolsetConfig": { "additionalProperties": false, - "description": "Configuration for cron scheduling toolset.\n\nGives agents the ability to schedule, list, and remove recurring or\none-shot jobs at runtime. Jobs are persisted to a JSON file.\n\nRequires the ``bot`` extra: ``pip install agentpool[bot]``", + "description": "Configuration for cron scheduling toolset.\n\nGives agents the ability to schedule, list, and remove recurring or\none-shot jobs at runtime. Jobs are persisted to a JSON file.\n\nRequires the ``bot`` extra: ``pip install wolfharness[bot]``", "properties": { "namespace": { "default": null, @@ -8697,10 +8697,10 @@ "type": "string" }, "store_path": { - "default": "~/.agentpool/cron_jobs.json", + "default": "~/.wolfharness/cron_jobs.json", "description": "Path to the JSON file where scheduled jobs are persisted.", "examples": [ - "~/.agentpool/cron_jobs.json", + "~/.wolfharness/cron_jobs.json", "/tmp/cron.json" ], "title": "Job store path", @@ -8732,7 +8732,7 @@ "service_name": { "default": null, "examples": [ - "agentpool", + "wolfharness", "my-ai-service", "production-bot" ], @@ -18969,7 +18969,7 @@ "description": "The task instruction/prompt.", "examples": [ "Analyze the following code", - "agentpool.prompts.prompts:BasePrompt" + "wolfharness.prompts.prompts:BasePrompt" ], "title": "Task prompt" }, @@ -18988,7 +18988,7 @@ "default": null, "description": "Dependencies or context data needed for task execution", "examples": [ - "agentpool.agents.context:AgentContext" + "wolfharness.agents.context:AgentContext" ], "title": "Required dependencies", "type": [ @@ -19390,7 +19390,7 @@ }, "Knowledge": { "additionalProperties": false, - "description": "Collection of context sources for an agent.\n\nSupports both simple paths and rich resource types for content loading,\nplus AgentPool's prompt system for dynamic content generation.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/knowledge_configuration/", + "description": "Collection of context sources for an agent.\n\nSupports both simple paths and rich resource types for content loading,\nplus AgentPool's prompt system for dynamic content generation.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/knowledge_configuration/", "properties": { "paths": { "description": "Quick access to files and URLs.\n\nPaths are automatically resolved relative to the config file location\nvia ConfigPath validation.", @@ -19642,7 +19642,7 @@ "service_name": { "default": null, "examples": [ - "agentpool", + "wolfharness", "my-ai-service", "production-bot" ], @@ -20502,7 +20502,7 @@ "service_name": { "default": null, "examples": [ - "agentpool", + "wolfharness", "my-ai-service", "production-bot" ], @@ -20576,7 +20576,7 @@ }, "MCPDiscoveryToolsetConfig": { "additionalProperties": false, - "description": "Configuration for MCP discovery toolset.\n\nEnables dynamic discovery and use of MCP servers without preloading tools.\nUses semantic search over 1000+ indexed servers for intelligent matching.\n\nRequires the `mcp-discovery` extra: `pip install agentpool[mcp-discovery]`", + "description": "Configuration for MCP discovery toolset.\n\nEnables dynamic discovery and use of MCP servers without preloading tools.\nUses semantic search over 1000+ indexed servers for intelligent matching.\n\nRequires the `mcp-discovery` extra: `pip install wolfharness[mcp-discovery]`", "properties": { "namespace": { "default": null, @@ -22635,7 +22635,7 @@ }, "NativeAgentConfig": { "additionalProperties": false, - "description": "Configuration for a single agent in the system.\n\nDefines an agent's complete configuration including its model, environment,\nand behavior settings.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/agent_configuration/", + "description": "Configuration for a single agent in the system.\n\nDefines an agent's complete configuration including its model, environment,\nand behavior settings.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/agent_configuration/", "properties": { "name": { "default": null, @@ -23889,16 +23889,16 @@ "type": "string" } ], - "description": "The model to use for this agent. Can be either a simple model name\nstring (e.g. 'openai:gpt-5') or a structured model definition.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/model_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/model_configuration/", + "description": "The model to use for this agent. Can be either a simple model name\nstring (e.g. 'openai:gpt-5') or a structured model definition.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/model_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/model_configuration/", "examples": [ "openai:gpt-5-nano" ], "title": "Model configuration or name" }, "tools": { - "description": "A list of tools and toolsets to register with this agent.\n\nSupports both single tools (bash, import, web_search, etc.) and\ntoolsets (file_access, process_management, code, etc.).\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/tool_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/tool_configuration/", + "description": "A list of tools and toolsets to register with this agent.\n\nSupports both single tools (bash, import, web_search, etc.) and\ntoolsets (file_access, process_management, code, etc.).\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/tool_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/tool_configuration/", "examples": [ [ "webbrowser:open", @@ -24153,8 +24153,8 @@ } ], "default": null, - "description": "Session configuration for conversation recovery.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/session_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/session_configuration/", + "description": "Session configuration for conversation recovery.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/session_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/session_configuration/", "examples": [ "main_session", "user_123" @@ -24278,7 +24278,7 @@ ], "default": null, "description": "System prompt for the agent.\n Can be a string or list of strings/prompt configs", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/system_prompts_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/system_prompts_configuration/", "examples": [ "You are a helpful assistant.", [ @@ -24298,13 +24298,13 @@ } ], "default": null, - "description": "Knowledge sources for this agent.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/knowledge_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/knowledge_configuration/", + "description": "Knowledge sources for this agent.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/knowledge_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/knowledge_configuration/", "title": "Knowledge sources" }, "workers": { - "description": "Worker agents which will be available as tools.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/worker_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/worker_configuration/", + "description": "Worker agents which will be available as tools.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/worker_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/worker_configuration/", "examples": [ [ { @@ -27432,7 +27432,7 @@ }, "PromptLibraryConfig": { "additionalProperties": false, - "description": "Complete prompt configuration.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/prompt_configuration/", + "description": "Complete prompt configuration.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/prompt_configuration/", "properties": { "system_prompts": { "additionalProperties": { @@ -32231,7 +32231,7 @@ }, "StorageConfig": { "additionalProperties": false, - "description": "Global storage configuration.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/storage_configuration/", + "description": "Global storage configuration.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/storage_configuration/", "properties": { "providers": { "anyOf": [ @@ -34309,7 +34309,7 @@ }, "StructuredResponseConfig": { "additionalProperties": false, - "description": "Base class for response definitions.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/response_configuration/", + "description": "Base class for response definitions.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/response_configuration/", "properties": { "response_schema": { "description": "A model describing the response schema.", @@ -34811,7 +34811,7 @@ }, "TeamConfig": { "additionalProperties": false, - "description": "Configuration for a team or chain of message nodes.\n\nTeams can be either parallel execution groups or sequential chains.\nThey can contain both agents and other teams as members.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/team_configuration/", + "description": "Configuration for a team or chain of message nodes.\n\nTeams can be either parallel execution groups or sequential chains.\nThey can contain both agents and other teams as members.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/team_configuration/", "properties": { "name": { "default": null, @@ -38406,7 +38406,7 @@ }, "additionalProperties": true, "description": "Complete agent configuration manifest defining all available agents.\n\nThis is the root configuration that:\n- Defines available response types (both inline and imported)\n- Configures all agent instances and their settings\n- Sets up custom role definitions and capabilities\n- Manages environment configurations\n\nA single manifest can define multiple agents that can work independently\nor collaborate through the orchestrator.", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/manifest_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/manifest_configuration/", "patternProperties": { "^\\.": { "description": "YAML anchor or hidden field" @@ -38761,8 +38761,8 @@ } ] }, - "description": "Mapping of agent IDs to their configurations.\n\nAll agent types are unified under this single dict, discriminated by the 'type' field:\n- type: \"native\" (default) - pydantic-ai based agents\n- type: \"agui\" - AG-UI protocol agents\n- type: \"claude_code\" - Claude Agent SDK agents\n- type: \"acp\" - ACP protocol agents (further discriminated by 'provider')\n\nExample:\n ```yaml\n agents:\n assistant:\n type: native\n model: openai:gpt-4\n system_prompt: \"You are a helpful assistant.\"\n\n coder:\n type: claude_code\n cwd: /path/to/project\n model: claude-sonnet-4-5\n\n orchestrator:\n type: acp\n provider: claude\n model: sonnet\n\n remote:\n type: agui\n endpoint: http://localhost:8000/agent/run\n ```\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/agent_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/agent_configuration/", + "description": "Mapping of agent IDs to their configurations.\n\nAll agent types are unified under this single dict, discriminated by the 'type' field:\n- type: \"native\" (default) - pydantic-ai based agents\n- type: \"agui\" - AG-UI protocol agents\n- type: \"claude_code\" - Claude Agent SDK agents\n- type: \"acp\" - ACP protocol agents (further discriminated by 'provider')\n\nExample:\n ```yaml\n agents:\n assistant:\n type: native\n model: openai:gpt-4\n system_prompt: \"You are a helpful assistant.\"\n\n coder:\n type: claude_code\n cwd: /path/to/project\n model: claude-sonnet-4-5\n\n orchestrator:\n type: acp\n provider: claude\n model: sonnet\n\n remote:\n type: agui\n endpoint: http://localhost:8000/agent/run\n ```\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/agent_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/agent_configuration/", "title": "Agents", "type": "object" }, @@ -38810,15 +38810,15 @@ "additionalProperties": { "$ref": "#/$defs/TeamConfig" }, - "description": "Mapping of team IDs to their configurations.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/team_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/team_configuration/", + "description": "Mapping of team IDs to their configurations.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/team_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/team_configuration/", "title": "Teams", "type": "object" }, "storage": { "$ref": "#/$defs/StorageConfig", - "description": "Storage provider configuration.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/storage_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/storage_configuration/" + "description": "Storage provider configuration.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/storage_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/storage_configuration/" }, "observability": { "$ref": "#/$defs/ObservabilityConfig", @@ -38832,8 +38832,8 @@ "additionalProperties": { "$ref": "#/$defs/StructuredResponseConfig" }, - "description": "Mapping of response names to their definitions.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/response_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/response_configuration/", + "description": "Mapping of response names to their definitions.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/response_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/response_configuration/", "title": "Responses", "type": "object" }, @@ -38938,8 +38938,8 @@ "type": "object" }, "mcp_servers": { - "description": "List of MCP server configurations:\n\nThese MCP servers are used to provide tools and other resources to the nodes.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/mcp_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/mcp_configuration/", + "description": "List of MCP server configurations:\n\nThese MCP servers are used to provide tools and other resources to the nodes.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/mcp_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/mcp_configuration/", "examples": [ [ "uvx some-server" @@ -38996,8 +38996,8 @@ }, "prompts": { "$ref": "#/$defs/PromptLibraryConfig", - "description": "Prompt library configuration.\n\nThis configuration defines the prompt library, which is used to provide prompts to the nodes.\n\nDocs: https://phil65.github.io/agentpool/YAML%20Configuration/prompt_configuration/", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/prompt_configuration/" + "description": "Prompt library configuration.\n\nThis configuration defines the prompt library, which is used to provide prompts to the nodes.\n\nDocs: https://phil65.github.io/wolfharness/YAML%20Configuration/prompt_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/prompt_configuration/" }, "skills": { "$ref": "#/$defs/SkillsConfig", diff --git a/scripts/add_frontmatter.py b/scripts/add_frontmatter.py index ebc76b0d3..c520fa1e0 100644 --- a/scripts/add_frontmatter.py +++ b/scripts/add_frontmatter.py @@ -8,7 +8,7 @@ # Root paths -DOCS_CODE_DIR = Path("src/agentpool_docs") +DOCS_CODE_DIR = Path("src/wolfharness_docs") DOCS_DIR = Path("docs") diff --git a/scripts/build_mcp_registry_index.py b/scripts/build_mcp_registry_index.py index cae02299b..731aeab8b 100644 --- a/scripts/build_mcp_registry_index.py +++ b/scripts/build_mcp_registry_index.py @@ -23,7 +23,7 @@ DEFAULT_OUTPUT = ( Path(__file__).parent.parent / "src" - / "agentpool_toolsets" + / "wolfharness_toolsets" / "mcp_discovery" / "data" / "mcp_servers.parquet" diff --git a/scripts/check_union_docs.py b/scripts/check_union_docs.py index 908d6032e..f3a1b4709 100644 --- a/scripts/check_union_docs.py +++ b/scripts/check_union_docs.py @@ -9,13 +9,13 @@ from pathlib import Path import sys -from agentpool.docs.utils import check_docs_for_union, discriminator_to_filename +from wolfharness.docs.utils import check_docs_for_union, discriminator_to_filename def main() -> int: """Run checks for known union types.""" # Import here to avoid import errors if dependencies missing - from agentpool_config.toolsets import ToolsetConfig + from wolfharness_config.toolsets import ToolsetConfig checks = [ (ToolsetConfig, Path("docs/configuration/toolsets"), "Toolsets"), @@ -46,7 +46,7 @@ def main() -> int: print(f" - {filename}.md") if not missing and not extra: - from agentpool.docs.utils import get_discriminator_values + from wolfharness.docs.utils import get_discriminator_values count = len(get_discriminator_values(union_type)) print(f" All {count} types documented") diff --git a/scripts/diagnose_skills.py b/scripts/diagnose_skills.py index 6b365ccf7..2dfe54def 100644 --- a/scripts/diagnose_skills.py +++ b/scripts/diagnose_skills.py @@ -40,7 +40,7 @@ async def diagnose(config_path: str | None = None) -> None: # noqa: PLR0915 # ============================================================ print_header("Stage 1: Filesystem Discovery") - from agentpool_config.skills import DEFAULT_SKILLS_PATHS + from wolfharness_config.skills import DEFAULT_SKILLS_PATHS # Check default skill directories for dp in DEFAULT_SKILLS_PATHS: @@ -90,7 +90,7 @@ async def diagnose(config_path: str | None = None) -> None: # noqa: PLR0915 else "MISSING", ) # Check for unknown keys - from agentpool.skills.skill import SkillMetadata + from wolfharness.skills.skill import SkillMetadata try: SkillMetadata.model_validate(frontmatter) @@ -124,7 +124,7 @@ async def diagnose(config_path: str | None = None) -> None: # noqa: PLR0915 if config_path: print("\n --- Custom paths from config ---") try: - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest.from_file(config_path) if manifest.skills: @@ -152,15 +152,15 @@ async def diagnose(config_path: str | None = None) -> None: # noqa: PLR0915 # Stage 2: SkillsManager / SkillsRegistry print_header("Stage 2: SkillsManager / SkillsRegistry") - from agentpool.skills.manager import SkillsManager + from wolfharness.skills.manager import SkillsManager try: config_file_path = to_upath(config_path) if config_path else None - from agentpool_config.skills import SkillsConfig + from wolfharness_config.skills import SkillsConfig skills_config: SkillsConfig | None = None if config_path: - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest.from_file(config_path) skills_config = manifest.skills @@ -190,7 +190,7 @@ async def diagnose(config_path: str | None = None) -> None: # noqa: PLR0915 print_header("Stage 3: AgentPool Initialization") try: - from agentpool.delegation import AgentPool + from wolfharness.delegation import AgentPool async with AgentPool(config_path) as pool: # Check skill_provider @@ -237,11 +237,11 @@ async def diagnose(config_path: str | None = None) -> None: # noqa: PLR0915 if config_path: try: - from agentpool.delegation import AgentPool + from wolfharness.delegation import AgentPool async with AgentPool(config_path) as pool: # Simulate what server.py does - from agentpool_server.opencode_server.skill_bridge import OpenCodeSkillBridge + from wolfharness_server.opencode_server.skill_bridge import OpenCodeSkillBridge bridge = OpenCodeSkillBridge(skill_provider=pool.skill_provider) @@ -320,7 +320,7 @@ async def diagnose(config_path: str | None = None) -> None: # noqa: PLR0915 4. Relative paths resolve against the CONFIG FILE location, not CWD. 5. Check server logs with: - OBSERVABILITY_ENABLED=true agentpool serve-opencode config.yml + OBSERVABILITY_ENABLED=true wolfharness serve-opencode config.yml 6. If pool.skill_provider is None, the OpenCode bridge won't have commands. """) diff --git a/scripts/generate_mcp_registry_models.py b/scripts/generate_mcp_registry_models.py index 6c81e42e1..bc7d6f779 100644 --- a/scripts/generate_mcp_registry_models.py +++ b/scripts/generate_mcp_registry_models.py @@ -21,7 +21,7 @@ REGISTRY_URL = "https://registry.modelcontextprotocol.io/v0/servers" -OUTPUT_FILE = Path(__file__).parent.parent / "src" / "agentpool_config" / "mcp_registry.py" +OUTPUT_FILE = Path(__file__).parent.parent / "src" / "wolfharness_config" / "mcp_registry.py" @dataclass @@ -405,7 +405,7 @@ def generate_single_file(servers: list[ServerDef]) -> str: "", "from pydantic import Field, HttpUrl", "", - "from agentpool_config.mcp_server import (", + "from wolfharness_config.mcp_server import (", " BaseMCPServerConfig,", " SSEMCPServerConfig,", " StdioMCPServerConfig,", diff --git a/scripts/generate_openresponses_models.py b/scripts/generate_openresponses_models.py index c62eba09a..fb26852bd 100644 --- a/scripts/generate_openresponses_models.py +++ b/scripts/generate_openresponses_models.py @@ -24,7 +24,7 @@ "https://raw.githubusercontent.com/openresponses/openresponses/main/public/openapi/openapi.json" ) PROJECT_ROOT = Path(__file__).parent.parent -OUTPUT_DIR = PROJECT_ROOT / "src" / "agentpool" / "models" / "openresponses" +OUTPUT_DIR = PROJECT_ROOT / "src" / "wolfharness" / "models" / "openresponses" OUTPUT_FILE = OUTPUT_DIR / "models.py" TEMP_OPENAPI = "/tmp/openresponses_openapi.json" @@ -78,7 +78,7 @@ def generate_models() -> None: "--use-one-literal-as-default", "--collapse-root-models", "--base-class", - "agentpool.models.openresponses.base.OpenResponsesBase", + "wolfharness.models.openresponses.base.OpenResponsesBase", "--formatters", "ruff-check", "ruff-format", diff --git a/scripts/generate_schema.py b/scripts/generate_schema.py index 8982012d8..f81fcc9ea 100644 --- a/scripts/generate_schema.py +++ b/scripts/generate_schema.py @@ -14,8 +14,8 @@ import sys from typing import Any, Literal -from agentpool import AgentsManifest -from agentpool.log import configure_logging, get_logger +from wolfharness import AgentsManifest +from wolfharness.log import configure_logging, get_logger logger = get_logger(__name__) diff --git a/scripts/qa_test_server.py b/scripts/qa_test_server.py index fa422860f..13ceab713 100644 --- a/scripts/qa_test_server.py +++ b/scripts/qa_test_server.py @@ -12,16 +12,16 @@ import uvicorn -from agentpool.storage import StorageManager -from agentpool.utils.streams import FileOpsTracker -from agentpool.utils.todos import TodoTracker -from agentpool_server.opencode_server.server import create_app +from wolfharness.storage import StorageManager +from wolfharness.utils.streams import FileOpsTracker +from wolfharness.utils.todos import TodoTracker +from wolfharness_server.opencode_server.server import create_app if TYPE_CHECKING: from fastapi import FastAPI - from agentpool.sessions.models import SessionData + from wolfharness.sessions.models import SessionData def create_test_app() -> FastAPI: # noqa: PLR0915 @@ -33,7 +33,7 @@ def create_test_app() -> FastAPI: # noqa: PLR0915 pool.manifest.opencode = Mock() # Storage - from agentpool_config.storage import MemoryStorageConfig, StorageConfig + from wolfharness_config.storage import MemoryStorageConfig, StorageConfig storage_manager = StorageManager(config=StorageConfig(providers=[MemoryStorageConfig()])) pool.storage = storage_manager @@ -60,7 +60,7 @@ async def _mock_create_session( ) -> Mock: from datetime import datetime - from agentpool.sessions.models import SessionData + from wolfharness.sessions.models import SessionData data = SessionData( session_id=session_id, @@ -97,7 +97,7 @@ async def _mock_close_session(session_id: str) -> None: def _mock_session_state(): from datetime import datetime - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState state = Mock(spec=SessionState) state.created_at = datetime.now().timestamp() diff --git a/scripts/rename_to_agentwolf.py b/scripts/rename_to_wolfharness.py similarity index 67% rename from scripts/rename_to_agentwolf.py rename to scripts/rename_to_wolfharness.py index 5fd3e1387..6856832e5 100755 --- a/scripts/rename_to_agentwolf.py +++ b/scripts/rename_to_wolfharness.py @@ -1,19 +1,23 @@ #!/usr/bin/env python3 -"""Rename agentpool → agentwolf across the entire codebase. +"""Rename agentpool → wolfharness across the entire codebase. -Phase 8 of the thin-wrapper refactor. This script performs a one-shot -mechanical rename of all agentpool references to agentwolf. +Mechanical rename of the project from AgentPool (wolfharness) to +WolfHarness (wolfharness). Supersedes the earlier wolfharness → agentwolf +rename (which was never merged to main). + +This script performs a one-shot mechanical rename of all wolfharness +references to wolfharness. Usage: - python scripts/rename_to_agentwolf.py [--dry-run] + python scripts/rename_to_wolfharness.py [--dry-run] Pre-requisites: - - All Phase 1-7 PRs merged into refactor/thin-wrapper + - Based on origin/main (which never contained agentwolf package code) - Clean working tree (no uncommitted changes) - Run from repository root What this script does: - 1. Rename src/ directories (agentpool → agentwolf, agentpool_* → agentwolf_*) + 1. Rename src/ directories (wolfharness → wolfharness, wolfharness_* → wolfharness_*) 2. Replace all references in .py/.toml/.yml/.md/.json/etc files 3. Verify entry point files exist post-rename 4. Print verification checklist @@ -28,7 +32,7 @@ 2. Run `uv run pytest` to verify all tests pass 3. Run `uv run ruff check src/` to verify linting 4. Run `uv run mypy src/` to verify type checking - 5. Verify `agentwolf --version` works + 5. Verify `wolfharness --version` works 6. Commit as a single atomic commit """ @@ -42,29 +46,29 @@ REPO_ROOT = Path(__file__).resolve().parent.parent SRC_DIRS_TO_RENAME = [ - ("src/agentpool", "src/agentwolf"), - ("src/agentpool_bot", "src/agentwolf_bot"), - ("src/agentpool_cli", "src/agentwolf_cli"), - ("src/agentpool_commands", "src/agentwolf_commands"), - ("src/agentpool_config", "src/agentwolf_config"), - ("src/agentpool_prompts", "src/agentwolf_prompts"), - ("src/agentpool_server", "src/agentwolf_server"), - ("src/agentpool_storage", "src/agentwolf_storage"), - ("src/agentpool_sync", "src/agentwolf_sync"), - ("src/agentpool_toolsets", "src/agentwolf_toolsets"), + ("src/wolfharness", "src/wolfharness"), + ("src/wolfharness_bot", "src/wolfharness_bot"), + ("src/wolfharness_cli", "src/wolfharness_cli"), + ("src/wolfharness_commands", "src/wolfharness_commands"), + ("src/wolfharness_config", "src/wolfharness_config"), + ("src/wolfharness_prompts", "src/wolfharness_prompts"), + ("src/wolfharness_server", "src/wolfharness_server"), + ("src/wolfharness_storage", "src/wolfharness_storage"), + ("src/wolfharness_sync", "src/wolfharness_sync"), + ("src/wolfharness_toolsets", "src/wolfharness_toolsets"), ] REPLACEMENTS = [ - ("agentpool_config", "agentwolf_config"), - ("agentpool_server", "agentwolf_server"), - ("agentpool_toolsets", "agentwolf_toolsets"), - ("agentpool_storage", "agentwolf_storage"), - ("agentpool_cli", "agentwolf_cli"), - ("agentpool_commands", "agentwolf_commands"), - ("agentpool_prompts", "agentwolf_prompts"), - ("agentpool_sync", "agentwolf_sync"), - ("agentpool_bot", "agentwolf_bot"), - ("agentpool", "agentwolf"), + ("wolfharness_config", "wolfharness_config"), + ("wolfharness_server", "wolfharness_server"), + ("wolfharness_toolsets", "wolfharness_toolsets"), + ("wolfharness_storage", "wolfharness_storage"), + ("wolfharness_cli", "wolfharness_cli"), + ("wolfharness_commands", "wolfharness_commands"), + ("wolfharness_prompts", "wolfharness_prompts"), + ("wolfharness_sync", "wolfharness_sync"), + ("wolfharness_bot", "wolfharness_bot"), + ("wolfharness", "wolfharness"), ] FILE_PATTERNS = [ @@ -100,7 +104,7 @@ def rename_directories(dry_run: bool) -> None: - """Rename src/ directories from agentpool* to agentwolf*.""" + """Rename src/ directories from wolfharness* to wolfharness*.""" print("Step 1: Renaming source directories") for old, new in SRC_DIRS_TO_RENAME: old_path = REPO_ROOT / old @@ -117,7 +121,7 @@ def rename_directories(dry_run: bool) -> None: def replace_references(dry_run: bool) -> None: - """Replace all agentpool references in source files.""" + """Replace all wolfharness references in source files.""" print("\nStep 2: Replacing references in files") files_changed = 0 for pattern in FILE_PATTERNS: @@ -144,9 +148,9 @@ def check_entry_points() -> None: """Check that renamed entry point files exist.""" print("\nStep 3: Checking entry points") entry_files = [ - REPO_ROOT / "src" / "agentwolf" / "__init__.py", - REPO_ROOT / "src" / "agentwolf_cli" / "__init__.py", - REPO_ROOT / "src" / "agentwolf_cli" / "cli.py", + REPO_ROOT / "src" / "wolfharness" / "__init__.py", + REPO_ROOT / "src" / "wolfharness_cli" / "__init__.py", + REPO_ROOT / "src" / "wolfharness_cli" / "cli.py", ] for ef in entry_files: if ef.exists(): @@ -162,10 +166,9 @@ def print_verification_checklist() -> None: print(" [ ] Run: uv run pytest") print(" [ ] Run: uv run ruff check src/") print(" [ ] Run: uv run mypy src/") - print(" [ ] Run: agentwolf --version") - print(" [ ] Run: agentwolf serve-acp config.yml") - print(" [ ] Commit: git add -A && git commit -m 'refactor: rename agentpool to agentwolf'") - print(" [ ] Push: git push million refactor/thin-wrapper --force") + print(" [ ] Run: wolfharness --version") + print(" [ ] Run: wolfharness serve-acp config.yml") + print(" [ ] Commit: git add -A && git commit -m 'refactor: rename wolfharness to wolfharness'") def main() -> None: diff --git a/scripts/restart_opencode_server.sh b/scripts/restart_opencode_server.sh index 456433a33..8561f02ff 100755 --- a/scripts/restart_opencode_server.sh +++ b/scripts/restart_opencode_server.sh @@ -22,8 +22,8 @@ fi echo "" echo "To start the server again with updated code, run:" -echo " cd /home/phil65/dev/oss/agentpool" -echo " agentpool ui desktop" +echo " cd /home/phil65/dev/oss/wolfharness" +echo " wolfharness ui desktop" echo "" echo "Or if you want to run just the server (without desktop app):" -echo " agentpool serve-opencode --port 4096" +echo " wolfharness serve-opencode --port 4096" diff --git a/scripts/tts_performance.py b/scripts/tts_performance.py index 7ca06d1ba..da6cb6770 100644 --- a/scripts/tts_performance.py +++ b/scripts/tts_performance.py @@ -15,15 +15,15 @@ import anyio from pydantic_ai import PartDeltaEvent, PartStartEvent, TextPart, TextPartDelta -from agentpool import Agent -from agentpool_config.event_handlers import EdgeTTSEventHandlerConfig, TTSEventHandlerConfig +from wolfharness import Agent +from wolfharness_config.event_handlers import EdgeTTSEventHandlerConfig, TTSEventHandlerConfig if TYPE_CHECKING: from anyvoice import TTSMode - from agentpool.agents.context import AgentContext - from agentpool.agents.events import RichAgentStreamEvent + from wolfharness.agents.context import AgentContext + from wolfharness.agents.events import RichAgentStreamEvent TTSProvider = Literal["openai", "edge"] diff --git a/src/acp/AGENTS.md b/src/acp/AGENTS.md index b46d8aed8..1c175edc7 100644 --- a/src/acp/AGENTS.md +++ b/src/acp/AGENTS.md @@ -2,7 +2,7 @@ ## Overview -Standalone JSON-RPC 2.0 protocol implementation for the Agent Communication Protocol (69 files). Independent from agentpool framework -- used by it, not part of it. +Standalone JSON-RPC 2.0 protocol implementation for the Agent Communication Protocol (69 files). Independent from wolfharness framework -- used by it, not part of it. ## Where To Look @@ -39,4 +39,4 @@ Standalone JSON-RPC 2.0 protocol implementation for the Agent Communication Prot - The `connection.py` `Connection` class is the central wire-protocol engine. It handles JSON-RPC framing, message dispatch, stream observers, and bidirectional communication. Both `AgentSideConnection` and `ClientSideConnection` wrap it with protocol-specific method handlers. - `ToolCallState` sends delta-only updates (per ACP spec, all fields except `toolCallId` are optional in updates). Re-sends nothing unless data changed. - Entry points in pyproject.toml: `acp-bridge` console script, `acp = ACPFileSystem` (fsspec), `acp = ACPPath` (universal_pathlib). -- The debug server at `agent/implementations/debug_server/` provides a full standalone ACP agent + HTML UI for testing without agentpool. +- The debug server at `agent/implementations/debug_server/` provides a full standalone ACP agent + HTML UI for testing without wolfharness. diff --git a/src/acp/agent/acp_agent_api.py b/src/acp/agent/acp_agent_api.py index e7120a3bf..ad8cb252c 100644 --- a/src/acp/agent/acp_agent_api.py +++ b/src/acp/agent/acp_agent_api.py @@ -65,7 +65,7 @@ class ACPAgentAPI: Avoids manual instantiation of request/notification objects. When optional ``state`` and ``update_event`` are provided, the instance - also satisfies the :class:`~agentpool.agents.acp_agent.turn.ACPClientProtocol` + also satisfies the :class:`~wolfharness.agents.acp_agent.turn.ACPClientProtocol` protocol by implementing :meth:`stream_events` and :meth:`get_messages`. """ diff --git a/src/acp/agent/notifications.py b/src/acp/agent/notifications.py index f43ad88e4..0b90947db 100644 --- a/src/acp/agent/notifications.py +++ b/src/acp/agent/notifications.py @@ -35,7 +35,7 @@ from acp.schema.tool_call import ToolCallLocation from acp.tool_call_reporter import ToolCallReporter from acp.utils import generate_tool_title, infer_tool_kind, to_acp_content_blocks -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict if TYPE_CHECKING: diff --git a/src/acp/bridge/__main__.py b/src/acp/bridge/__main__.py index 10c4788f7..4a625e23a 100644 --- a/src/acp/bridge/__main__.py +++ b/src/acp/bridge/__main__.py @@ -7,7 +7,7 @@ # WebSocket bridge uv run -m acp.bridge --transport websocket [args...] - uv run -m acp.bridge -t ws --port 8765 -- uv run agentpool serve-acp + uv run -m acp.bridge -t ws --port 8765 -- uv run wolfharness serve-acp """ from __future__ import annotations @@ -31,7 +31,7 @@ def main() -> None: " acp-bridge --port 8080 -- your-agent --config config.yml\n" "\n" " # WebSocket bridge\n" - " acp-bridge -t ws -- uv run agentpool serve-acp\n" + " acp-bridge -t ws -- uv run wolfharness serve-acp\n" " acp-bridge --transport websocket --port 8765 -- uv run my-agent\n" ), formatter_class=argparse.RawTextHelpFormatter, diff --git a/src/acp/bridge/ws_server.py b/src/acp/bridge/ws_server.py index 77907ba9d..00063ace2 100644 --- a/src/acp/bridge/ws_server.py +++ b/src/acp/bridge/ws_server.py @@ -4,7 +4,7 @@ allowing clients like mcp-ws to connect. Usage: - python -m acp.bridge.ws_server "uv run agentpool serve-acp" --port 8765 + python -m acp.bridge.ws_server "uv run wolfharness serve-acp" --port 8765 """ from __future__ import annotations diff --git a/src/acp/bridge/ws_server_cli.py b/src/acp/bridge/ws_server_cli.py index 85dbb7a3d..78a37b94f 100644 --- a/src/acp/bridge/ws_server_cli.py +++ b/src/acp/bridge/ws_server_cli.py @@ -1,8 +1,8 @@ """CLI entry point for the ACP WebSocket server bridge. Usage: - python -m acp.bridge.ws_server_cli "uv run agentpool serve-acp" - python -m acp.bridge.ws_server_cli --port 8765 -- uv run agentpool serve-acp + python -m acp.bridge.ws_server_cli "uv run wolfharness serve-acp" + python -m acp.bridge.ws_server_cli --port 8765 -- uv run wolfharness serve-acp """ from __future__ import annotations @@ -21,8 +21,8 @@ def main() -> None: description="Bridge a stdio ACP agent to WebSocket transport.", epilog=( "Examples:\n" - ' acp-ws-server "uv run agentpool serve-acp"\n' - " acp-ws-server --port 8765 -- uv run agentpool serve-acp\n" + ' acp-ws-server "uv run wolfharness serve-acp"\n' + " acp-ws-server --port 8765 -- uv run wolfharness serve-acp\n" " acp-ws-server -H 0.0.0.0 -p 9000 -- uv run my-agent\n" ), formatter_class=argparse.RawTextHelpFormatter, diff --git a/src/acp/filesystem.py b/src/acp/filesystem.py index 342ca2132..c31b50cf5 100644 --- a/src/acp/filesystem.py +++ b/src/acp/filesystem.py @@ -13,7 +13,7 @@ from acp.agent.acp_requests import ACPRequests from acp.agent.notifications import ACPNotifications -from agentpool.mime_utils import guess_type, is_text_mime +from wolfharness.mime_utils import guess_type, is_text_mime if TYPE_CHECKING: diff --git a/src/agentpool/__main__.py b/src/agentpool/__main__.py deleted file mode 100644 index 65aa5fb74..000000000 --- a/src/agentpool/__main__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""CLI entry point - forwards to agentpool_cli for faster startup.""" - -from agentpool_cli.__main__ import cli - - -if __name__ == "__main__": - cli() diff --git a/src/agentpool/agents/__init__.py b/src/agentpool/agents/__init__.py deleted file mode 100644 index ff06ed5ae..000000000 --- a/src/agentpool/agents/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -"""CLI commands for agentpool.""" - -from __future__ import annotations - -from agentpool.agents.native_agent import Agent -from agentpool.agents.acp_agent import ACPAgent -from agentpool.agents.events import ( - detailed_print_handler, - resolve_event_handlers, - simple_print_handler, -) -from agentpool.agents.context import AgentContext -from agentpool.agents.interactions import Interactions -from agentpool.agents.prompt_injection import PromptInjectionManager -from agentpool.agents.sys_prompts import SystemPrompts -from agentpool.agents.exceptions import DelegationDepthError, MAX_DELEGATION_DEPTH - - -__all__ = [ - "MAX_DELEGATION_DEPTH", - "ACPAgent", - "Agent", - "AgentContext", - "DelegationDepthError", - "Interactions", - "PromptInjectionManager", - "SystemPrompts", - "detailed_print_handler", - "resolve_event_handlers", - "simple_print_handler", -] diff --git a/src/agentpool/delegation/__init__.py b/src/agentpool/delegation/__init__.py deleted file mode 100644 index c9b3e9904..000000000 --- a/src/agentpool/delegation/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Agent delegation and collaboration functionality.""" - -from agentpool.delegation.pool import AgentPool -from agentpool.delegation.base_team import BaseTeam - -__all__ = ["AgentPool", "BaseTeam"] diff --git a/src/agentpool/hooks/__init__.py b/src/agentpool/hooks/__init__.py deleted file mode 100644 index 5a5418e26..000000000 --- a/src/agentpool/hooks/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Runtime hook classes for agent lifecycle events.""" - -from __future__ import annotations - -from agentpool.hooks.agent_hooks import AgentHooks -from agentpool.hooks.base import Hook, HookEvent, HookInput, HookResult -from agentpool.hooks.callable import CallableHook -from agentpool.hooks.command import CommandHook -from agentpool.hooks.prompt import PromptHook - -__all__ = [ - "AgentHooks", - "CallableHook", - "CommandHook", - "Hook", - "HookEvent", - "HookInput", - "HookResult", - "PromptHook", -] diff --git a/src/agentpool/mcp_server/__init__.py b/src/agentpool/mcp_server/__init__.py deleted file mode 100644 index 3f6b140ef..000000000 --- a/src/agentpool/mcp_server/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""MCP server integration for AgentPool.""" - -from agentpool.mcp_server.client import MCPClient -from agentpool.mcp_server.tool_bridge import ToolManagerBridge - -__all__ = ["MCPClient", "ToolManagerBridge"] diff --git a/src/agentpool/models/__init__.py b/src/agentpool/models/__init__.py deleted file mode 100644 index 60ea571cf..000000000 --- a/src/agentpool/models/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Core data models for AgentPool.""" - -from __future__ import annotations - -from agentpool.models.acp_agents import ACPAgentConfig, ACPAgentConfigTypes, BaseACPAgentConfig -from agentpool.models.agents import AnyToolConfig, NativeAgentConfig # noqa: F401 -from agentpool.models.manifest import AgentsManifest, AnyAgentConfig -from agentpool.models.pending_interaction import PendingPermission, PendingQuestion - - -__all__ = [ - "ACPAgentConfig", - "ACPAgentConfigTypes", - "AgentsManifest", - "AnyAgentConfig", - "BaseACPAgentConfig", - "NativeAgentConfig", - "PendingPermission", - "PendingQuestion", -] diff --git a/src/agentpool/running/__init__.py b/src/agentpool/running/__init__.py deleted file mode 100644 index 8aeaa908c..000000000 --- a/src/agentpool/running/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Decorator auto-injection / run package.""" - -from __future__ import annotations - -from agentpool.running.decorators import with_nodes -from agentpool.running.discovery import NodeFunction, node_function -from agentpool.running.executor import execute_single, execute_functions -from agentpool.running.run_nodes import run_nodes_async, run_nodes -from agentpool.running.injection import NodeInjectionError - -__all__ = [ - "NodeFunction", - "NodeInjectionError", - "execute_functions", - "execute_single", - "node_function", - "run_nodes", - "run_nodes_async", - "with_nodes", -] diff --git a/src/agentpool/skills/__init__.py b/src/agentpool/skills/__init__.py deleted file mode 100644 index 1ca591654..000000000 --- a/src/agentpool/skills/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Skills package for Claude Code Skills support.""" - -from agentpool.skills.manager import SkillsManager -from agentpool.skills.skill import Skill, to_prompt - -__all__ = ["Skill", "SkillsManager", "to_prompt"] diff --git a/src/agentpool/talk/__init__.py b/src/agentpool/talk/__init__.py deleted file mode 100644 index 35467fe95..000000000 --- a/src/agentpool/talk/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Talk classes.""" - -from agentpool.talk.stats import TalkStats, AggregatedTalkStats -from agentpool.talk.talk import Talk, TeamTalk -from agentpool.talk.registry import ConnectionRegistry - -__all__ = [ - "AggregatedTalkStats", - "ConnectionRegistry", - "Talk", - "TalkStats", - "TeamTalk", -] diff --git a/src/agentpool_bot/__init__.py b/src/agentpool_bot/__init__.py deleted file mode 100644 index c18221138..000000000 --- a/src/agentpool_bot/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""agentpool_bot - Chat channel integrations for agentpool.""" - -from __future__ import annotations diff --git a/src/agentpool_commands/base.py b/src/agentpool_commands/base.py deleted file mode 100644 index ea82abdea..000000000 --- a/src/agentpool_commands/base.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Base command class with node-type filtering support. - -Re-exports from ``agentpool.commands.base`` for backward compatibility. -New code should import from ``agentpool.commands.base`` directly. -""" - -from __future__ import annotations - -from agentpool.commands.base import AgentCommand, NodeCommand - - -__all__ = ["AgentCommand", "NodeCommand"] diff --git a/src/agentpool_server/__init__.py b/src/agentpool_server/__init__.py deleted file mode 100644 index 1c58d53fa..000000000 --- a/src/agentpool_server/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""AgentPool Server implementations.""" - -from agentpool_server.a2a_server import A2AServer -from agentpool_server.aggregating_server import AggregatingServer -from agentpool_server.agui_server import AGUIServer -from agentpool_server.base import BaseServer -from agentpool_server.http_server import HTTPServer - -__all__ = ["A2AServer", "AGUIServer", "AggregatingServer", "BaseServer", "HTTPServer"] diff --git a/src/agentpool_server/acp_server/__init__.py b/src/agentpool_server/acp_server/__init__.py deleted file mode 100644 index 77f23a92e..000000000 --- a/src/agentpool_server/acp_server/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -"""ACP (Agent Client Protocol) integration for agentpool.""" - -from __future__ import annotations - -from agentpool_server.acp_server.handler import ACPProtocolHandler -from agentpool_server.acp_server.server import ACPServer -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.session import ACPSession -from agentpool_server.acp_server.session_manager import ACPSessionManager -from agentpool_server.acp_server.converters import ( - convert_acp_mcp_server_to_config, - from_acp_content, -) - - -__all__ = [ - "ACPProtocolHandler", - "ACPServer", - "ACPSession", - "ACPSessionManager", - "AgentPoolACPAgent", - "convert_acp_mcp_server_to_config", - "from_acp_content", -] diff --git a/src/agentpool_server/opencode_server/routes/__init__.py b/src/agentpool_server/opencode_server/routes/__init__.py deleted file mode 100644 index 79a03fb47..000000000 --- a/src/agentpool_server/opencode_server/routes/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Route handlers for the OpenCode API.""" - -from agentpool_server.opencode_server.routes.global_routes import router as global_router -from agentpool_server.opencode_server.routes.app_routes import router as app_router -from agentpool_server.opencode_server.routes.config_routes import router as config_router -from agentpool_server.opencode_server.routes.session_routes import router as session_router -from agentpool_server.opencode_server.routes.message_routes import router as message_router -from agentpool_server.opencode_server.routes.file_routes import router as file_router -from agentpool_server.opencode_server.routes.agent_routes import router as agent_router -from agentpool_server.opencode_server.routes.permission_routes import router as permission_router -from agentpool_server.opencode_server.routes.question_routes import router as question_router -from agentpool_server.opencode_server.routes.pty_routes import router as pty_router -from agentpool_server.opencode_server.routes.tui_routes import router as tui_router -from agentpool_server.opencode_server.routes.lsp_routes import router as lsp_router - -__all__ = [ - "agent_router", - "app_router", - "config_router", - "file_router", - "global_router", - "lsp_router", - "message_router", - "permission_router", - "pty_router", - "question_router", - "session_router", - "tui_router", -] diff --git a/src/agentpool_storage/file_provider/__init__.py b/src/agentpool_storage/file_provider/__init__.py deleted file mode 100644 index 918417b94..000000000 --- a/src/agentpool_storage/file_provider/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""File provider for agentpool.""" - -from __future__ import annotations - -from agentpool_storage.file_provider.provider import FileProvider - -__all__ = ["FileProvider"] diff --git a/src/agentpool_storage/memory_provider/__init__.py b/src/agentpool_storage/memory_provider/__init__.py deleted file mode 100644 index 2fe4671d1..000000000 --- a/src/agentpool_storage/memory_provider/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Memory provider for agentpool.""" - -from __future__ import annotations - -from agentpool_storage.memory_provider.provider import MemoryStorageProvider - -__all__ = ["MemoryStorageProvider"] diff --git a/src/agentpool_toolsets/__init__.py b/src/agentpool_toolsets/__init__.py deleted file mode 100644 index 50fcdb6f1..000000000 --- a/src/agentpool_toolsets/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Toolsets package.""" - -from agentpool_toolsets.config_creation import ConfigCreationTools -from agentpool_toolsets.fsspec_toolset import FSSpecTools -from agentpool_toolsets.notifications import NotificationsTools -from agentpool_toolsets.vfs_toolset import VFSTools - -__all__ = [ - "ConfigCreationTools", - "FSSpecTools", - "NotificationsTools", - "VFSTools", -] diff --git a/src/agentpool_toolsets/builtin/__init__.py b/src/agentpool_toolsets/builtin/__init__.py deleted file mode 100644 index 7abda0f05..000000000 --- a/src/agentpool_toolsets/builtin/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Built-in toolsets for agent capabilities.""" - -from __future__ import annotations - - -# Import provider classes -from agentpool_toolsets.builtin.code import CodeTools -from agentpool_toolsets.builtin.debug import DebugTools -from agentpool_toolsets.builtin.execution_environment import ProcessManagementTools -from agentpool_toolsets.builtin.question_tools import QuestionTools -from agentpool_toolsets.builtin.subagent_tools import SubagentTools -from agentpool_toolsets.builtin.workers import WorkersTools - - -__all__ = [ - # Provider classes - "CodeTools", - "DebugTools", - "ProcessManagementTools", - "QuestionTools", - "SubagentTools", - "WorkersTools", -] diff --git a/src/agentpool/AGENTS.md b/src/wolfharness/AGENTS.md similarity index 96% rename from src/agentpool/AGENTS.md rename to src/wolfharness/AGENTS.md index 83e6034d3..84d5236de 100644 --- a/src/agentpool/AGENTS.md +++ b/src/wolfharness/AGENTS.md @@ -1,4 +1,4 @@ -# agentpool (Core Framework) +# wolfharness (Core Framework) ## Overview @@ -37,7 +37,7 @@ | CommChannel implementations (Direct, Protocol) | `lifecycle/comm_channel.py` | | EventTransport (InProcess) | `lifecycle/event_transport.py` | | Dimension factory from config | `lifecycle/factory.py` | -| LifecycleConfig Pydantic model | `agentpool_config/lifecycle.py` | +| LifecycleConfig Pydantic model | `wolfharness_config/lifecycle.py` | | Capability base + all capabilities | `capabilities/{function_toolset,filtered_toolset,combined_toolset,...}.py` | | Skill YAML frontmatter model | `skills/skill.py` | | Skill as pydantic-ai capability (instructions, tools, MCP) | `skills/capability.py` (`SkillCapability`) | @@ -46,7 +46,7 @@ | Skill auto-discovery from paths | `skills/registry.py` | | Skill wrapped as slash commands | `skills/command.py`, `command_registry.py` | | Skill URI resolver (`skill://`) | `skills/uri_resolver.py` | -| Skill config models (McpServerConfig, ToolConfig) | `agentpool_config/skills.py` | +| Skill config models (McpServerConfig, ToolConfig) | `wolfharness_config/skills.py` | | MCP client + tool bridge | `mcp_server/{client,manager,tool_bridge}.py` | | Hook types (callable, command, prompt) | `hooks/{base,callable,command,prompt}.py` | | AgentHooks container (deprecated) | `hooks/agent_hooks.py` | @@ -79,10 +79,10 @@ - **`connect_to()` at runtime**: Deprecated. Define connections in YAML `graph:` or `connections:` sections. - **Bare `async for` in agent loops**: Use `RunExecutor`. The bare pattern silently drops `after_node_run` capability hooks. - **Mutable state on Agent objects**: `AgentRunContext` is per-execution isolation. Stashing mutable state on the `Agent` instance leaks between runs. -- **Config model imports from core**: Import config types from `agentpool_config.*`, not `agentpool.models`, to avoid circular deps. +- **Config model imports from core**: Import config types from `wolfharness_config.*`, not `wolfharness.models`, to avoid circular deps. - **Direct tool code in `tools/`**: Tool framework goes in `tools/`. Concrete implementations go in `tool_impls/`. - **Accessing `agent_pool` read-only**: Use `host_context` (immutable `HostContext`) instead. The `agent_pool` property emits `DeprecationWarning` as of M2. -- **Mixing `lifecycle.EventEnvelope` with `orchestrator.event_bus.EventEnvelope`**: These are separate types with different roles. Import from `agentpool.lifecycle.types` for lifecycle transport envelopes; from `agentpool.orchestrator.event_bus` for internal EventBus envelopes. +- **Mixing `lifecycle.EventEnvelope` with `orchestrator.event_bus.EventEnvelope`**: These are separate types with different roles. Import from `wolfharness.lifecycle.types` for lifecycle transport envelopes; from `wolfharness.orchestrator.event_bus` for internal EventBus envelopes. - **Blocking calls in async paths**: MCP connections, tool execution, and hooks all expect async methods. Use `anyio` or `asyncio`. - **Bare `asyncio.create_task()` without span**: Produces orphan traces. Always ensure a `logfire.span` is active at the call site. @@ -100,7 +100,7 @@ - **SkillCapability injection order matters**: In `get_agentlet()`, skill capabilities are injected at position 5 (after MCP, deferred bridge, approval bridge, and hook capabilities). Each skill produces one `SkillCapability` instance with its own `SkillMcpManager` and `SkillToolManager` — there is one manager tree shared across all skills from the same agentlet creation call. - **mcp.json format follows Claude Desktop**: The companion file uses `{"mcpServers": {"name": {"command": "...", "args": [...], ...}}}` JSON format. The `_load_mcp_json()` function handles env var expansion and converts entries to `SkillMcpServerConfig` objects. Only filesystem skills (UPath paths) can have companion files — virtual skills (PurePosixPath) cannot. - **Tool prefixing prevents name collisions**: Python tools get the prefix `{skill_name}__tool__` and MCP tools get `{skill_name}__mcp__`. This ensures tool names from different skills never collide in the agent's tool namespace. -- **Config model lives in separate package**: `agentpool_config/` exists solely to prevent import cycles with protocol servers. +- **Config model lives in separate package**: `wolfharness_config/` exists solely to prevent import cycles with protocol servers. ## Stream Event Types diff --git a/src/agentpool/__init__.py b/src/wolfharness/__init__.py similarity index 63% rename from src/agentpool/__init__.py rename to src/wolfharness/__init__.py index 6c1b44e10..3c0cdc626 100644 --- a/src/agentpool/__init__.py +++ b/src/wolfharness/__init__.py @@ -9,18 +9,18 @@ from importlib.metadata import version from upathtools import register_http_filesystems -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest # Builtin toolsets imports removed to avoid circular dependency -# Import them directly from agentpool_toolsets.builtin when needed -from agentpool.agents import Agent, AgentContext, ACPAgent -from agentpool.delegation import AgentPool, BaseTeam +# Import them directly from wolfharness_toolsets.builtin when needed +from wolfharness.agents import Agent, AgentContext, ACPAgent +from wolfharness.delegation import AgentPool, BaseTeam from dotenv import load_dotenv -from agentpool.messaging.messages import ChatMessage -from agentpool.tools import Tool, ToolCallInfo -from agentpool.messaging.messagenode import MessageNode -from agentpool.testing import acp_test_session +from wolfharness.messaging.messages import ChatMessage +from wolfharness.tools import Tool, ToolCallInfo +from wolfharness.messaging.messagenode import MessageNode +from wolfharness.testing import acp_test_session from pydantic_ai import ( AudioUrl, BinaryContent, @@ -30,13 +30,13 @@ VideoUrl, ) -__version__ = version("agentpool") +__version__ = version("wolfharness") __title__ = "AgentPool" __author__ = "Philipp Temminghoff" __author_email__ = "philipptemminghoff@googlemail.com" __copyright__ = "Copyright (c) 2025 Philipp Temminghoff" __license__ = "MIT" -__url__ = "https://github.com/phil65/agentpool" +__url__ = "https://github.com/phil65/wolfharness" load_dotenv() register_http_filesystems() @@ -44,12 +44,12 @@ # Rebuild models with forward references that couldn't be resolved during # module initialization due to circular import avoidance. # PromptType and BasePrompt are imported under TYPE_CHECKING in -# agentpool_config.knowledge and agentpool_config.task to break a circular -# import chain. By this point, agentpool.prompts.prompts is fully loaded, +# wolfharness_config.knowledge and wolfharness_config.task to break a circular +# import chain. By this point, wolfharness.prompts.prompts is fully loaded, # so we can resolve the forward references. -from agentpool.prompts.prompts import BasePrompt, PromptType -from agentpool_config.knowledge import Knowledge -from agentpool_config.task import Job +from wolfharness.prompts.prompts import BasePrompt, PromptType +from wolfharness_config.knowledge import Knowledge +from wolfharness_config.task import Job _ns = {"PromptType": PromptType, "BasePrompt": BasePrompt} Knowledge.model_rebuild(_types_namespace=_ns) diff --git a/src/wolfharness/__main__.py b/src/wolfharness/__main__.py new file mode 100644 index 000000000..7c9230e84 --- /dev/null +++ b/src/wolfharness/__main__.py @@ -0,0 +1,7 @@ +"""CLI entry point - forwards to wolfharness_cli for faster startup.""" + +from wolfharness_cli.__main__ import cli + + +if __name__ == "__main__": + cli() diff --git a/src/wolfharness/agents/__init__.py b/src/wolfharness/agents/__init__.py new file mode 100644 index 000000000..f8aa4afd7 --- /dev/null +++ b/src/wolfharness/agents/__init__.py @@ -0,0 +1,31 @@ +"""CLI commands for wolfharness.""" + +from __future__ import annotations + +from wolfharness.agents.native_agent import Agent +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.agents.events import ( + detailed_print_handler, + resolve_event_handlers, + simple_print_handler, +) +from wolfharness.agents.context import AgentContext +from wolfharness.agents.interactions import Interactions +from wolfharness.agents.prompt_injection import PromptInjectionManager +from wolfharness.agents.sys_prompts import SystemPrompts +from wolfharness.agents.exceptions import DelegationDepthError, MAX_DELEGATION_DEPTH + + +__all__ = [ + "MAX_DELEGATION_DEPTH", + "ACPAgent", + "Agent", + "AgentContext", + "DelegationDepthError", + "Interactions", + "PromptInjectionManager", + "SystemPrompts", + "detailed_print_handler", + "resolve_event_handlers", + "simple_print_handler", +] diff --git a/src/agentpool/agents/acp_agent/__init__.py b/src/wolfharness/agents/acp_agent/__init__.py similarity index 100% rename from src/agentpool/agents/acp_agent/__init__.py rename to src/wolfharness/agents/acp_agent/__init__.py diff --git a/src/agentpool/agents/acp_agent/acp_agent.py b/src/wolfharness/agents/acp_agent/acp_agent.py similarity index 92% rename from src/agentpool/agents/acp_agent/acp_agent.py rename to src/wolfharness/agents/acp_agent/acp_agent.py index 15a2f0a96..2c614e479 100644 --- a/src/agentpool/agents/acp_agent/acp_agent.py +++ b/src/wolfharness/agents/acp_agent/acp_agent.py @@ -2,7 +2,7 @@ This module provides an agent implementation that communicates with external ACP (Agent Client Protocol) servers via stdio, enabling integration of any -ACP-compatible agent into the agentpool pool. +ACP-compatible agent into the wolfharness pool. The ACPAgent class acts as an ACP client, spawning an ACP server subprocess and communicating with it via JSON-RPC over stdio. This allows: @@ -12,7 +12,7 @@ Example: ```python - from agentpool.models.acp_agents import ACPAgentConfig + from wolfharness.models.acp_agents import ACPAgentConfig config = ACPAgentConfig( command="claude-code-acp", @@ -49,25 +49,25 @@ from acp import InitializeRequest from acp.agent import ACPAgentAPI -from agentpool.agents.acp_agent.session_state import ACPSessionState -from agentpool.agents.acp_agent.turn import ACPClientProtocol, ACPTurn -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.events import ( +from wolfharness.agents.acp_agent.session_state import ACPSessionState +from wolfharness.agents.acp_agent.turn import ACPClientProtocol, ACPTurn +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.events import ( RunStartedEvent, StreamCompleteEvent, ToolCallCompleteEvent, ToolResultMetadataEvent, ) -from agentpool.agents.events.processors import event_to_part -from agentpool.agents.exceptions import ( +from wolfharness.agents.events.processors import event_to_part +from wolfharness.agents.exceptions import ( AgentNotInitializedError, UnknownCategoryError, UnknownModeError, ) -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.utils.subprocess_utils import SubprocessError, run_with_process_monitor -from agentpool.utils.token_breakdown import calculate_usage_from_parts +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.utils.subprocess_utils import SubprocessError, run_with_process_monitor +from wolfharness.utils.token_breakdown import calculate_usage_from_parts if TYPE_CHECKING: @@ -86,25 +86,25 @@ from acp.schema import Implementation, RequestPermissionRequest, RequestPermissionResponse from acp.schema.capabilities import AgentCapabilities from acp.schema.mcp import McpServer - from agentpool.agents.acp_agent.client_handler import ACPClientHandler - from agentpool.agents.context import AgentRunContext - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.agents.modes import ModeCategory - from agentpool.common_types import AnyEventHandlerType - from agentpool.delegation import AgentPool - from agentpool.hooks import AgentHooks - from agentpool.messaging import MessageHistory - from agentpool.models.acp_agents import BaseACPAgentConfig - from agentpool.orchestrator.turn import Turn - from agentpool.sessions import SessionData - from agentpool.ui.base import InputProvider - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness.agents.acp_agent.client_handler import ACPClientHandler + from wolfharness.agents.context import AgentRunContext + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.agents.modes import ModeCategory + from wolfharness.common_types import AnyEventHandlerType + from wolfharness.delegation import AgentPool + from wolfharness.hooks import AgentHooks + from wolfharness.messaging import MessageHistory + from wolfharness.models.acp_agents import BaseACPAgentConfig + from wolfharness.orchestrator.turn import Turn + from wolfharness.sessions import SessionData + from wolfharness.ui.base import InputProvider + from wolfharness_config.mcp_server import MCPServerConfig logger = get_logger(__name__) def get_updated_at(date_str: str | None) -> datetime: - from agentpool.utils.time_utils import get_now + from wolfharness.utils.time_utils import get_now updated_at = get_now() if date_str: @@ -116,7 +116,7 @@ def get_updated_at(date_str: str | None) -> datetime: class ACPAgent[TDeps = None](BaseAgent[TDeps, str]): """MessageNode that wraps an external ACP agent subprocess. - This allows integrating any ACP-compatible agent into the agentpool + This allows integrating any ACP-compatible agent into the wolfharness pool, enabling composition with native agents via connections, teams, etc. """ @@ -155,7 +155,7 @@ def __init__( hooks: AgentHooks | None = None, session_id: str | None = None, ) -> None: - from agentpool.mcp_server.tool_bridge import ToolManagerBridge + from wolfharness.mcp_server.tool_bridge import ToolManagerBridge super().__init__( name=name or command, @@ -182,7 +182,7 @@ def __init__( self._env_vars = env_vars or {} self._client_env = client_execution_env # ACP initialization - self._init_request = init_request or InitializeRequest.create_for_package("agentpool") + self._init_request = init_request or InitializeRequest.create_for_package("wolfharness") # Tools self._tool_providers = tool_providers or [] # Provider type for model messages @@ -235,7 +235,7 @@ def from_config( client_execution_env=config.get_client_execution_environment(), # ACP initialization init_request=InitializeRequest.create_for_package( - "agentpool", + "wolfharness", allow_terminal=config.allow_terminal, allow_file_operations=config.allow_file_operations, ), @@ -333,7 +333,7 @@ async def _start_process(self) -> Process: async def _initialize(self) -> None: """Initialize the ACP connection.""" from acp.client.connection import ClientSideConnection - from agentpool.agents.acp_agent.client_handler import ACPClientHandler + from wolfharness.agents.acp_agent.client_handler import ACPClientHandler if not self._process or not self._process.stdin or not self._process.stdout: raise RuntimeError("Process not started") @@ -357,8 +357,8 @@ async def _initialize(self) -> None: async def _create_session(self) -> None: """Create a new ACP session with configured MCP servers.""" - from agentpool.agents.acp_agent.acp_converters import mcp_config_to_acp - from agentpool.agents.acp_agent.helpers import filter_servers_by_capabilities + from wolfharness.agents.acp_agent.acp_converters import mcp_config_to_acp + from wolfharness.agents.acp_agent.helpers import filter_servers_by_capabilities if not self._api: raise AgentNotInitializedError @@ -427,7 +427,7 @@ async def _stream_events( # noqa: PLR0915 # type: ignore[override] store_history: bool = True, **pydantic_ai_kwargs: Any, ) -> AsyncIterator[RichAgentStreamEvent[str]]: - from agentpool.agents.acp_agent.acp_converters import to_finish_reason + from wolfharness.agents.acp_agent.acp_converters import to_finish_reason if input_provider is not None and self._client_handler: self._client_handler._input_provider = input_provider @@ -640,7 +640,7 @@ async def get_available_models(self) -> list[ModelInfo] | None: async def get_modes(self) -> list[ModeCategory]: """Get available modes from the ACP session state.""" - from agentpool.agents.acp_agent.acp_converters import get_modes + from wolfharness.agents.acp_agent.acp_converters import get_modes if not self._state: return [] @@ -691,7 +691,7 @@ async def list_sessions( limit: int | None = None, ) -> list[SessionData]: """List sessions from the remote ACP server.""" - from agentpool.sessions.models import SessionData + from wolfharness.sessions.models import SessionData if not self._api: raise RuntimeError("Not connected to ACP server") @@ -728,13 +728,13 @@ async def list_sessions( async def load_session(self, session_id: str) -> SessionData | None: """Load and restore a session from the remote ACP server.""" - from agentpool.agents.acp_agent.acp_converters import ( + from wolfharness.agents.acp_agent.acp_converters import ( acp_notifications_to_messages, mcp_config_to_acp, ) - from agentpool.agents.acp_agent.helpers import filter_servers_by_capabilities - from agentpool.sessions.models import SessionData - from agentpool.utils.time_utils import get_now + from wolfharness.agents.acp_agent.helpers import filter_servers_by_capabilities + from wolfharness.sessions.models import SessionData + from wolfharness.utils.time_utils import get_now if not self._api: self.log.error("Cannot load session: not connected to ACP server") diff --git a/src/agentpool/agents/acp_agent/acp_converters.py b/src/wolfharness/agents/acp_agent/acp_converters.py similarity index 97% rename from src/agentpool/agents/acp_agent/acp_converters.py rename to src/wolfharness/agents/acp_agent/acp_converters.py index d5516e420..14fcd6f4a 100644 --- a/src/agentpool/agents/acp_agent/acp_converters.py +++ b/src/wolfharness/agents/acp_agent/acp_converters.py @@ -1,6 +1,6 @@ """ACP to native event converters. -This module provides conversion from ACP session updates to native agentpool +This module provides conversion from ACP session updates to native wolfharness streaming events, enabling ACPAgent to yield the same event types as native agents. This is the reverse of the conversion done in acp_server/session.py handle_event(). @@ -55,7 +55,7 @@ UsageUpdate, UserMessageChunk, ) -from agentpool.agents.events import ( +from wolfharness.agents.events import ( DiffContentItem, LocationContentItem, PartDeltaEvent, @@ -88,10 +88,10 @@ ToolCallLocation, ) from acp.schema.mcp import AcpMcpServer - from agentpool.agents.events import RichAgentStreamEvent, ToolCallContentItem - from agentpool.agents.modes import ModeCategory, ModeInfo - from agentpool.messaging.messages import ChatMessage - from agentpool_config.mcp_server import ( + from wolfharness.agents.events import RichAgentStreamEvent, ToolCallContentItem + from wolfharness.agents.modes import ModeCategory, ModeInfo + from wolfharness.messaging.messages import ChatMessage + from wolfharness_config.mcp_server import ( AcpMCPServerConfig, MCPServerConfig, SSEMCPServerConfig, @@ -113,7 +113,7 @@ def get_modes( available_modes: SessionModeState | None, available_models: SessionModelState | None, ) -> list[ModeCategory]: - from agentpool.agents.modes import ModeCategory, ModeInfo + from wolfharness.agents.modes import ModeCategory, ModeInfo categories: list[ModeCategory] = [] @@ -213,7 +213,7 @@ def convert_acp_content(content: Sequence[ToolCallContent] | None) -> list[ToolC case FileEditToolCallContent(path=path, old_text=old_text, new_text=new_text): result.append(DiffContentItem(path=path, old_text=old_text, new_text=new_text)) case ContentToolCallContent(content=TextContentBlock(text=text)): - from agentpool.agents.events import TextContentItem + from wolfharness.agents.events import TextContentItem result.append(TextContentItem(text=text)) return result @@ -360,7 +360,7 @@ def acp_to_native_event( # noqa: PLR0911 # Plan update -> PlanUpdateEvent case AgentPlanUpdate(entries=entries): - from agentpool.utils.todos import PlanEntry + from wolfharness.utils.todos import PlanEntry native_entries = [ PlanEntry(content=e.content, priority=e.priority, status=e.status) for e in entries @@ -410,14 +410,14 @@ def mcp_config_to_acp(config: MCPServerConfig) -> McpServer: the server is wrapped with mcp-filter proxy to apply the filtering. Args: - config: agentpool MCP server configuration + config: wolfharness MCP server configuration Returns: ACP-compatible McpServer instance, or None if conversion not possible """ from acp.schema.common import EnvVariable from acp.schema.mcp import AcpMcpServer, HttpMcpServer, SseMcpServer, StdioMcpServer - from agentpool_config.mcp_server import ( + from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, @@ -533,7 +533,7 @@ def reset(self) -> None: def _finalize_current_message(self) -> None: """Finalize the current message and add it to the messages list.""" - from agentpool.messaging.messages import ChatMessage + from wolfharness.messaging.messages import ChatMessage if self._current_role is None: return diff --git a/src/agentpool/agents/acp_agent/client_handler.py b/src/wolfharness/agents/acp_agent/client_handler.py similarity index 97% rename from src/agentpool/agents/acp_agent/client_handler.py rename to src/wolfharness/agents/acp_agent/client_handler.py index 1251983ec..f74111434 100644 --- a/src/agentpool/agents/acp_agent/client_handler.py +++ b/src/wolfharness/agents/acp_agent/client_handler.py @@ -21,8 +21,8 @@ WaitForTerminalExitResponse, WriteTextFileResponse, ) -from agentpool.agents.exceptions import OperationNotAllowedError, ResourceNotFoundError -from agentpool.log import get_logger +from wolfharness.agents.exceptions import OperationNotAllowedError, ResourceNotFoundError +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -46,9 +46,9 @@ WaitForTerminalExitRequest, WriteTextFileRequest, ) - from agentpool.agents.acp_agent import ACPAgent - from agentpool.agents.acp_agent.session_state import ACPSessionState - from agentpool.ui.base import InputProvider + from wolfharness.agents.acp_agent import ACPAgent + from wolfharness.agents.acp_agent.session_state import ACPSessionState + from wolfharness.ui.base import InputProvider logger = get_logger(__name__) @@ -133,7 +133,7 @@ async def session_update(self, params: SessionNotification[Any]) -> None: CurrentModelUpdate, CurrentModeUpdate, ) - from agentpool.agents.modes import ModeInfo + from wolfharness.agents.modes import ModeInfo # Handle state updates - these modify session state, not stream data match params.update: @@ -408,8 +408,8 @@ async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any async def ext_notification(self, method: str, params: dict[str, Any]) -> None: """Handle extension notifications.""" match method: - case "_agentpool/toast": - from agentpool.agents.events import ToastInfo + case "_wolfharness/toast": + from wolfharness.agents.events import ToastInfo toast = ToastInfo( message=params.get("message", ""), @@ -489,11 +489,11 @@ async def run_cmd(ctx: Any, args: list[str], kwargs: dict[str, str]) -> None: if __name__ == "__main__": - from agentpool.agents.acp_agent import ACPAgent + from wolfharness.agents.acp_agent import ACPAgent async def main() -> None: """Demo: Basic call to an ACP agent.""" - args = ["run", "agentpool", "serve-acp"] + args = ["run", "wolfharness", "serve-acp"] cwd = str(Path.cwd()) async with ACPAgent(command="uv", args=args, cwd=cwd, event_handlers=["detailed"]) as agent: print("Response (streaming): ", end="", flush=True) diff --git a/src/agentpool/agents/acp_agent/helpers.py b/src/wolfharness/agents/acp_agent/helpers.py similarity index 97% rename from src/agentpool/agents/acp_agent/helpers.py rename to src/wolfharness/agents/acp_agent/helpers.py index a347cf349..c1f737c2e 100644 --- a/src/agentpool/agents/acp_agent/helpers.py +++ b/src/wolfharness/agents/acp_agent/helpers.py @@ -23,7 +23,7 @@ def filter_servers_by_capabilities( """Filter MCP servers based on agent's supported transports. Args: - servers: ACP-schema MCP servers to filter (not agentpool_config types) + servers: ACP-schema MCP servers to filter (not wolfharness_config types) agent_capabilities: Agent's capabilities (None = no HTTP/SSE support) logger: Optional logger for warnings about unsupported servers diff --git a/src/agentpool/agents/acp_agent/modes.py b/src/wolfharness/agents/acp_agent/modes.py similarity index 98% rename from src/agentpool/agents/acp_agent/modes.py rename to src/wolfharness/agents/acp_agent/modes.py index f0d2762a1..88f500209 100644 --- a/src/agentpool/agents/acp_agent/modes.py +++ b/src/wolfharness/agents/acp_agent/modes.py @@ -5,13 +5,13 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, ClassVar -from agentpool.agents.exceptions import UnknownModeError -from agentpool.agents.modes import ConfigOptionChanged, ModeCategoryProtocol, ModeInfo +from wolfharness.agents.exceptions import UnknownModeError +from wolfharness.agents.modes import ConfigOptionChanged, ModeCategoryProtocol, ModeInfo if TYPE_CHECKING: from acp.schema import SessionConfigOption, SessionModelState, SessionModeState - from agentpool.agents.acp_agent.acp_agent import ACPAgent + from wolfharness.agents.acp_agent.acp_agent import ACPAgent # ============================================================================= diff --git a/src/agentpool/agents/acp_agent/session_state.py b/src/wolfharness/agents/acp_agent/session_state.py similarity index 98% rename from src/agentpool/agents/acp_agent/session_state.py rename to src/wolfharness/agents/acp_agent/session_state.py index 18762c6f5..6f4318a2b 100644 --- a/src/agentpool/agents/acp_agent/session_state.py +++ b/src/wolfharness/agents/acp_agent/session_state.py @@ -6,7 +6,7 @@ from dataclasses import dataclass, field as dataclass_field from typing import TYPE_CHECKING -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool/agents/acp_agent/turn.py b/src/wolfharness/agents/acp_agent/turn.py similarity index 90% rename from src/agentpool/agents/acp_agent/turn.py rename to src/wolfharness/agents/acp_agent/turn.py index 7d013d516..554d3168d 100644 --- a/src/agentpool/agents/acp_agent/turn.py +++ b/src/wolfharness/agents/acp_agent/turn.py @@ -1,8 +1,8 @@ """ACP Turn — wraps ACP session/prompt stream into a single reactive Turn. -This module provides :class:`ACPTurn`, a :class:`~agentpool.orchestrator.turn.Turn` +This module provides :class:`ACPTurn`, a :class:`~wolfharness.orchestrator.turn.Turn` subclass that drives an ACP client through a single prompt → stream → complete -cycle, yielding :class:`~agentpool.agents.events.RichAgentStreamEvent` items +cycle, yielding :class:`~wolfharness.agents.events.RichAgentStreamEvent` items and populating ``message_history`` / ``final_message`` after execution. """ @@ -17,13 +17,13 @@ from pydantic import ValidationError from acp.exceptions import RequestError -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunErrorEvent, StreamCompleteEvent, ) -from agentpool.observability.spans import safe_span -from agentpool.orchestrator.turn import HookAwareTurn, Turn -from agentpool.utils.pydantic_ai_helpers import flatten_prompts +from wolfharness.observability.spans import safe_span +from wolfharness.orchestrator.turn import HookAwareTurn, Turn +from wolfharness.utils.pydantic_ai_helpers import flatten_prompts if TYPE_CHECKING: @@ -33,10 +33,10 @@ from pydantic_ai import ModelMessage, UserContent from acp.schema import ContentBlock, PromptResponse, SessionUpdate - from agentpool.agents.context import AgentRunContext - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.hooks import AgentHooks - from agentpool.messaging import ChatMessage + from wolfharness.agents.context import AgentRunContext + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.hooks import AgentHooks + from wolfharness.messaging import ChatMessage @runtime_checkable @@ -66,15 +66,15 @@ def _convert_updates_to_model_messages( ) -> tuple[list[ModelMessage], ChatMessage[str] | None]: """Convert ACP session updates to model messages and final chat message. - Uses :class:`~agentpool.agents.acp_agent.acp_converters.ACPMessageAccumulator` - to build :class:`~agentpool.messaging.ChatMessage` objects from the raw + Uses :class:`~wolfharness.agents.acp_agent.acp_converters.ACPMessageAccumulator` + to build :class:`~wolfharness.messaging.ChatMessage` objects from the raw session updates, then flattens the model messages. Returns: A tuple of (model_messages, final_chat_message). The final chat message is the last assistant message, or None if no messages were produced. """ - from agentpool.agents.acp_agent.acp_converters import ACPMessageAccumulator + from wolfharness.agents.acp_agent.acp_converters import ACPMessageAccumulator accumulator = ACPMessageAccumulator( session_id=session_id, @@ -153,11 +153,11 @@ async def execute(self) -> AsyncGenerator[RichAgentStreamEvent[Any]]: # noqa: P """ from pydantic_ai import RunUsage - from agentpool.agents.acp_agent.acp_converters import ( + from wolfharness.agents.acp_agent.acp_converters import ( acp_to_native_event, convert_to_acp_content, ) - from agentpool.agents.events import ( + from wolfharness.agents.events import ( StepUsageEvent, ToolCallCompleteEvent, ToolCallStartEvent, @@ -170,7 +170,7 @@ async def execute(self) -> AsyncGenerator[RichAgentStreamEvent[Any]]: # noqa: P turn_id=self._run_ctx.turn_id, session_id=self._run_ctx.session_id, ): - from agentpool.observability.trace import get_trace_id + from wolfharness.observability.trace import get_trace_id logfire.info( "Turn started", @@ -185,7 +185,7 @@ async def execute(self) -> AsyncGenerator[RichAgentStreamEvent[Any]]: # noqa: P pre_turn_result = await self._fire_pre_turn_hooks() if pre_turn_result is not None and pre_turn_result.get("decision") == "deny": self._run_ctx.cancelled = True - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage self._final_message = ChatMessage[str]( content="", @@ -296,7 +296,7 @@ async def execute(self) -> AsyncGenerator[RichAgentStreamEvent[Any]]: # noqa: P if final_msg is not None: self._final_message = final_msg else: - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage self._final_message = ChatMessage[str]( content="", diff --git a/src/agentpool/agents/base_agent.py b/src/wolfharness/agents/base_agent.py similarity index 96% rename from src/agentpool/agents/base_agent.py rename to src/wolfharness/agents/base_agent.py index 2d8c2859b..cb282a642 100644 --- a/src/agentpool/agents/base_agent.py +++ b/src/wolfharness/agents/base_agent.py @@ -19,21 +19,21 @@ import anyio from upathtools.filesystems import IsolatedMemoryFileSystem -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events import ( RunErrorEvent, StreamCompleteEvent, resolve_event_handlers, ) -from agentpool.agents.modes import ModeInfo -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.common_types import IndividualEventHandler -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage, MessageHistory, MessageNode -from agentpool.observability.spans import safe_span -from agentpool.prompts.convert import convert_prompts -from agentpool.utils.inspection import call_with_context -from agentpool.utils.time_utils import get_now +from wolfharness.agents.modes import ModeInfo +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.common_types import IndividualEventHandler +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage, MessageHistory, MessageNode +from wolfharness.observability.spans import safe_span +from wolfharness.prompts.convert import convert_prompts +from wolfharness.utils.inspection import call_with_context +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: @@ -51,16 +51,16 @@ from upathtools.filesystems import OverlayFileSystem from acp.schema import AvailableCommandsUpdate - from agentpool.agents.events import ( + from wolfharness.agents.events import ( CommandCompleteEvent, CommandOutputEvent, RichAgentStreamEvent, StreamWithCommandsEvent, ToastInfo, ) - from agentpool.agents.modes import ConfigOptionChanged, ModeCategory, ModeCategoryId - from agentpool.agents.native_agent import Agent - from agentpool.common_types import ( + from wolfharness.agents.modes import ConfigOptionChanged, ModeCategory, ModeCategoryId + from wolfharness.agents.native_agent import Agent + from wolfharness.common_types import ( AgentName, AnyEventHandlerType, MCPServerStatus, @@ -68,17 +68,17 @@ PromptCompatible, StrPath, ) - from agentpool.delegation import AgentPool, BaseTeam - from agentpool.hooks import AgentHooks - from agentpool.messaging import ChatMessage - from agentpool.orchestrator.core import EventBus, SessionPool, SessionState - from agentpool.orchestrator.run import RunHandle - from agentpool.orchestrator.turn import Turn - from agentpool.sessions import SessionData - from agentpool.talk.stats import MessageStats - from agentpool.ui.base import InputProvider - from agentpool_config.lifecycle import LifecycleConfig - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness.delegation import AgentPool, BaseTeam + from wolfharness.hooks import AgentHooks + from wolfharness.messaging import ChatMessage + from wolfharness.orchestrator.core import EventBus, SessionPool, SessionState + from wolfharness.orchestrator.run import RunHandle + from wolfharness.orchestrator.turn import Turn + from wolfharness.sessions import SessionData + from wolfharness.talk.stats import MessageStats + from wolfharness.ui.base import InputProvider + from wolfharness_config.lifecycle import LifecycleConfig + from wolfharness_config.mcp_server import MCPServerConfig # Union type for state updates emitted via state_updated signal type StateUpdate = ( @@ -234,8 +234,8 @@ def __init__( from exxec import ExecutionEnvironment, LocalExecutionEnvironment from slashed import CommandStore - from agentpool.agents.staged_content import StagedContent - from agentpool_commands import get_commands + from wolfharness.agents.staged_content import StagedContent + from wolfharness_commands import get_commands super().__init__( name=name, @@ -373,8 +373,8 @@ def __and__(self, other: MessageNode[Any, Any] | ProcessorCallback[Any]) -> Base group = analyzer & planner & executor # Create group of 3 group = analyzer & existing_group # Add to existing group """ - from agentpool.agents.native_agent import Agent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents.native_agent import Agent + from wolfharness.delegation.base_team import BaseTeam match other: case BaseTeam(): @@ -398,8 +398,8 @@ def __or__(self, other: ProcessorCallback[Any]) -> BaseTeam[Any, Any]: ... def __or__(self, other: MessageNode[Any, Any] | ProcessorCallback[Any]) -> BaseTeam[Any, Any]: # Create new execution with sequential mode (for piping) - from agentpool.agents.native_agent import Agent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents.native_agent import Agent + from wolfharness.delegation.base_team import BaseTeam if callable(other): other = Agent.from_callback(other, agent_pool=self._agent_pool) @@ -407,7 +407,7 @@ def __or__(self, other: MessageNode[Any, Any] | ProcessorCallback[Any]) -> BaseT return BaseTeam([self, other], mode="sequential") async def update_state(self, config_id: str, value_id: str) -> None: - from agentpool.agents.modes import ConfigOptionChanged + from wolfharness.agents.modes import ConfigOptionChanged self.log.info("Config option changed", config_id=config_id, mode=value_id) change = ConfigOptionChanged(config_id=config_id, value_id=value_id) @@ -572,9 +572,9 @@ async def get_resource(self, name: str) -> Any: """ import asyncio - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.resource_protocols import ResourceAccess - from agentpool.tools.exceptions import ToolError + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.resource_protocols import ResourceAccess + from wolfharness.tools.exceptions import ToolError all_resources: list[Any] = [] @@ -626,9 +626,9 @@ async def list_prompts(self) -> list[Any]: Returns: List of prompt objects from capabilities that provide prompts """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.prompts.prompts import MCPClientPrompt as MCPPrompt + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.prompts.prompts import MCPClientPrompt as MCPPrompt all_prompts: list[Any] = [] @@ -693,7 +693,7 @@ async def _temporary_tools( Yields: List of registered tool infos """ - from agentpool.tools.base import Tool as ToolClass + from wolfharness.tools.base import Tool as ToolClass match tools: case str() | Callable() | ToolClass(): @@ -820,7 +820,7 @@ def create_run( A RunHandle wired with agent, event_bus, session, and run_ctx, ready to be started via ``start(prompt)``. """ - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle return RunHandle( run_id=run_ctx.run_id, @@ -1203,7 +1203,7 @@ def _maybe_pool_stream( _skip_pool: bool, ) -> AsyncIterator[RichAgentStreamEvent[TResult]] | None: """Return async iterator for pool-based streaming, or None if not applicable.""" - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id if ( not _skip_pool @@ -1303,8 +1303,8 @@ async def _prepare_standalone_context( Returns: (run_ctx, effective_session_id, local_bus, stream, created_local_bus) """ - from agentpool.orchestrator.core import EventBus - from agentpool.utils.identifiers import generate_session_id + from wolfharness.orchestrator.core import EventBus + from wolfharness.utils.identifiers import generate_session_id if _run_ctx is not None: run_ctx = _run_ctx @@ -1408,7 +1408,7 @@ async def run_stream( # type: ignore[override] # noqa: PLR0915 Yields: Stream events during execution """ - from agentpool.orchestrator.core import drain_and_merge + from wolfharness.orchestrator.core import drain_and_merge # --- Path A: SessionPool available & outside a turn context --- pool_stream = self._maybe_pool_stream( @@ -1454,7 +1454,7 @@ async def run_stream( # type: ignore[override] # noqa: PLR0915 # SessionController). When None or all-defaults, RunHandle's # __post_init__ creates in-memory defaults automatically. if self._lifecycle_config is not None and not self._lifecycle_config.is_all_defaults(): - from agentpool.lifecycle.factory import create_dimensions + from wolfharness.lifecycle.factory import create_dimensions self._lifecycle_dimensions = create_dimensions( self._lifecycle_config, @@ -1567,7 +1567,7 @@ async def _run_stream_once( Yields: Stream events during execution """ - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage # Convert prompts to standard UserContent format converted_prompts = await convert_prompts(prompts) @@ -1685,7 +1685,7 @@ async def _execute_slash_command_streaming( CommandOutputEvent as SlashedCommandOutputEvent, ) - from agentpool.agents.events import CommandCompleteEvent, CommandOutputEvent + from wolfharness.agents.events import CommandCompleteEvent, CommandOutputEvent parsed = _parse_slash_command(command_text) if not parsed: @@ -1931,7 +1931,7 @@ async def _interrupt(self, run_ctx: AgentRunContext | None = None) -> None: async def get_stats(self) -> MessageStats: """Get message statistics.""" - from agentpool.talk.stats import MessageStats + from wolfharness.talk.stats import MessageStats return MessageStats(messages=list(self.conversation.chat_messages)) @@ -2153,7 +2153,7 @@ async def load_rules(self, project_dir: str | None = None) -> None: """Load agent rules from global and project locations. Searches for AGENTS.md/CLAUDE.md files in: - 1. Global: ~/.config/agentpool/AGENTS.md (user-wide rules) + 1. Global: ~/.config/wolfharness/AGENTS.md (user-wide rules) 2. Project: {project_dir}/AGENTS.md (project-specific rules) Both are merged and staged for injection into the first prompt. @@ -2164,7 +2164,7 @@ async def load_rules(self, project_dir: str | None = None) -> None: project_dir: Project directory to search for rules. Falls back to env.cwd if not provided. """ - from agentpool_config.resolution import get_global_config_dir + from wolfharness_config.resolution import get_global_config_dir effective_project_dir = project_dir or self.env.cwd fs = self.env.get_fs() diff --git a/src/agentpool/agents/context.py b/src/wolfharness/agents/context.py similarity index 96% rename from src/agentpool/agents/context.py rename to src/wolfharness/agents/context.py index af3de8c27..d83a6be21 100644 --- a/src/agentpool/agents/context.py +++ b/src/wolfharness/agents/context.py @@ -17,23 +17,23 @@ ErrorData, ) -from agentpool.agents.prompt_injection import PromptInjectionManager -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext -from agentpool.tools import CallDeferred -from agentpool.ui.elicitation import normalize_elicit_content +from wolfharness.agents.prompt_injection import PromptInjectionManager +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext +from wolfharness.tools import CallDeferred +from wolfharness.ui.elicitation import normalize_elicit_content if TYPE_CHECKING: from upathtools.filesystems import IsolatedMemoryFileSystem, OverlayFileSystem - from agentpool import Agent - from agentpool.agents.events import StreamEventEmitter - from agentpool.agents.native_agent.checkpoint import CheckpointManager - from agentpool.agents.native_agent.elicitation_bridge import ElicitationFutureRegistry - from agentpool.orchestrator.core import EventBus - from agentpool.orchestrator.run import RunHandle - from agentpool.tools.base import Tool + from wolfharness import Agent + from wolfharness.agents.events import StreamEventEmitter + from wolfharness.agents.native_agent.checkpoint import CheckpointManager + from wolfharness.agents.native_agent.elicitation_bridge import ElicitationFutureRegistry + from wolfharness.orchestrator.core import EventBus + from wolfharness.orchestrator.run import RunHandle + from wolfharness.tools.base import Tool ConfirmationResult = Literal["allow", "skip", "abort_run", "abort_chain"] @@ -269,7 +269,7 @@ class AgentContext[TDeps = Any](NodeContext[TDeps]): @property def native_agent(self) -> Agent[TDeps, Any]: """Current agent, type-narrowed to native pydantic-ai Agent.""" - from agentpool import Agent + from wolfharness import Agent assert isinstance(self.node, Agent) return self.node # ty: ignore[invalid-return-type] @@ -382,8 +382,8 @@ async def handle_elicitation(self, params: ElicitRequestParams) -> ElicitResult # Checkpoint the session for crash recovery. if run_ctx.checkpoint_manager is not None: try: - from agentpool.agents.events.events import ElicitationDeferredEvent - from agentpool.sessions.models import PendingDeferredCall + from wolfharness.agents.events.events import ElicitationDeferredEvent + from wolfharness.sessions.models import PendingDeferredCall pending_call = PendingDeferredCall( tool_call_id=handle, @@ -481,7 +481,7 @@ async def handle_elicitation(self, params: ElicitRequestParams) -> ElicitResult else: payload = await future except TimeoutError: - from agentpool.tasks.exceptions import RunAbortedError + from wolfharness.tasks.exceptions import RunAbortedError logger.warning( "Elicitation timed out — aborting agent run", @@ -599,7 +599,7 @@ def get_session_state(self) -> Any | None: async def report_progress(self, progress: float, total: float | None, message: str) -> None: """Report progress by emitting event into the agent's stream.""" - from agentpool.agents.events import ToolCallProgressEvent + from wolfharness.agents.events import ToolCallProgressEvent logger.info("Reporting tool call progress", progress=progress, total=total, message=message) progress_event = ToolCallProgressEvent( @@ -620,7 +620,7 @@ async def report_progress(self, progress: float, total: float | None, message: s @property def events(self) -> StreamEventEmitter: """Get event emitter with context automatically injected.""" - from agentpool.agents.events import StreamEventEmitter + from wolfharness.agents.events import StreamEventEmitter event_bus = self.run_ctx.event_bus if self.run_ctx else None return StreamEventEmitter(self, event_bus=event_bus) @@ -745,12 +745,12 @@ async def create_child_session( **agent_kwargs, ) else: - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id child_sid = generate_session_id() else: # Fallback: no pool, no session_pool — generate ephemeral ID. - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id child_sid = generate_session_id() @@ -763,7 +763,7 @@ async def create_child_session( raise SubagentDepthError( f"Subagent depth {child_depth} exceeds limit {MAX_SUBAGENT_DEPTH}", ) - from agentpool.agents.events.events import SpawnSessionStart + from wolfharness.agents.events.events import SpawnSessionStart event_spawn_mechanism: Literal["task", "spawn"] = ( "task" if spawn_mechanism == "task" else "spawn" diff --git a/src/agentpool/agents/events/__init__.py b/src/wolfharness/agents/events/__init__.py similarity index 100% rename from src/agentpool/agents/events/__init__.py rename to src/wolfharness/agents/events/__init__.py diff --git a/src/agentpool/agents/events/builtin_handlers.py b/src/wolfharness/agents/events/builtin_handlers.py similarity index 95% rename from src/agentpool/agents/events/builtin_handlers.py rename to src/wolfharness/agents/events/builtin_handlers.py index 16255fba2..695e48895 100644 --- a/src/agentpool/agents/events/builtin_handlers.py +++ b/src/wolfharness/agents/events/builtin_handlers.py @@ -18,22 +18,22 @@ ToolReturnPart, ) -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunErrorEvent, StreamCompleteEvent, ToolCallProgressEvent, ToolCallStartEvent, UserMessageInsertedEvent, ) -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.agents.context import AgentContext - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.common_types import AnyEventHandlerType, IndividualEventHandler + from wolfharness.agents.context import AgentContext + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.common_types import AnyEventHandlerType, IndividualEventHandler async def simple_print_handler(ctx: AgentContext[Any], event: RichAgentStreamEvent[Any]) -> None: diff --git a/src/agentpool/agents/events/event_emitter.py b/src/wolfharness/agents/events/event_emitter.py similarity index 96% rename from src/agentpool/agents/events/event_emitter.py rename to src/wolfharness/agents/events/event_emitter.py index 2c7d4d22e..a0fc08105 100644 --- a/src/agentpool/agents/events/event_emitter.py +++ b/src/wolfharness/agents/events/event_emitter.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Literal -from agentpool.agents.events import ( +from wolfharness.agents.events import ( CustomEvent, DiffContentItem, LocationContentItem, @@ -13,18 +13,18 @@ ToolCallProgressEvent, ToolCallStartEvent, ) -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.agents.context import AgentContext - from agentpool.agents.events import RichAgentStreamEvent, ToolCallContentItem - from agentpool.agents.events.events import ToolCallStatus - from agentpool.orchestrator.core import EventBus - from agentpool.tools.base import ToolKind - from agentpool.utils.todos import PlanEntry + from wolfharness.agents.context import AgentContext + from wolfharness.agents.events import RichAgentStreamEvent, ToolCallContentItem + from wolfharness.agents.events.events import ToolCallStatus + from wolfharness.orchestrator.core import EventBus + from wolfharness.tools.base import ToolKind + from wolfharness.utils.todos import PlanEntry logger = get_logger(__name__) diff --git a/src/agentpool/agents/events/events.py b/src/wolfharness/agents/events/events.py similarity index 99% rename from src/agentpool/agents/events/events.py rename to src/wolfharness/agents/events/events.py index 3f435ef14..3f59a492b 100644 --- a/src/agentpool/agents/events/events.py +++ b/src/wolfharness/agents/events/events.py @@ -33,16 +33,16 @@ ToolCallPartDelta, ) -from agentpool.messaging import ChatMessage # noqa: TC001 +from wolfharness.messaging import ChatMessage # noqa: TC001 if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.lifecycle.types import RunState - from agentpool.messaging.messages import TokenCost - from agentpool.tools.base import ToolKind - from agentpool.utils.todos import PlanEntry + from wolfharness.lifecycle.types import RunState + from wolfharness.messaging.messages import TokenCost + from wolfharness.tools.base import ToolKind + from wolfharness.utils.todos import PlanEntry ToolCallStatus = Literal["pending", "in_progress", "completed", "failed"] diff --git a/src/agentpool/agents/events/infer_info.py b/src/wolfharness/agents/events/infer_info.py similarity index 97% rename from src/agentpool/agents/events/infer_info.py rename to src/wolfharness/agents/events/infer_info.py index f6c45124e..04ca01547 100644 --- a/src/agentpool/agents/events/infer_info.py +++ b/src/wolfharness/agents/events/infer_info.py @@ -5,14 +5,14 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any -from agentpool.agents.events import DiffContentItem, LocationContentItem +from wolfharness.agents.events import DiffContentItem, LocationContentItem if TYPE_CHECKING: from clawd_code_sdk.models import ToolInput - from agentpool.agents.events import ToolCallContentItem - from agentpool.tools.base import ToolKind + from wolfharness.agents.events import ToolCallContentItem + from wolfharness.tools.base import ToolKind @dataclass diff --git a/src/agentpool/agents/events/processors.py b/src/wolfharness/agents/events/processors.py similarity index 97% rename from src/agentpool/agents/events/processors.py rename to src/wolfharness/agents/events/processors.py index 6e38ec9f9..465992e21 100644 --- a/src/agentpool/agents/events/processors.py +++ b/src/wolfharness/agents/events/processors.py @@ -35,13 +35,13 @@ async def log_events(stream): ToolReturnPart, ) -from agentpool.agents.events import ToolCallProgressEvent, ToolCallStartEvent +from wolfharness.agents.events import ToolCallProgressEvent, ToolCallStartEvent if TYPE_CHECKING: from collections.abc import AsyncIterator, Coroutine - from agentpool.agents.events.events import RichAgentStreamEvent + from wolfharness.agents.events.events import RichAgentStreamEvent # Type alias for processor callables diff --git a/src/agentpool/agents/events/tts_handlers.py b/src/wolfharness/agents/events/tts_handlers.py similarity index 95% rename from src/agentpool/agents/events/tts_handlers.py rename to src/wolfharness/agents/events/tts_handlers.py index 7c03d1169..7047393d9 100644 --- a/src/agentpool/agents/events/tts_handlers.py +++ b/src/wolfharness/agents/events/tts_handlers.py @@ -7,14 +7,14 @@ from pydantic_ai import PartDeltaEvent, PartStartEvent, TextPart, TextPartDelta -from agentpool.agents.events import UserMessageInsertedEvent +from wolfharness.agents.events import UserMessageInsertedEvent if TYPE_CHECKING: from anyvoice import TTSStream - from agentpool.agents.context import AgentContext - from agentpool.agents.events import RichAgentStreamEvent + from wolfharness.agents.context import AgentContext + from wolfharness.agents.events import RichAgentStreamEvent TTSMode = Literal["sync_sentence", "sync_run", "async_queue", "async_cancel"] TTSModel = Literal["tts-1", "tts-1-hd"] @@ -71,7 +71,7 @@ async def _close_stream(self) -> None: async def __call__(self, ctx: AgentContext[Any], event: RichAgentStreamEvent[Any]) -> None: """Handle stream events and trigger TTS synthesis.""" - from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent + from wolfharness.agents.events import RunStartedEvent, StreamCompleteEvent match event: case RunStartedEvent(): diff --git a/src/agentpool/agents/exceptions.py b/src/wolfharness/agents/exceptions.py similarity index 100% rename from src/agentpool/agents/exceptions.py rename to src/wolfharness/agents/exceptions.py diff --git a/src/agentpool/agents/interactions.py b/src/wolfharness/agents/interactions.py similarity index 96% rename from src/agentpool/agents/interactions.py rename to src/wolfharness/agents/interactions.py index 446032f1d..c5ec71da8 100644 --- a/src/agentpool/agents/interactions.py +++ b/src/wolfharness/agents/interactions.py @@ -8,8 +8,8 @@ from schemez import Schema -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage if TYPE_CHECKING: @@ -17,10 +17,10 @@ from toprompt import AnyPromptType - from agentpool import AgentPool, MessageNode - from agentpool.agents import Agent - from agentpool.common_types import SupportsStructuredOutput - from agentpool.delegation.base_team import BaseTeam + from wolfharness import AgentPool, MessageNode + from wolfharness.agents import Agent + from wolfharness.common_types import SupportsStructuredOutput + from wolfharness.delegation.base_team import BaseTeam ExtractionMode = Literal["structured", "tool_calls"] @@ -72,7 +72,7 @@ def get_label(item: Any) -> str: - types use __name__ - others use __repr__ for unique identifiable string """ - from agentpool import MessageNode + from wolfharness import MessageNode match item: case str(): @@ -209,8 +209,8 @@ async def pick[T]( # Get items and create label mapping from toprompt import to_prompt - from agentpool import AgentPool - from agentpool.delegation.base_team import BaseTeam + from wolfharness import AgentPool + from wolfharness.delegation.base_team import BaseTeam match selections: case dict(): @@ -308,8 +308,8 @@ async def pick_multiple[T]( """ from toprompt import to_prompt - from agentpool import AgentPool - from agentpool.delegation.base_team import BaseTeam + from wolfharness import AgentPool + from wolfharness.delegation.base_team import BaseTeam match selections: case AgentPool(): diff --git a/src/agentpool/agents/modes.py b/src/wolfharness/agents/modes.py similarity index 98% rename from src/agentpool/agents/modes.py rename to src/wolfharness/agents/modes.py index c105b1d77..50d87a6ec 100644 --- a/src/agentpool/agents/modes.py +++ b/src/wolfharness/agents/modes.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent # TypeVar for generic protocol - contravariant because it's used in method parameters AgentT_contra = TypeVar("AgentT_contra", bound="BaseAgent", contravariant=True) diff --git a/src/agentpool/agents/native_agent/__init__.py b/src/wolfharness/agents/native_agent/__init__.py similarity index 100% rename from src/agentpool/agents/native_agent/__init__.py rename to src/wolfharness/agents/native_agent/__init__.py diff --git a/src/agentpool/agents/native_agent/agent.py b/src/wolfharness/agents/native_agent/agent.py similarity index 93% rename from src/agentpool/agents/native_agent/agent.py rename to src/wolfharness/agents/native_agent/agent.py index 36646d303..7fd02314e 100644 --- a/src/agentpool/agents/native_agent/agent.py +++ b/src/wolfharness/agents/native_agent/agent.py @@ -20,20 +20,20 @@ from pydantic_ai.capabilities import NativeTool, ProcessHistory from pydantic_ai.models import Model -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import ( RunErrorEvent, ) -from agentpool.agents.exceptions import UnknownCategoryError, UnknownModeError -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage, MessageHistory -from agentpool.storage import StorageManager -from agentpool.tools import Tool -from agentpool.tools.exceptions import ToolError -from agentpool.utils.result_utils import to_type +from wolfharness.agents.exceptions import UnknownCategoryError, UnknownModeError +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage, MessageHistory +from wolfharness.storage import StorageManager +from wolfharness.tools import Tool +from wolfharness.tools.exceptions import ToolError +from wolfharness.utils.result_utils import to_type if TYPE_CHECKING: @@ -53,10 +53,10 @@ from toprompt import AnyPromptType from upathtools import JoinablePathLike - from agentpool.agents.context import AgentRunContext - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.agents.modes import ModeCategory - from agentpool.common_types import ( + from wolfharness.agents.context import AgentRunContext + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.agents.modes import ModeCategory + from wolfharness.common_types import ( AnyEventHandlerType, EndStrategy, ModelType, @@ -65,22 +65,22 @@ StrPath, ToolType, ) - from agentpool.delegation import AgentPool - from agentpool.hooks import AgentHooks - from agentpool.mcp_server.config_snapshot import McpConfigEntry - from agentpool.messaging import MessageNode - from agentpool.models.agents import NativeAgentConfig, ToolMode - from agentpool.models.model_configs import BaseModelConfig - from agentpool.orchestrator.turn import Turn - from agentpool.prompts.prompts import PromptType - from agentpool.sessions import SessionData - from agentpool.tools.base import FunctionTool - from agentpool.ui.base import InputProvider - from agentpool_config.knowledge import Knowledge - from agentpool_config.mcp_server import MCPServerConfig - from agentpool_config.model_capabilities import ModelCapabilities - from agentpool_config.nodes import ToolConfirmationMode - from agentpool_config.session import MemoryConfig, SessionQuery + from wolfharness.delegation import AgentPool + from wolfharness.hooks import AgentHooks + from wolfharness.mcp_server.config_snapshot import McpConfigEntry + from wolfharness.messaging import MessageNode + from wolfharness.models.agents import NativeAgentConfig, ToolMode + from wolfharness.models.model_configs import BaseModelConfig + from wolfharness.orchestrator.turn import Turn + from wolfharness.prompts.prompts import PromptType + from wolfharness.sessions import SessionData + from wolfharness.tools.base import FunctionTool + from wolfharness.ui.base import InputProvider + from wolfharness_config.knowledge import Knowledge + from wolfharness_config.mcp_server import MCPServerConfig + from wolfharness_config.model_capabilities import ModelCapabilities + from wolfharness_config.nodes import ToolConfirmationMode + from wolfharness_config.session import MemoryConfig, SessionQuery logger = get_logger(__name__) @@ -90,10 +90,10 @@ def _build_capability_from_config(config: Any) -> Any: """Build a capability instance from a config model. - Delegates to ``agentpool_config.capabilities.build_capability()``, + Delegates to ``wolfharness_config.capabilities.build_capability()``, using late import to avoid a static config→core dependency. """ - from agentpool_config.capabilities import build_capability + from wolfharness_config.capabilities import build_capability return build_capability(config) @@ -105,7 +105,7 @@ def _model_config_names(config: Any) -> list[str]: For configs with an ``identifier`` field, returns ``[identifier]``. For ``TestModelConfig``, returns ``[]`` (no tokonomics lookup). """ - from agentpool.models.model_configs import ( + from wolfharness.models.model_configs import ( AnthropicModelConfig, FallbackModelConfig, GeminiModelConfig, @@ -152,7 +152,7 @@ def _intersect_capabilities( For each field, ``False`` wins over ``True`` — if any model says ``False``, the result is ``False``. """ - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities fields = ( "image_input", @@ -212,7 +212,7 @@ class Agent[TDeps = None, OutputDataT = str](BaseAgent[TDeps, OutputDataT]): def __init__( # noqa: PLR0915 # we dont use AgentKwargs here so that we can work with explicit ones in the ctor self, - name: str = "agentpool", + name: str = "wolfharness", *, deps_type: type[TDeps] | None = None, model: ModelType, @@ -303,13 +303,13 @@ def __init__( # noqa: PLR0915 Used for capability resolution when the agent references a model variant by name. """ - from agentpool.agents.interactions import Interactions - from agentpool.agents.native_agent.hook_manager import NativeAgentHookManager - from agentpool.agents.sys_prompts import SystemPrompts - from agentpool.models.manifest import AgentsManifest - from agentpool.prompts.conversion_manager import ConversionManager - from agentpool_commands.pool import CompactCommand - from agentpool_config.session import MemoryConfig + from wolfharness.agents.interactions import Interactions + from wolfharness.agents.native_agent.hook_manager import NativeAgentHookManager + from wolfharness.agents.sys_prompts import SystemPrompts + from wolfharness.models.manifest import AgentsManifest + from wolfharness.prompts.conversion_manager import ConversionManager + from wolfharness_commands.pool import CompactCommand + from wolfharness_config.session import MemoryConfig self.model_settings = model_settings self.config = agent_config @@ -417,7 +417,7 @@ def __init__( # noqa: PLR0915 # can reuse them instead of rebuilding (preventing double-append bug). self._config_capabilities_built: list[Any] = [] if self.config and self.config.capabilities: - from agentpool_config.capabilities import build_config_capabilities + from wolfharness_config.capabilities import build_config_capabilities built_caps = build_config_capabilities(self.config.capabilities) self._external_capabilities.extend(built_caps) @@ -432,7 +432,7 @@ def _build_pool_configs(self) -> tuple[McpConfigEntry, ...]: Returns: Tuple of pool-scoped MCP config entries. """ - from agentpool.mcp_server.config_snapshot import McpConfigEntry + from wolfharness.mcp_server.config_snapshot import McpConfigEntry if self.host_context is None: return () @@ -453,7 +453,7 @@ def _build_agent_configs(self) -> tuple[McpConfigEntry, ...]: Returns: Tuple of agent-scoped MCP config entries. """ - from agentpool.mcp_server.config_snapshot import McpConfigEntry + from wolfharness.mcp_server.config_snapshot import McpConfigEntry if self._mcp_shared: return () @@ -526,7 +526,7 @@ def _resolve_history_processors(self, *, _warn: bool = True) -> list[Callable[.. if memory_cfg := self.conversation._config: processor_paths = getattr(memory_cfg, "history_processors", None) or [] if processor_paths: - from agentpool.utils.importing import import_callable + from wolfharness.utils.importing import import_callable for path in processor_paths: try: @@ -572,16 +572,16 @@ def from_config( # noqa: PLR0915 Returns: Configured Agent instance """ - from agentpool.models.manifest import AgentsManifest - from agentpool.utils.result_utils import to_type - from agentpool_config.system_prompts import ( + from wolfharness.models.manifest import AgentsManifest + from wolfharness.utils.result_utils import to_type + from wolfharness_config.system_prompts import ( FilePromptConfig, FunctionPromptConfig, LibraryPromptConfig, PackagePromptConfig, StaticPromptConfig, ) - from agentpool_toolsets.builtin.workers import WorkersTools + from wolfharness_toolsets.builtin.workers import WorkersTools name = config.name or "native_agent" # Get manifest from pool or create empty one @@ -652,7 +652,7 @@ def from_config( # noqa: PLR0915 merged_handlers: list[AnyEventHandlerType] = [*config_handlers, *(event_handlers or [])] # Handle model configuration - resolve model_variants reference if needed - from agentpool.models.model_configs import BaseModelConfig, StringModelConfig + from wolfharness.models.model_configs import BaseModelConfig, StringModelConfig model_config = config.model if ( @@ -763,9 +763,9 @@ def from_callback( name: Optional name for the agent kwargs: Additional arguments for agent """ - from agentpool.utils.inspection import get_fn_name - from agentpool.utils.model_helpers import function_to_model - from agentpool.utils.signatures import get_return_type + from wolfharness.utils.inspection import get_fn_name + from wolfharness.utils.model_helpers import function_to_model + from wolfharness.utils.signatures import get_return_type name = name or get_fn_name(callback) or "processor" model = function_to_model(callback) @@ -775,7 +775,7 @@ def from_callback( @property def name(self) -> str: """Get agent name.""" - return self._name or "agentpool" + return self._name or "wolfharness" @name.setter def name(self, value: str) -> None: @@ -792,7 +792,7 @@ def _resolve_model_string(self, model: str) -> tuple[Model, ModelSettings | None Tuple of (Model instance, ModelSettings or None) Settings are only returned for variants. """ - from agentpool.utils.model_helpers import infer_model + from wolfharness.utils.model_helpers import infer_model # Check if it's a variant ctx = self.host_context @@ -885,7 +885,7 @@ def _get_model_names_for_capability_resolution(self) -> list[str]: For fallback models, returns all sub-model names so the caller can compute the intersection (pessimistic) of capabilities. """ - from agentpool.models.model_configs import ( + from wolfharness.models.model_configs import ( BaseModelConfig, FallbackModelConfig, ) @@ -921,7 +921,7 @@ def _get_declared_capabilities(self) -> ModelCapabilities | None: Returns ``None`` when no model config or no capabilities field is present. """ - from agentpool.models.model_configs import BaseModelConfig + from wolfharness.models.model_configs import BaseModelConfig model_cfg: BaseModelConfig | str | None = self._resolved_model_config if model_cfg is None and self.config is not None: @@ -949,7 +949,7 @@ async def _resolve_model_capabilities( For ``FallbackModelConfig``, returns declared capabilities directly without per-model cache lookups or intersection. """ - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities declared = self._get_declared_capabilities() model_names = self._get_model_names_for_capability_resolution() @@ -965,7 +965,7 @@ async def _resolve_model_capabilities( declared = ModelCapabilities() if len(model_names) == 1: - from agentpool.host.stubs import resolve_capabilities + from wolfharness.host.stubs import resolve_capabilities return await resolve_capabilities( model_names[0], @@ -1043,7 +1043,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 try: provider_tools = await provider.get_tools() for tool in provider_tools: - from agentpool.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool context_for_tools = self.get_context( input_provider=input_provider, run_ctx=run_ctx @@ -1056,7 +1056,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 provider=type(provider).__name__, ) # 2. Hooks capability — always registered (unified tool interception) - from agentpool.agents.native_agent.tool_intercept import ToolInterceptCapability + from wolfharness.agents.native_agent.tool_intercept import ToolInterceptCapability hooks_capability = ToolInterceptCapability(hook_manager=self._hook_manager) tool_capabilities.append(hooks_capability) @@ -1064,7 +1064,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 # approval_bridge can resolve them. Block-strategy calls are # excluded from returned results so they remain unresolved for # CheckpointManager (Task 13). - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -1086,8 +1086,8 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 # MCP servers) before approval_bridge. Checkpoints the session, # emits ElicitationDeferredEvent, and registers a future for # later resolution when the user responds. - from agentpool.agents.native_agent.checkpoint import CheckpointManager - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.checkpoint import CheckpointManager + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, create_elicitation_bridge_capability, ) @@ -1113,7 +1113,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 ) ) # 4. Approval bridge: routes pydantic-ai deferred approvals to InputProvider - from agentpool.agents.native_agent.approval_bridge import ( + from wolfharness.agents.native_agent.approval_bridge import ( create_approval_bridge_capability, ) @@ -1129,7 +1129,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 if pool is not None: pool_capabilities = pool.skill_capabilities if pool_capabilities: - from agentpool.capabilities.skill_manager_cap import SkillManagerCap + from wolfharness.capabilities.skill_manager_cap import SkillManagerCap tool_capabilities.extend( cap for cap in pool_capabilities if isinstance(cap, SkillManagerCap) @@ -1153,7 +1153,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 if registry is not None: session_id = run_ctx.session_id if session_id not in self._registered_session_ids: - from agentpool.capabilities.extension_registry import ( + from wolfharness.capabilities.extension_registry import ( Scope, ScopeLevel, ) @@ -1164,7 +1164,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 if pool is not None: pool_caps = pool.skill_capabilities if pool_caps: - from agentpool.capabilities.skill_manager_cap import ( + from wolfharness.capabilities.skill_manager_cap import ( SkillManagerCap, ) @@ -1221,7 +1221,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 if self.config and self.config.capabilities and not self._config_capabilities_built: from pydantic import BaseModel as _BaseModel - from agentpool_config.capabilities import ( + from wolfharness_config.capabilities import ( EntryPointCapabilityConfig, GenericCapabilityConfig, build_capability, @@ -1256,7 +1256,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 # resolved model capabilities. This is NOT auto-injection — # the user must explicitly configure ``type: modality_filter`` # in YAML capabilities for this to activate. - from agentpool.capabilities.modality_filter import ( + from wolfharness.capabilities.modality_filter import ( ModalityFilterCapability, ) @@ -1283,7 +1283,7 @@ async def get_agentlet[AgentOutputType]( # noqa: PLR0915 if self.host_context is not None and run_ctx is not None: registry = self.host_context.extension_registry if registry is not None: - from agentpool.capabilities.extension_registry import ( + from wolfharness.capabilities.extension_registry import ( Scope, ScopeLevel, ) @@ -1337,15 +1337,15 @@ async def _execute_node(self, *prompts: Any, **kwargs: Any) -> ChatMessage[Any]: """Execute agent as a pydantic-graph step node. Detects graph context via *_state* in kwargs (injected by - :class:`~agentpool.messaging.graph_adapter.MessageNodeStep`) and - delegates execution to :class:`~agentpool.agents.native_agent.turn.NativeTurn`, + :class:`~wolfharness.messaging.graph_adapter.MessageNodeStep`) and + delegates execution to :class:`~wolfharness.agents.native_agent.turn.NativeTurn`, forwarding all events to the EventBus for the parent graph to drain. Args: *prompts: Input prompts passed from the graph. **kwargs: Must contain ``_state`` (an - :class:`~agentpool.messaging.graph_adapter.AgentPoolState`). + :class:`~wolfharness.messaging.graph_adapter.AgentPoolState`). Returns: The final response ChatMessage. @@ -1353,7 +1353,7 @@ async def _execute_node(self, *prompts: Any, **kwargs: Any) -> ChatMessage[Any]: Raises: RuntimeError: If ``_state`` or required sub-keys are missing. """ - from agentpool.messaging.graph_adapter import AgentPoolState + from wolfharness.messaging.graph_adapter import AgentPoolState state = kwargs.get("_state") if not isinstance(state, AgentPoolState): @@ -1369,7 +1369,7 @@ async def _execute_node(self, *prompts: Any, **kwargs: Any) -> ChatMessage[Any]: # Get or create EventBus for graph path — events flow through EventBus # instead of state.event_queue. - from agentpool.orchestrator.core import EventBus + from wolfharness.orchestrator.core import EventBus event_bus = run_ctx.event_bus if event_bus is None: @@ -1418,7 +1418,7 @@ async def _stream_events( ) -> AsyncIterator[RichAgentStreamEvent[OutputDataT]]: """Stream agent events in real-time using NativeTurn. - Delegates to :class:`~agentpool.agents.native_agent.turn.NativeTurn` + Delegates to :class:`~wolfharness.agents.native_agent.turn.NativeTurn` which drives the PydanticAI agent run loop with ``agent_run.next(node)``, yielding fine-grained streaming events including ``RunStartedEvent``, ``PartStartEvent``, ``ToolCallStartEvent``, and ``StreamCompleteEvent``. @@ -1432,7 +1432,7 @@ async def _stream_events( # Get or create EventBus — events go directly to EventBus. event_bus = run_ctx.event_bus if event_bus is None: - from agentpool.orchestrator.core import EventBus + from wolfharness.orchestrator.core import EventBus event_bus = EventBus() run_ctx.event_bus = event_bus @@ -1443,7 +1443,7 @@ async def _stream_events( model_messages.extend(chat_msg.messages) # Inject RetryPromptPart for any trailing unprocessed tool calls # (e.g. from a cancelled turn). - from agentpool.orchestrator.run import inject_cancelled_tool_results + from wolfharness.orchestrator.run import inject_cancelled_tool_results model_messages = inject_cancelled_tool_results(model_messages) @@ -1522,7 +1522,7 @@ def create_turn( # deferred_tool_results (same tool_call_id appears in both, # causing "already executed" or mismatch errors). if "deferred_tool_results" not in pydantic_ai_kwargs: - from agentpool.orchestrator.run import inject_cancelled_tool_results + from wolfharness.orchestrator.run import inject_cancelled_tool_results message_history = inject_cancelled_tool_results(message_history) return NativeTurn( @@ -1765,7 +1765,7 @@ def _parse_npmmirror_providers( # Zero additional network requests — data is already available. if all_models: try: - from agentpool.host.stubs import _get_default_cache + from wolfharness.host.stubs import _get_default_cache cache = _get_default_cache() for model_info in all_models: @@ -1872,8 +1872,8 @@ def _parse_npmmirror_model( async def get_modes(self) -> list[ModeCategory]: """Get available mode categories for this agent.""" - from agentpool.agents.modes import ModeCategory as ModeCategoryRuntime, ModeInfo - from agentpool.agents.native_agent.helpers import ( + from wolfharness.agents.modes import ModeCategory as ModeCategoryRuntime, ModeInfo + from wolfharness.agents.native_agent.helpers import ( get_model_category, get_permission_category, ) diff --git a/src/agentpool/agents/native_agent/approval_bridge.py b/src/wolfharness/agents/native_agent/approval_bridge.py similarity index 96% rename from src/agentpool/agents/native_agent/approval_bridge.py rename to src/wolfharness/agents/native_agent/approval_bridge.py index 72f34ecc3..240e7d69c 100644 --- a/src/agentpool/agents/native_agent/approval_bridge.py +++ b/src/wolfharness/agents/native_agent/approval_bridge.py @@ -19,12 +19,12 @@ ToolDenied, ) -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool import Agent - from agentpool.agents.context import AgentContext, ConfirmationResult + from wolfharness import Agent + from wolfharness.agents.context import AgentContext, ConfirmationResult logger = get_logger(__name__) diff --git a/src/agentpool/agents/native_agent/checkpoint.py b/src/wolfharness/agents/native_agent/checkpoint.py similarity index 96% rename from src/agentpool/agents/native_agent/checkpoint.py rename to src/wolfharness/agents/native_agent/checkpoint.py index 7a81a4c88..fdcc87bb6 100644 --- a/src/agentpool/agents/native_agent/checkpoint.py +++ b/src/wolfharness/agents/native_agent/checkpoint.py @@ -10,9 +10,9 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any -from agentpool.agents.events.events import ToolCallDeferredEvent -from agentpool.log import get_logger -from agentpool.storage.serialization import ( +from wolfharness.agents.events.events import ToolCallDeferredEvent +from wolfharness.log import get_logger +from wolfharness.storage.serialization import ( deferred_calls_adapter, messages_adapter, ) @@ -21,8 +21,8 @@ if TYPE_CHECKING: from pydantic_ai.messages import ModelMessage - from agentpool.sessions.models import PendingDeferredCall - from agentpool.storage.manager import StorageManager + from wolfharness.sessions.models import PendingDeferredCall + from wolfharness.storage.manager import StorageManager logger = get_logger(__name__) @@ -185,7 +185,7 @@ async def checkpoint( message_count > self._compaction_threshold_messages or byte_size > self._compaction_threshold_bytes ): - from agentpool.messaging.compaction import ( + from wolfharness.messaging.compaction import ( CompactionPipeline, KeepLastMessages, TruncateToolOutputs, diff --git a/src/agentpool/agents/native_agent/deferred_bridge.py b/src/wolfharness/agents/native_agent/deferred_bridge.py similarity index 97% rename from src/agentpool/agents/native_agent/deferred_bridge.py rename to src/wolfharness/agents/native_agent/deferred_bridge.py index 7dd3fe4de..2091fc35b 100644 --- a/src/agentpool/agents/native_agent/deferred_bridge.py +++ b/src/wolfharness/agents/native_agent/deferred_bridge.py @@ -24,14 +24,14 @@ from pydantic_ai.capabilities import HandleDeferredToolCalls from pydantic_ai.messages import ToolReturn -from agentpool.agents.events.events import ToolCallDeferredEvent -from agentpool.log import get_logger +from wolfharness.agents.events.events import ToolCallDeferredEvent +from wolfharness.log import get_logger if TYPE_CHECKING: from pydantic_ai.tools import DeferredToolRequests, DeferredToolResults, RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext logger = get_logger(__name__) diff --git a/src/agentpool/agents/native_agent/elicitation_bridge.py b/src/wolfharness/agents/native_agent/elicitation_bridge.py similarity index 97% rename from src/agentpool/agents/native_agent/elicitation_bridge.py rename to src/wolfharness/agents/native_agent/elicitation_bridge.py index 2b4c2ea0c..ea62a429e 100644 --- a/src/agentpool/agents/native_agent/elicitation_bridge.py +++ b/src/wolfharness/agents/native_agent/elicitation_bridge.py @@ -24,18 +24,18 @@ from mcp import types from pydantic_ai.capabilities import HandleDeferredToolCalls -from agentpool.agents.events.events import ElicitationDeferredEvent -from agentpool.log import get_logger -from agentpool.sessions.models import PendingDeferredCall +from wolfharness.agents.events.events import ElicitationDeferredEvent +from wolfharness.log import get_logger +from wolfharness.sessions.models import PendingDeferredCall if TYPE_CHECKING: from pydantic_ai.messages import ModelMessage from pydantic_ai.tools import DeferredToolRequests, DeferredToolResults, RunContext - from agentpool.agents.context import AgentContext - from agentpool.agents.native_agent.checkpoint import CheckpointManager - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.agents.context import AgentContext + from wolfharness.agents.native_agent.checkpoint import CheckpointManager + from wolfharness.sessions.models import ElicitationResumePayload logger = get_logger(__name__) diff --git a/src/agentpool/agents/native_agent/elicitation_strategy.py b/src/wolfharness/agents/native_agent/elicitation_strategy.py similarity index 96% rename from src/agentpool/agents/native_agent/elicitation_strategy.py rename to src/wolfharness/agents/native_agent/elicitation_strategy.py index 4f1734ccb..86d5b8368 100644 --- a/src/agentpool/agents/native_agent/elicitation_strategy.py +++ b/src/wolfharness/agents/native_agent/elicitation_strategy.py @@ -18,8 +18,8 @@ if TYPE_CHECKING: from pydantic_ai.messages import ModelMessage - from agentpool.agents.native_agent.checkpoint import CheckpointManager - from agentpool.sessions.models import ElicitationResumePayload, PendingDeferredCall + from wolfharness.agents.native_agent.checkpoint import CheckpointManager + from wolfharness.sessions.models import ElicitationResumePayload, PendingDeferredCall @runtime_checkable diff --git a/src/agentpool/agents/native_agent/helpers.py b/src/wolfharness/agents/native_agent/helpers.py similarity index 92% rename from src/agentpool/agents/native_agent/helpers.py rename to src/wolfharness/agents/native_agent/helpers.py index 253994e13..23d340eda 100644 --- a/src/agentpool/agents/native_agent/helpers.py +++ b/src/wolfharness/agents/native_agent/helpers.py @@ -25,17 +25,17 @@ VideoUrl, ) -from agentpool.agents.events import ToolCallCompleteEvent, UserMessageInsertedEvent -from agentpool.agents.modes import ModeCategory, ModeInfo -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.agents.events import ToolCallCompleteEvent, UserMessageInsertedEvent +from wolfharness.agents.modes import ModeCategory, ModeInfo +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict if TYPE_CHECKING: from tokonomics.model_discovery import ModelInfo - from agentpool.agents.context import AgentRunContext - from agentpool.agents.events import RichAgentStreamEvent - from agentpool_config.nodes import ToolConfirmationMode + from wolfharness.agents.context import AgentRunContext + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness_config.nodes import ToolConfirmationMode async def process_tool_event( @@ -186,7 +186,7 @@ def _summarize_content_block(block: Any) -> str: block, BinaryImage | BinaryContent | ImageUrl | AudioUrl | VideoUrl | DocumentUrl | UploadedFile, ): - from agentpool.capabilities.modality_utils import describe_multimodal_content + from wolfharness.capabilities.modality_utils import describe_multimodal_content return describe_multimodal_content(block) return f"[{type(block).__name__}]" diff --git a/src/agentpool/agents/native_agent/hook_manager.py b/src/wolfharness/agents/native_agent/hook_manager.py similarity index 96% rename from src/agentpool/agents/native_agent/hook_manager.py rename to src/wolfharness/agents/native_agent/hook_manager.py index 0991f639c..ff552d5df 100644 --- a/src/agentpool/agents/native_agent/hook_manager.py +++ b/src/wolfharness/agents/native_agent/hook_manager.py @@ -15,15 +15,15 @@ from typing import TYPE_CHECKING, Any -from agentpool.hooks.base import HookResult -from agentpool.log import get_logger +from wolfharness.hooks.base import HookResult +from wolfharness.log import get_logger if TYPE_CHECKING: from exxec import ExecutionEnvironment - from agentpool.agents.base_agent import BaseAgent - from agentpool.hooks import AgentHooks + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.hooks import AgentHooks logger = get_logger(__name__) diff --git a/src/agentpool/agents/native_agent/process_history_capability.py b/src/wolfharness/agents/native_agent/process_history_capability.py similarity index 98% rename from src/agentpool/agents/native_agent/process_history_capability.py rename to src/wolfharness/agents/native_agent/process_history_capability.py index 2e336b354..ec04e5407 100644 --- a/src/agentpool/agents/native_agent/process_history_capability.py +++ b/src/wolfharness/agents/native_agent/process_history_capability.py @@ -1,6 +1,7 @@ """Adapter for converting AgentPool history processors to pydantic-ai ProcessHistory capabilities. -Replaces manual history processor resolution in :class:`~agentpool.agents.native_agent.agent.Agent` +Replaces manual history processor resolution in +:class:`~wolfharness.agents.native_agent.agent.Agent` with a unified capability-based approach. AgentPool history processors may have untyped ``RunContext`` parameters; pydantic-ai's ``takes_run_context()`` requires the first parameter to be explicitly typed as ``RunContext``. This adapter wraps diff --git a/src/agentpool/agents/native_agent/tool_intercept.py b/src/wolfharness/agents/native_agent/tool_intercept.py similarity index 95% rename from src/agentpool/agents/native_agent/tool_intercept.py rename to src/wolfharness/agents/native_agent/tool_intercept.py index 138bd95cc..6013231da 100644 --- a/src/agentpool/agents/native_agent/tool_intercept.py +++ b/src/wolfharness/agents/native_agent/tool_intercept.py @@ -25,7 +25,7 @@ from pydantic_ai.capabilities.abstract import AbstractCapability from pydantic_ai.exceptions import ApprovalRequired, CallDeferred, ToolRetryError -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -36,7 +36,7 @@ from pydantic_ai.tools import RunContext, ToolDefinition from pydantic_ai.toolsets import AbstractToolset - from agentpool.agents.native_agent.hook_manager import NativeAgentHookManager + from wolfharness.agents.native_agent.hook_manager import NativeAgentHookManager logger = get_logger(__name__) @@ -120,7 +120,7 @@ async def wrap_tool_execute( from pydantic_ai.messages import ToolReturn - from agentpool.tools.base import ToolResult + from wolfharness.tools.base import ToolResult agent_ctx = ctx.deps tool_start_times: dict[str, float] | None = getattr(agent_ctx, "_tool_start_times", None) @@ -129,7 +129,7 @@ async def wrap_tool_execute( agent_ctx._tool_start_times = tool_start_times tool_start_times[call.tool_call_id] = perf_counter() - from agentpool.tasks.exceptions import RunAbortedError, ToolSkippedError + from wolfharness.tasks.exceptions import RunAbortedError, ToolSkippedError try: result = await handler(args) @@ -185,7 +185,7 @@ async def before_tool_execute( """ from pydantic_ai import ModelRetry - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext agent_ctx = ctx.deps env = agent_ctx.agent.env if isinstance(agent_ctx, AgentContext) else None @@ -232,8 +232,8 @@ async def after_tool_execute( Also consumes pending prompt injections from ``PromptInjectionManager``. """ - from agentpool.agents.context import AgentContext - from agentpool.agents.native_agent.tool_wrapping import ( + from wolfharness.agents.context import AgentContext + from wolfharness.agents.native_agent.tool_wrapping import ( _inject_additional_context, ) diff --git a/src/agentpool/agents/native_agent/tool_wrapping.py b/src/wolfharness/agents/native_agent/tool_wrapping.py similarity index 96% rename from src/agentpool/agents/native_agent/tool_wrapping.py rename to src/wolfharness/agents/native_agent/tool_wrapping.py index 4bec67686..bc4a53db7 100644 --- a/src/agentpool/agents/native_agent/tool_wrapping.py +++ b/src/wolfharness/agents/native_agent/tool_wrapping.py @@ -15,11 +15,11 @@ from pydantic_ai.exceptions import ApprovalRequired, CallDeferred from pydantic_ai.messages import ToolReturn -from agentpool.agents.context import AgentContext -from agentpool.log import get_logger -from agentpool.tools.base import ToolResult -from agentpool.utils.inspection import execute, get_argument_key -from agentpool.utils.signatures import create_modified_signature, update_signature +from wolfharness.agents.context import AgentContext +from wolfharness.log import get_logger +from wolfharness.tools.base import ToolResult +from wolfharness.utils.inspection import execute, get_argument_key +from wolfharness.utils.signatures import create_modified_signature, update_signature logger = get_logger(__name__) @@ -27,7 +27,7 @@ if TYPE_CHECKING: from collections.abc import Awaitable, Callable - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool def _inject_additional_context( diff --git a/src/agentpool/agents/native_agent/turn.py b/src/wolfharness/agents/native_agent/turn.py similarity index 97% rename from src/agentpool/agents/native_agent/turn.py rename to src/wolfharness/agents/native_agent/turn.py index ca23ea276..48bcc97fd 100644 --- a/src/agentpool/agents/native_agent/turn.py +++ b/src/wolfharness/agents/native_agent/turn.py @@ -21,28 +21,28 @@ from pydantic_ai.usage import RequestUsage from pydantic_graph import End -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( RunErrorEvent, StepErrorMetadata, StepUsageEvent, StreamCompleteEvent, ToolCallCompleteEvent, ) -from agentpool.agents.native_agent.helpers import extract_text_from_messages -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.messaging.messages import TokenCost -from agentpool.observability.spans import safe_span -from agentpool.orchestrator.event_mapper import ( +from wolfharness.agents.native_agent.helpers import extract_text_from_messages +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.messaging.messages import TokenCost +from wolfharness.observability.spans import safe_span +from wolfharness.orchestrator.event_mapper import ( EventMapper, normalize_thinking_parts_in_messages, sanitize_tool_call_args_in_messages, ) -from agentpool.orchestrator.turn import HookAwareTurn, Turn -from agentpool.tasks.exceptions import RunAbortedError -from agentpool.tools.base import is_terminal_tool -from agentpool.utils.pydantic_ai_helpers import flatten_prompts -from agentpool.utils.usage import diff_usage +from wolfharness.orchestrator.turn import HookAwareTurn, Turn +from wolfharness.tasks.exceptions import RunAbortedError +from wolfharness.tools.base import is_terminal_tool +from wolfharness.utils.pydantic_ai_helpers import flatten_prompts +from wolfharness.utils.usage import diff_usage if TYPE_CHECKING: @@ -51,10 +51,10 @@ from pydantic_ai import Agent as PydanticAgent from pydantic_ai.messages import ModelMessage - from agentpool.agents.context import AgentRunContext - from agentpool.agents.events.events import RichAgentStreamEvent - from agentpool.agents.native_agent.agent import Agent - from agentpool.hooks import AgentHooks + from wolfharness.agents.context import AgentRunContext + from wolfharness.agents.events.events import RichAgentStreamEvent + from wolfharness.agents.native_agent.agent import Agent + from wolfharness.hooks import AgentHooks logger = get_logger(__name__) @@ -157,7 +157,7 @@ async def execute(self) -> AsyncGenerator[RichAgentStreamEvent[Any]]: # noqa: P turn_id=self._run_ctx.turn_id, session_id=self._run_ctx.session_id, ): - from agentpool.observability.trace import get_trace_id + from wolfharness.observability.trace import get_trace_id logfire.info( "Turn started", diff --git a/src/agentpool/agents/prompt_injection.py b/src/wolfharness/agents/prompt_injection.py similarity index 98% rename from src/agentpool/agents/prompt_injection.py rename to src/wolfharness/agents/prompt_injection.py index 5491dbfa6..6e52f5973 100644 --- a/src/agentpool/agents/prompt_injection.py +++ b/src/wolfharness/agents/prompt_injection.py @@ -6,7 +6,7 @@ from __future__ import annotations -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) diff --git a/src/agentpool/agents/staged_content.py b/src/wolfharness/agents/staged_content.py similarity index 97% rename from src/agentpool/agents/staged_content.py rename to src/wolfharness/agents/staged_content.py index 0d02789af..adcd114cd 100644 --- a/src/agentpool/agents/staged_content.py +++ b/src/wolfharness/agents/staged_content.py @@ -7,7 +7,7 @@ from pydantic_ai import UserPromptPart -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool/agents/sys_prompts.py b/src/wolfharness/agents/sys_prompts.py similarity index 96% rename from src/agentpool/agents/sys_prompts.py rename to src/wolfharness/agents/sys_prompts.py index 51ea378b6..ba5c43a99 100644 --- a/src/agentpool/agents/sys_prompts.py +++ b/src/wolfharness/agents/sys_prompts.py @@ -5,8 +5,8 @@ from functools import cache from typing import TYPE_CHECKING, Any, Literal -from agentpool import text_templates -from agentpool.agents.exceptions import PromptResolutionError +from wolfharness import text_templates +from wolfharness.agents.exceptions import PromptResolutionError if TYPE_CHECKING: @@ -16,8 +16,8 @@ from pydantic_ai import RunContext from toprompt import AnyPromptType - from agentpool.agents.base_agent import BaseAgent - from agentpool.prompts.manager import PromptManager + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.prompts.manager import PromptManager ToolInjectionMode = Literal["off", "all"] @@ -185,7 +185,7 @@ async def to_pydantic_ai_instructions( """ import inspect - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction instructions: list[str | Callable[[RunContext[Any]], Awaitable[str]]] = [] diff --git a/src/agentpool/agents/tool_call_accumulator.py b/src/wolfharness/agents/tool_call_accumulator.py similarity index 100% rename from src/agentpool/agents/tool_call_accumulator.py rename to src/wolfharness/agents/tool_call_accumulator.py diff --git a/src/agentpool/auth/__init__.py b/src/wolfharness/auth/__init__.py similarity index 100% rename from src/agentpool/auth/__init__.py rename to src/wolfharness/auth/__init__.py diff --git a/src/agentpool/auth/anthropic_auth.py b/src/wolfharness/auth/anthropic_auth.py similarity index 100% rename from src/agentpool/auth/anthropic_auth.py rename to src/wolfharness/auth/anthropic_auth.py diff --git a/src/agentpool/capabilities/AGENTS.md b/src/wolfharness/capabilities/AGENTS.md similarity index 98% rename from src/agentpool/capabilities/AGENTS.md rename to src/wolfharness/capabilities/AGENTS.md index d4ed62d4a..b7a0fb03b 100644 --- a/src/agentpool/capabilities/AGENTS.md +++ b/src/wolfharness/capabilities/AGENTS.md @@ -26,7 +26,7 @@ - **AgentContext is frozen**: Constructed by RunLoop per-turn. Carries `agent_registry`, `delegation`, `session`, `scope`, `resources`, `host`. - **DelegationService limits exposure**: `spawn_subagent(name, prompt)` and `get_available_agents()` only. Does not expose full `AgentPool`. - **SkillCapability injection order**: In `get_agentlet()`, skill capabilities are injected at position 5 (after MCP, deferred bridge, approval bridge, and hook capabilities). -- **Entry-point registry**: Custom capabilities discovered via `agentpool.capabilities` entry-point group. +- **Entry-point registry**: Custom capabilities discovered via `wolfharness.capabilities` entry-point group. - **ExtensionRegistry scope hierarchy**: `POOL → AGENT → SESSION → TURN`. Agents outlive sessions (AGENT scope keyed by `agent_name` only, no `session_id`). `clear_session()` removes SESSION and TURN entries during teardown. Factory registers config-derived caps at AGENT scope; `get_agentlet()` registers session-specific caps (MCP, SkillManagerCap, ResourceCapability) at SESSION scope. - **ScopeLevel reorder is breaking**: `Scope` field order changed — `agent_name` is now 2nd (after `level`), `session_id` is 3rd. AGENT scope queries no longer include SESSION caps. diff --git a/src/agentpool/capabilities/__init__.py b/src/wolfharness/capabilities/__init__.py similarity index 100% rename from src/agentpool/capabilities/__init__.py rename to src/wolfharness/capabilities/__init__.py diff --git a/src/agentpool/capabilities/agent_context.py b/src/wolfharness/capabilities/agent_context.py similarity index 86% rename from src/agentpool/capabilities/agent_context.py rename to src/wolfharness/capabilities/agent_context.py index 470e825bc..a2eaa8bd9 100644 --- a/src/agentpool/capabilities/agent_context.py +++ b/src/wolfharness/capabilities/agent_context.py @@ -5,7 +5,7 @@ per-turn services that agent tools and capabilities need. This class is the ``deps`` type for pydantic-ai's ``RunContext``. -It is distinct from ``agentpool.agents.context.AgentContext`` which IS +It is distinct from ``wolfharness.agents.context.AgentContext`` which IS the ``RunContext`` itself. The name ``AgentContextDeps`` makes this relationship explicit: ``RunContext[AgentContextDeps]``. """ @@ -18,12 +18,12 @@ if TYPE_CHECKING: - from agentpool.capabilities.delegation import DelegationService - from agentpool.capabilities.extension_registry import ExtensionRegistry - from agentpool.host.context import HostContext, RunScope - from agentpool.host.registry import AgentRegistry - from agentpool.orchestrator.session_controller import SessionState - from agentpool_config.team_mode import TeamModeConfig + from wolfharness.capabilities.delegation import DelegationService + from wolfharness.capabilities.extension_registry import ExtensionRegistry + from wolfharness.host.context import HostContext, RunScope + from wolfharness.host.registry import AgentRegistry + from wolfharness.orchestrator.session_controller import SessionState + from wolfharness_config.team_mode import TeamModeConfig @dataclass(frozen=True, slots=True) @@ -77,7 +77,7 @@ def resolve_agent_context_from_deps( RuntimeError: If deps is None, ``.data`` is None, or deps is neither ``RuntimeAgentContext`` nor ``AgentContextDeps``. """ - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext if deps is None: msg = f"{capability_name} requires AgentContextDeps as deps. Got: None" @@ -103,7 +103,7 @@ def resolve_agent_context_from_deps( def __getattr__(name: str) -> Any: if name == "AgentContext": warnings.warn( - "agentpool.capabilities.agent_context.AgentContext is deprecated; " + "wolfharness.capabilities.agent_context.AgentContext is deprecated; " "use AgentContextDeps instead.", DeprecationWarning, stacklevel=2, diff --git a/src/agentpool/capabilities/background_task/__init__.py b/src/wolfharness/capabilities/background_task/__init__.py similarity index 73% rename from src/agentpool/capabilities/background_task/__init__.py rename to src/wolfharness/capabilities/background_task/__init__.py index 4009822d8..60b7ca5da 100644 --- a/src/agentpool/capabilities/background_task/__init__.py +++ b/src/wolfharness/capabilities/background_task/__init__.py @@ -7,17 +7,17 @@ from __future__ import annotations -from agentpool.capabilities.background_task.capability import ( +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.manager import ( +from wolfharness.capabilities.background_task.manager import ( BackgroundTaskManager, TERMINAL_STATES, ) -from agentpool.capabilities.background_task.notification import ( +from wolfharness.capabilities.background_task.notification import ( NotificationBatcher, ) -from agentpool.capabilities.background_task.types import ( +from wolfharness.capabilities.background_task.types import ( BackgroundTask, SessionTaskState, TaskHandle, diff --git a/src/agentpool/capabilities/background_task/capability.py b/src/wolfharness/capabilities/background_task/capability.py similarity index 98% rename from src/agentpool/capabilities/background_task/capability.py rename to src/wolfharness/capabilities/background_task/capability.py index 2c1138060..29efcf0e3 100644 --- a/src/agentpool/capabilities/background_task/capability.py +++ b/src/wolfharness/capabilities/background_task/capability.py @@ -26,9 +26,9 @@ from pydantic_ai.toolsets import AgentToolset, FunctionToolset from pydantic_graph import End -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import ( PartDeltaEvent, RunErrorEvent, RunFailedEvent, @@ -36,18 +36,18 @@ ToolCallCompleteEvent, ToolCallStartEvent, ) -from agentpool.capabilities.background_task.manager import TERMINAL_STATES, BackgroundTaskManager -from agentpool.capabilities.background_task.notification import ( +from wolfharness.capabilities.background_task.manager import TERMINAL_STATES, BackgroundTaskManager +from wolfharness.capabilities.background_task.notification import ( NotificationBatcher, _format_duration, ) -from agentpool.capabilities.background_task.types import BackgroundTask, SessionTaskState -from agentpool.orchestrator.core import EventEnvelope -from agentpool.skills.uri_resolver import ResolvedSkillURI -from agentpool.tools.exceptions import ToolError -from agentpool.utils.tool_schema import apply_params_schema, load_tool_schema -from agentpool_config.context import get_config_dir -from agentpool_toolsets.builtin.skills import load_skill_for_node +from wolfharness.capabilities.background_task.types import BackgroundTask, SessionTaskState +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness.skills.uri_resolver import ResolvedSkillURI +from wolfharness.tools.exceptions import ToolError +from wolfharness.utils.tool_schema import apply_params_schema, load_tool_schema +from wolfharness_config.context import get_config_dir +from wolfharness_toolsets.builtin.skills import load_skill_for_node if TYPE_CHECKING: @@ -58,8 +58,8 @@ from pydantic_ai.run import AgentRunResult from schemez.functionschema import OpenAIFunctionDefinition - from agentpool.agents.context import AgentRunContext - from agentpool.orchestrator.session_pool import SessionPool + from wolfharness.agents.context import AgentRunContext + from wolfharness.orchestrator.session_pool import SessionPool MAX_DELEGATION_DEPTH = 5 diff --git a/src/agentpool/capabilities/background_task/manager.py b/src/wolfharness/capabilities/background_task/manager.py similarity index 99% rename from src/agentpool/capabilities/background_task/manager.py rename to src/wolfharness/capabilities/background_task/manager.py index e5d8c207c..ed8c72fe2 100644 --- a/src/agentpool/capabilities/background_task/manager.py +++ b/src/wolfharness/capabilities/background_task/manager.py @@ -12,7 +12,7 @@ import logfire -from agentpool.capabilities.background_task.types import ( +from wolfharness.capabilities.background_task.types import ( BackgroundTask, TaskHandle, TaskStatus, diff --git a/src/agentpool/capabilities/background_task/notification.py b/src/wolfharness/capabilities/background_task/notification.py similarity index 99% rename from src/agentpool/capabilities/background_task/notification.py rename to src/wolfharness/capabilities/background_task/notification.py index 6a3f9ea0e..7010cbc9f 100644 --- a/src/agentpool/capabilities/background_task/notification.py +++ b/src/wolfharness/capabilities/background_task/notification.py @@ -20,7 +20,7 @@ from collections.abc import Awaitable, Callable from datetime import datetime - from agentpool.capabilities.background_task.types import ( + from wolfharness.capabilities.background_task.types import ( BackgroundTask, TaskStatus, ) diff --git a/src/agentpool/capabilities/background_task/types.py b/src/wolfharness/capabilities/background_task/types.py similarity index 93% rename from src/agentpool/capabilities/background_task/types.py rename to src/wolfharness/capabilities/background_task/types.py index b703815c2..6110f8cb9 100644 --- a/src/agentpool/capabilities/background_task/types.py +++ b/src/wolfharness/capabilities/background_task/types.py @@ -11,8 +11,8 @@ if TYPE_CHECKING: from collections.abc import Callable - from agentpool.capabilities.background_task.manager import BackgroundTaskManager - from agentpool.capabilities.background_task.notification import NotificationBatcher + from wolfharness.capabilities.background_task.manager import BackgroundTaskManager + from wolfharness.capabilities.background_task.notification import NotificationBatcher TaskStatus = Literal[ "pending", diff --git a/src/agentpool/capabilities/change_event.py b/src/wolfharness/capabilities/change_event.py similarity index 100% rename from src/agentpool/capabilities/change_event.py rename to src/wolfharness/capabilities/change_event.py diff --git a/src/agentpool/capabilities/code_mode_capability.py b/src/wolfharness/capabilities/code_mode_capability.py similarity index 97% rename from src/agentpool/capabilities/code_mode_capability.py rename to src/wolfharness/capabilities/code_mode_capability.py index 871eb4bdc..51008719b 100644 --- a/src/agentpool/capabilities/code_mode_capability.py +++ b/src/wolfharness/capabilities/code_mode_capability.py @@ -18,8 +18,8 @@ from pydantic_ai.toolsets import AgentToolset, FunctionToolset from schemez.code_generation.namespace_callable import NamespaceCallable -from agentpool.capabilities.codemode.default_prompt import USAGE -from agentpool.capabilities.codemode.helpers import ( +from wolfharness.capabilities.codemode.default_prompt import USAGE +from wolfharness.capabilities.codemode.helpers import ( tools_to_codegen, validate_code, ) @@ -28,7 +28,7 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator, Sequence - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool _CODE_MODE_INSTRUCTIONS = ( diff --git a/src/agentpool/capabilities/codemode/__init__.py b/src/wolfharness/capabilities/codemode/__init__.py similarity index 100% rename from src/agentpool/capabilities/codemode/__init__.py rename to src/wolfharness/capabilities/codemode/__init__.py diff --git a/src/agentpool/capabilities/codemode/default_prompt.py b/src/wolfharness/capabilities/codemode/default_prompt.py similarity index 100% rename from src/agentpool/capabilities/codemode/default_prompt.py rename to src/wolfharness/capabilities/codemode/default_prompt.py diff --git a/src/agentpool/capabilities/codemode/helpers.py b/src/wolfharness/capabilities/codemode/helpers.py similarity index 94% rename from src/agentpool/capabilities/codemode/helpers.py rename to src/wolfharness/capabilities/codemode/helpers.py index 25dbcd3cd..f92fdffd0 100644 --- a/src/agentpool/capabilities/codemode/helpers.py +++ b/src/wolfharness/capabilities/codemode/helpers.py @@ -5,7 +5,7 @@ import ast from typing import TYPE_CHECKING -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -13,7 +13,7 @@ from schemez import ToolsetCodeGenerator - from agentpool.tools import Tool + from wolfharness.tools import Tool logger = get_logger(__name__) @@ -64,7 +64,7 @@ def tools_to_codegen( from pydantic_ai import RunContext from schemez import ToolCodeGenerator, ToolsetCodeGenerator, create_schema - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext generators = [ ToolCodeGenerator( diff --git a/src/agentpool/capabilities/combined_toolset.py b/src/wolfharness/capabilities/combined_toolset.py similarity index 98% rename from src/agentpool/capabilities/combined_toolset.py rename to src/wolfharness/capabilities/combined_toolset.py index 54c8029ec..83687f051 100644 --- a/src/agentpool/capabilities/combined_toolset.py +++ b/src/wolfharness/capabilities/combined_toolset.py @@ -16,7 +16,7 @@ children, joins them with ``"\n\n"``, returns the result (or ``None``). - **``on_change()``**: merges the ``on_change()`` async generators from all children that provide one, using an ``asyncio.Queue`` bridge. - Yields :class:`~agentpool.capabilities.change_event.ChangeEvent` + Yields :class:`~wolfharness.capabilities.change_event.ChangeEvent` from any child. Returns ``None`` if no child provides ``on_change()``. - **``__aenter__``/``__aexit__``**: enters/exits all children, using ``AsyncExitStack`` for safe cleanup even if some children fail. @@ -40,8 +40,8 @@ from pydantic_ai.capabilities.abstract import AgentInstructions # type: ignore[attr-defined] - from agentpool.capabilities.change_event import ChangeEvent - from agentpool.tools.base import Tool + from wolfharness.capabilities.change_event import ChangeEvent + from wolfharness.tools.base import Tool # ---- Protocols for optional capability methods ---- diff --git a/src/agentpool/capabilities/dcp/__init__.py b/src/wolfharness/capabilities/dcp/__init__.py similarity index 71% rename from src/agentpool/capabilities/dcp/__init__.py rename to src/wolfharness/capabilities/dcp/__init__.py index 17b9a1efe..01c4344c8 100644 --- a/src/agentpool/capabilities/dcp/__init__.py +++ b/src/wolfharness/capabilities/dcp/__init__.py @@ -7,9 +7,9 @@ from __future__ import annotations -from agentpool.capabilities.dcp.capability import DynamicContextPruningCapability -from agentpool.capabilities.dcp.config import DCPConfig -from agentpool.capabilities.dcp.state import ( +from wolfharness.capabilities.dcp.capability import DynamicContextPruningCapability +from wolfharness.capabilities.dcp.config import DCPConfig +from wolfharness.capabilities.dcp.state import ( CompressionBlock, DCPState, WatermarkLevel, diff --git a/src/agentpool/capabilities/dcp/block_store.py b/src/wolfharness/capabilities/dcp/block_store.py similarity index 98% rename from src/agentpool/capabilities/dcp/block_store.py rename to src/wolfharness/capabilities/dcp/block_store.py index 8fa25d9e1..931681856 100644 --- a/src/agentpool/capabilities/dcp/block_store.py +++ b/src/wolfharness/capabilities/dcp/block_store.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: - from agentpool.capabilities.dcp.state import CompressionBlock + from wolfharness.capabilities.dcp.state import CompressionBlock @dataclass diff --git a/src/agentpool/capabilities/dcp/capability.py b/src/wolfharness/capabilities/dcp/capability.py similarity index 99% rename from src/agentpool/capabilities/dcp/capability.py rename to src/wolfharness/capabilities/dcp/capability.py index 928d11037..2eb69f8c8 100644 --- a/src/agentpool/capabilities/dcp/capability.py +++ b/src/wolfharness/capabilities/dcp/capability.py @@ -49,28 +49,28 @@ ) from pydantic_ai.toolsets import AgentToolset, FunctionToolset -from agentpool.agents.context import ( +from wolfharness.agents.context import ( AgentContext, # noqa: TC001 — needed at runtime for get_type_hints() ) -from agentpool.capabilities.dcp.block_store import ( +from wolfharness.capabilities.dcp.block_store import ( CompressionBlockStore, ) -from agentpool.capabilities.dcp.config import DCPConfig -from agentpool.capabilities.dcp.nudge import ( +from wolfharness.capabilities.dcp.config import DCPConfig +from wolfharness.capabilities.dcp.nudge import ( build_nudge_text, ) -from agentpool.capabilities.dcp.prunable_list import ( +from wolfharness.capabilities.dcp.prunable_list import ( META_TOOL_NAMES, build_prunable_list, inject_prunable_list, ) -from agentpool.capabilities.dcp.state import ( +from wolfharness.capabilities.dcp.state import ( CompressionBlock, DCPState, PruneAction, WatermarkLevel, ) -from agentpool.capabilities.dcp.strategies import ( +from wolfharness.capabilities.dcp.strategies import ( _apply_pruned_tools, _dedup_exact, _is_pruned, @@ -80,14 +80,14 @@ _strip_thinking_content, purge_failed_tool_inputs, ) -from agentpool.capabilities.dcp.token_utils import estimate_tokens -from agentpool.capabilities.dcp.tools import ( +from wolfharness.capabilities.dcp.token_utils import estimate_tokens +from wolfharness.capabilities.dcp.tools import ( DistillTargetInput, decompress_tool, distill_tool, prune_tool, ) -from agentpool.capabilities.dcp.watermark import ( +from wolfharness.capabilities.dcp.watermark import ( WatermarkStateMachine, ) diff --git a/src/agentpool/capabilities/dcp/config.py b/src/wolfharness/capabilities/dcp/config.py similarity index 98% rename from src/agentpool/capabilities/dcp/config.py rename to src/wolfharness/capabilities/dcp/config.py index 9c2b60101..85a6b9ef3 100644 --- a/src/agentpool/capabilities/dcp/config.py +++ b/src/wolfharness/capabilities/dcp/config.py @@ -10,7 +10,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator -from agentpool.capabilities.dcp.state import WatermarkLevel +from wolfharness.capabilities.dcp.state import WatermarkLevel class DCPConfig(BaseModel): diff --git a/src/agentpool/capabilities/dcp/nudge.py b/src/wolfharness/capabilities/dcp/nudge.py similarity index 96% rename from src/agentpool/capabilities/dcp/nudge.py rename to src/wolfharness/capabilities/dcp/nudge.py index df82220d6..a515e14d2 100644 --- a/src/agentpool/capabilities/dcp/nudge.py +++ b/src/wolfharness/capabilities/dcp/nudge.py @@ -23,14 +23,14 @@ ToolReturnPart, ) -from agentpool.capabilities.dcp.state import WatermarkLevel -from agentpool.capabilities.dcp.strategies import _is_pruned -from agentpool.capabilities.dcp.token_utils import estimate_tokens +from wolfharness.capabilities.dcp.state import WatermarkLevel +from wolfharness.capabilities.dcp.strategies import _is_pruned +from wolfharness.capabilities.dcp.token_utils import estimate_tokens if TYPE_CHECKING: - from agentpool.capabilities.dcp.config import DCPConfig - from agentpool.capabilities.dcp.state import DCPState + from wolfharness.capabilities.dcp.config import DCPConfig + from wolfharness.capabilities.dcp.state import DCPState __all__ = ["build_nudge_text"] diff --git a/src/agentpool/capabilities/dcp/prunable_list.py b/src/wolfharness/capabilities/dcp/prunable_list.py similarity index 96% rename from src/agentpool/capabilities/dcp/prunable_list.py rename to src/wolfharness/capabilities/dcp/prunable_list.py index 7ac019620..404d27f8a 100644 --- a/src/agentpool/capabilities/dcp/prunable_list.py +++ b/src/wolfharness/capabilities/dcp/prunable_list.py @@ -19,13 +19,13 @@ UserPromptPart, ) -from agentpool.capabilities.dcp.strategies import _is_pruned -from agentpool.capabilities.dcp.token_utils import estimate_tokens +from wolfharness.capabilities.dcp.strategies import _is_pruned +from wolfharness.capabilities.dcp.token_utils import estimate_tokens if TYPE_CHECKING: - from agentpool.capabilities.dcp.config import DCPConfig - from agentpool.capabilities.dcp.state import DCPState + from wolfharness.capabilities.dcp.config import DCPConfig + from wolfharness.capabilities.dcp.state import DCPState __all__ = ["META_TOOL_NAMES", "build_prunable_list", "inject_prunable_list"] diff --git a/src/agentpool/capabilities/dcp/state.py b/src/wolfharness/capabilities/dcp/state.py similarity index 99% rename from src/agentpool/capabilities/dcp/state.py rename to src/wolfharness/capabilities/dcp/state.py index e4452a447..49fcbc49c 100644 --- a/src/agentpool/capabilities/dcp/state.py +++ b/src/wolfharness/capabilities/dcp/state.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: from pydantic_ai.messages import ModelMessage - from agentpool.capabilities.dcp.block_store import CompressionBlockStore + from wolfharness.capabilities.dcp.block_store import CompressionBlockStore class WatermarkLevel(IntEnum): diff --git a/src/agentpool/capabilities/dcp/strategies.py b/src/wolfharness/capabilities/dcp/strategies.py similarity index 98% rename from src/agentpool/capabilities/dcp/strategies.py rename to src/wolfharness/capabilities/dcp/strategies.py index 612799b3f..ac9e51a5b 100644 --- a/src/agentpool/capabilities/dcp/strategies.py +++ b/src/wolfharness/capabilities/dcp/strategies.py @@ -23,12 +23,12 @@ UserPromptPart, ) -from agentpool.capabilities.dcp.state import CompressionBlock, DCPState +from wolfharness.capabilities.dcp.state import CompressionBlock, DCPState if TYPE_CHECKING: - from agentpool.capabilities.dcp.block_store import CompressionBlockStore - from agentpool.capabilities.dcp.config import DCPConfig + from wolfharness.capabilities.dcp.block_store import CompressionBlockStore + from wolfharness.capabilities.dcp.config import DCPConfig __all__ = [ diff --git a/src/agentpool/capabilities/dcp/token_utils.py b/src/wolfharness/capabilities/dcp/token_utils.py similarity index 100% rename from src/agentpool/capabilities/dcp/token_utils.py rename to src/wolfharness/capabilities/dcp/token_utils.py diff --git a/src/agentpool/capabilities/dcp/tools.py b/src/wolfharness/capabilities/dcp/tools.py similarity index 99% rename from src/agentpool/capabilities/dcp/tools.py rename to src/wolfharness/capabilities/dcp/tools.py index e7702d895..a08dc1a35 100644 --- a/src/agentpool/capabilities/dcp/tools.py +++ b/src/wolfharness/capabilities/dcp/tools.py @@ -26,18 +26,18 @@ from pydantic_ai import ModelRetry from pydantic_ai.messages import ModelResponse, ThinkingPart, ToolCallPart, ToolReturnPart -from agentpool.capabilities.dcp.state import ( +from wolfharness.capabilities.dcp.state import ( DCPState, DistillTarget, PruneAction, ) -from agentpool.capabilities.dcp.strategies import _is_pruned, _prune_part +from wolfharness.capabilities.dcp.strategies import _is_pruned, _prune_part if TYPE_CHECKING: from pydantic_ai import RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext class DistillTargetInput(TypedDict): diff --git a/src/agentpool/capabilities/dcp/watermark.py b/src/wolfharness/capabilities/dcp/watermark.py similarity index 96% rename from src/agentpool/capabilities/dcp/watermark.py rename to src/wolfharness/capabilities/dcp/watermark.py index 2419a52d0..a87b857e3 100644 --- a/src/agentpool/capabilities/dcp/watermark.py +++ b/src/wolfharness/capabilities/dcp/watermark.py @@ -8,8 +8,8 @@ from typing import TYPE_CHECKING -from agentpool.capabilities.dcp.state import WatermarkLevel -from agentpool.capabilities.dcp.token_utils import estimate_tokens +from wolfharness.capabilities.dcp.state import WatermarkLevel +from wolfharness.capabilities.dcp.token_utils import estimate_tokens if TYPE_CHECKING: diff --git a/src/agentpool/capabilities/delegation.py b/src/wolfharness/capabilities/delegation.py similarity index 100% rename from src/agentpool/capabilities/delegation.py rename to src/wolfharness/capabilities/delegation.py diff --git a/src/agentpool/capabilities/extension_registry.py b/src/wolfharness/capabilities/extension_registry.py similarity index 96% rename from src/agentpool/capabilities/extension_registry.py rename to src/wolfharness/capabilities/extension_registry.py index baf335898..20152a0ab 100644 --- a/src/agentpool/capabilities/extension_registry.py +++ b/src/wolfharness/capabilities/extension_registry.py @@ -24,7 +24,7 @@ from typing import TYPE_CHECKING, Any import warnings -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -32,8 +32,8 @@ from pydantic_ai.capabilities import AbstractCapability - from agentpool.capabilities.change_event import ChangeEvent - from agentpool.capabilities.resource_protocols import ( + from wolfharness.capabilities.change_event import ChangeEvent + from wolfharness.capabilities.resource_protocols import ( ChangeObservable, CommandResource, ResourceAccess, @@ -41,7 +41,7 @@ SkillResource, ToolAccess, ) - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill logger = get_logger(__name__) @@ -366,7 +366,7 @@ def get_skill_resources( Returns: List of capabilities implementing ``SkillResource``. """ - from agentpool.capabilities.resource_protocols import SkillResource + from wolfharness.capabilities.resource_protocols import SkillResource return [ cap for cap in self.get_visible_capabilities(scope) if isinstance(cap, SkillResource) @@ -384,7 +384,7 @@ def get_resource_access( Returns: List of capabilities implementing ``ResourceAccess``. """ - from agentpool.capabilities.resource_protocols import ResourceAccess + from wolfharness.capabilities.resource_protocols import ResourceAccess return [ cap for cap in self.get_visible_capabilities(scope) if isinstance(cap, ResourceAccess) @@ -402,7 +402,7 @@ def get_tool_access( Returns: List of capabilities implementing ``ToolAccess``. """ - from agentpool.capabilities.resource_protocols import ToolAccess + from wolfharness.capabilities.resource_protocols import ToolAccess return [cap for cap in self.get_visible_capabilities(scope) if isinstance(cap, ToolAccess)] @@ -418,7 +418,7 @@ def get_resource_template_access( Returns: List of capabilities implementing ``ResourceTemplateAccess``. """ - from agentpool.capabilities.resource_protocols import ResourceTemplateAccess + from wolfharness.capabilities.resource_protocols import ResourceTemplateAccess return [ cap @@ -463,7 +463,7 @@ def get_command_resources( Returns: List of capabilities implementing ``CommandResource``. """ - from agentpool.capabilities.resource_protocols import CommandResource + from wolfharness.capabilities.resource_protocols import CommandResource return [ cap for cap in self.get_visible_capabilities(scope) if isinstance(cap, CommandResource) @@ -481,7 +481,7 @@ def get_observable_capabilities( Returns: List of capabilities implementing ``ChangeObservable``. """ - from agentpool.capabilities.resource_protocols import ChangeObservable + from wolfharness.capabilities.resource_protocols import ChangeObservable return [ cap for cap in self.get_visible_capabilities(scope) if isinstance(cap, ChangeObservable) @@ -514,8 +514,8 @@ async def resolve_uri( """ if uri.startswith("skill://"): # Flat URI (D9): parse via ResolvedSkillURI for consistency. - from agentpool.skills.skill import Skill - from agentpool.skills.uri_resolver import ResolvedSkillURI, _name_alternatives + from wolfharness.skills.skill import Skill + from wolfharness.skills.uri_resolver import ResolvedSkillURI, _name_alternatives resolved = ResolvedSkillURI.parse(uri) skill_name = resolved.skill_name @@ -552,7 +552,7 @@ async def resolve_uri( return None if uri.startswith("mcp://"): - from agentpool.capabilities.resource_protocols import TextResourceContent + from wolfharness.capabilities.resource_protocols import TextResourceContent for resource_cap in self.get_resource_access(scope): try: diff --git a/src/agentpool/capabilities/file_team_state.py b/src/wolfharness/capabilities/file_team_state.py similarity index 100% rename from src/agentpool/capabilities/file_team_state.py rename to src/wolfharness/capabilities/file_team_state.py diff --git a/src/agentpool/capabilities/filtered_toolset.py b/src/wolfharness/capabilities/filtered_toolset.py similarity index 97% rename from src/agentpool/capabilities/filtered_toolset.py rename to src/wolfharness/capabilities/filtered_toolset.py index a48d6e2b2..b765cc719 100644 --- a/src/agentpool/capabilities/filtered_toolset.py +++ b/src/wolfharness/capabilities/filtered_toolset.py @@ -23,7 +23,7 @@ if TYPE_CHECKING: from types import TracebackType - from agentpool.capabilities.change_event import ChangeEvent + from wolfharness.capabilities.change_event import ChangeEvent ToolFilterFunc = Callable[[RunContext[AgentDepsT], ToolDefinition], bool | Awaitable[bool]] @@ -72,7 +72,7 @@ class FilteredToolsetCapability(AbstractCapability[AgentDepsT]): in a :class:`FilteredToolset` that applies the provided filter function. If the wrapped capability does not implement ``on_change()`` (i.e. - it is not an agentpool capability), ``on_change()`` returns ``None``. + it is not an wolfharness capability), ``on_change()`` returns ``None``. Attributes: wrapped: The inner ``AbstractCapability`` being filtered. @@ -165,7 +165,7 @@ def on_change(self) -> AsyncIterator[ChangeEvent] | None: """Delegate ``on_change()`` to the wrapped capability. If the wrapped capability implements ``on_change()`` (i.e. it - is an agentpool capability), its return value is forwarded. + is an wolfharness capability), its return value is forwarded. Otherwise, returns ``None`` — static capabilities never change. """ if isinstance(self._wrapped, _OnChangeCapable): diff --git a/src/agentpool/capabilities/function_toolset.py b/src/wolfharness/capabilities/function_toolset.py similarity index 90% rename from src/agentpool/capabilities/function_toolset.py rename to src/wolfharness/capabilities/function_toolset.py index 97370d744..7d538d912 100644 --- a/src/agentpool/capabilities/function_toolset.py +++ b/src/wolfharness/capabilities/function_toolset.py @@ -21,19 +21,19 @@ from collections.abc import AsyncIterator, Sequence from types import TracebackType - from agentpool import Agent, MessageNode - from agentpool.capabilities.change_event import ChangeEvent - from agentpool.common_types import ToolSource, ToolType - from agentpool.prompts.prompts import BasePrompt - from agentpool.tools.base import Tool + from wolfharness import Agent, MessageNode + from wolfharness.capabilities.change_event import ChangeEvent + from wolfharness.common_types import ToolSource, ToolType + from wolfharness.prompts.prompts import BasePrompt + from wolfharness.tools.base import Tool class FunctionToolsetCapability(AbstractCapability[AgentDepsT]): - """Wraps a static list of :class:`~agentpool.tools.base.Tool` instances. + """Wraps a static list of :class:`~wolfharness.tools.base.Tool` instances. Provides: - ``get_toolset()``: eagerly builds a ``FunctionToolset`` from the - wrapped tools via :func:`~agentpool.tools.tool_wrapping.wrap_tool_for_pydantic_ai`. + wrapped tools via :func:`~wolfharness.tools.tool_wrapping.wrap_tool_for_pydantic_ai`. Returns ``None`` if the tool list is empty. - ``get_instructions()``: returns the optional instructions string, or ``None`` if none were provided. @@ -85,7 +85,7 @@ async def get_prompts(self) -> list[BasePrompt]: def add_tool(self, tool: ToolType) -> None: """Add a tool to this capability.""" - from agentpool.tools.base import Tool as ToolClass + from wolfharness.tools.base import Tool as ToolClass match tool: case ToolClass(): @@ -109,8 +109,8 @@ def create_tool( prepare: Any | None = None, ) -> Tool[Any]: """Create a tool from a function and add it to this capability.""" - from agentpool.tools.base import Tool as ToolClass - from agentpool_config.tools import ToolHints + from wolfharness.tools.base import Tool as ToolClass + from wolfharness_config.tools import ToolHints tool = ToolClass.from_callable( fn=fn, @@ -152,7 +152,7 @@ def register_tool( metadata: dict[str, str] | None = None, ) -> Tool[Any]: """Register a new tool with custom settings.""" - from agentpool.tools.base import Tool as ToolClass + from wolfharness.tools.base import Tool as ToolClass match tool: case ToolClass(): @@ -184,8 +184,8 @@ def register_worker( parent: Agent[Any, Any] | None = None, ) -> Tool[Any]: """Register an agent as a worker tool.""" - from agentpool import Agent, BaseTeam - from agentpool.agents.base_agent import BaseAgent + from wolfharness import Agent, BaseTeam + from wolfharness.agents.base_agent import BaseAgent match worker: case Agent(): @@ -229,7 +229,7 @@ def tool( **kwargs: Any, ) -> Callable[..., Any] | Callable[[Callable[..., Any]], Callable[..., Any]]: """Decorator to register a function as a tool.""" - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool def decorator(f: Callable[..., Any]) -> Callable[..., Any]: tool_obj = Tool.from_callable( @@ -264,7 +264,7 @@ def get_toolset(self) -> AgentToolset[AgentDepsT] | None: """Return a ``FunctionToolset`` built from the wrapped tools. Each tool is converted to a pydantic-ai compatible callable via - :func:`~agentpool.tools.tool_wrapping.wrap_tool_for_pydantic_ai`, + :func:`~wolfharness.tools.tool_wrapping.wrap_tool_for_pydantic_ai`, then assembled into a ``FunctionToolset``. Returns ``None`` if the tool list is empty. @@ -272,7 +272,7 @@ def get_toolset(self) -> AgentToolset[AgentDepsT] | None: if not self._tools: return None - from agentpool.tools.tool_wrapping import wrap_tool_for_pydantic_ai + from wolfharness.tools.tool_wrapping import wrap_tool_for_pydantic_ai pa_tools = [wrap_tool_for_pydantic_ai(tool) for tool in self._tools] return FunctionToolset(pa_tools, id=self._name) diff --git a/src/agentpool/capabilities/loop_detection.py b/src/wolfharness/capabilities/loop_detection.py similarity index 98% rename from src/agentpool/capabilities/loop_detection.py rename to src/wolfharness/capabilities/loop_detection.py index bd635a8a3..4bb10481e 100644 --- a/src/agentpool/capabilities/loop_detection.py +++ b/src/wolfharness/capabilities/loop_detection.py @@ -26,7 +26,7 @@ # agent runs, so delegation depth survives the fresh copies that # ``for_run()`` creates for child agents. _delegation_depth: contextvars.ContextVar[int] = contextvars.ContextVar( - "agentpool_delegation_depth", + "wolfharness_delegation_depth", default=0, ) diff --git a/src/agentpool/capabilities/mcp_server_cap.py b/src/wolfharness/capabilities/mcp_server_cap.py similarity index 97% rename from src/agentpool/capabilities/mcp_server_cap.py rename to src/wolfharness/capabilities/mcp_server_cap.py index 6c33edfc1..b0e01cd2a 100644 --- a/src/agentpool/capabilities/mcp_server_cap.py +++ b/src/wolfharness/capabilities/mcp_server_cap.py @@ -1,6 +1,6 @@ """McpServerCap — wraps an MCP server as a pydantic-ai capability with Resource Protocols. -Replaces :class:`~agentpool.capabilities.mcp_capability.MCPCapability` with +Replaces :class:`~wolfharness.capabilities.mcp_capability.MCPCapability` with a clean implementation that delegates to ``MCPClient`` via ``SessionConnectionPool.get_client()`` using lazy initialization. @@ -21,8 +21,8 @@ from pydantic_ai.capabilities import AbstractCapability from pydantic_ai.tools import AgentDepsT, RunContext -from agentpool.capabilities.change_event import ChangeEvent -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.change_event import ChangeEvent +from wolfharness.capabilities.resource_protocols import ( BlobResourceContent, ChangeObservable, CommandEntry, @@ -48,9 +48,9 @@ from pydantic_ai.toolsets import AbstractToolset - from agentpool.mcp_server.client import MCPClient - from agentpool.mcp_server.session_pool import SessionConnectionPool - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness.mcp_server.session_pool import SessionConnectionPool + from wolfharness_config.mcp_server import MCPServerConfig logger = logging.getLogger(__name__) @@ -244,7 +244,7 @@ async def _build_toolset( return None from pydantic_ai.toolsets import CombinedToolset, FunctionToolset - from agentpool.tools.tool_wrapping import wrap_tool_for_pydantic_ai + from wolfharness.tools.tool_wrapping import wrap_tool_for_pydantic_ai converted = [client.convert_tool(t) for t in tools] pydantic_tools = [wrap_tool_for_pydantic_ai(tool) for tool in converted] diff --git a/src/agentpool/capabilities/memory.py b/src/wolfharness/capabilities/memory.py similarity index 100% rename from src/agentpool/capabilities/memory.py rename to src/wolfharness/capabilities/memory.py diff --git a/src/agentpool/capabilities/modality_filter.py b/src/wolfharness/capabilities/modality_filter.py similarity index 98% rename from src/agentpool/capabilities/modality_filter.py rename to src/wolfharness/capabilities/modality_filter.py index b455a8816..ac1bbabc6 100644 --- a/src/agentpool/capabilities/modality_filter.py +++ b/src/wolfharness/capabilities/modality_filter.py @@ -44,7 +44,7 @@ VideoUrl, ) -from agentpool.capabilities.modality_utils import ( +from wolfharness.capabilities.modality_utils import ( BinaryCategory, classify_binary_content, describe_multimodal_content, @@ -58,7 +58,7 @@ from pydantic_ai.models import ModelRequestContext from pydantic_ai.tools import ToolDefinition - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities logger = logging.getLogger(__name__) @@ -120,10 +120,10 @@ def get_ordering(self) -> CapabilityOrdering | None: modality-filtered BEFORE budget truncation and BEFORE DCP pruning. """ - from agentpool.capabilities.dcp.capability import ( + from wolfharness.capabilities.dcp.capability import ( DynamicContextPruningCapability, ) - from agentpool.capabilities.tool_output_budget import ( + from wolfharness.capabilities.tool_output_budget import ( ToolOutputBudgetCapability, ) diff --git a/src/agentpool/capabilities/modality_utils.py b/src/wolfharness/capabilities/modality_utils.py similarity index 100% rename from src/agentpool/capabilities/modality_utils.py rename to src/wolfharness/capabilities/modality_utils.py diff --git a/src/agentpool/capabilities/question.py b/src/wolfharness/capabilities/question.py similarity index 90% rename from src/agentpool/capabilities/question.py rename to src/wolfharness/capabilities/question.py index 167139c60..3fac9b177 100644 --- a/src/agentpool/capabilities/question.py +++ b/src/wolfharness/capabilities/question.py @@ -1,7 +1,7 @@ """QuestionCapability — user interaction question tool with YAML schema overrides. Provides the ``question`` tool backed by -``agentpool_toolsets.builtin.question_tools.QuestionTools``. +``wolfharness_toolsets.builtin.question_tools.QuestionTools``. Accepts optional YAML schema files to override the LLM-facing parameter description, mirroring the ``BackgroundTaskCapability`` pattern. @@ -25,11 +25,11 @@ ) from pydantic_ai.toolsets import AgentToolset, FunctionToolset -from agentpool.agents.context import AgentContext -from agentpool.tools.base import ToolResult # noqa: TC001 -from agentpool.utils.tool_schema import apply_params_schema, load_tool_schema -from agentpool_config.context import get_config_dir -from agentpool_toolsets.builtin.question_tools import QuestionTools +from wolfharness.agents.context import AgentContext +from wolfharness.tools.base import ToolResult # noqa: TC001 +from wolfharness.utils.tool_schema import apply_params_schema, load_tool_schema +from wolfharness_config.context import get_config_dir +from wolfharness_toolsets.builtin.question_tools import QuestionTools if TYPE_CHECKING: @@ -39,7 +39,7 @@ class QuestionCapability(AbstractCapability[AgentContext]): """Capability providing the user interaction ``question`` tool. - Wraps :class:`~agentpool_toolsets.builtin.question_tools.QuestionTools` + Wraps :class:`~wolfharness_toolsets.builtin.question_tools.QuestionTools` and applies optional YAML schema overrides for richer LLM-facing parameter descriptions. @@ -117,7 +117,7 @@ def _resolve_and_load_schema(schema_path_str: str) -> OpenAIFunctionDefinition: def get_toolset(self) -> AgentToolset[AgentContext] | None: """Return ``FunctionToolset`` with the enabled question tool. - The tool callable is sourced from the agentpool ``QuestionTools`` + The tool callable is sourced from the wolfharness ``QuestionTools`` instance, ensuring a single canonical implementation. YAML schema overrides are applied on top for richer LLM-facing descriptions. """ @@ -147,7 +147,7 @@ def get_ordering(self) -> CapabilityOrdering | None: return CapabilityOrdering(wrapped_by=[ProcessHistory, NativeTool]) # ---- Tool wrapper ---- - # This wraps the canonical tool function from agentpool's QuestionTools, + # This wraps the canonical tool function from wolfharness's QuestionTools, # adapting RunContext[AgentContext] → AgentContext for direct invocation. @logfire.instrument("question.capability.question") diff --git a/src/agentpool/capabilities/registry.py b/src/wolfharness/capabilities/registry.py similarity index 93% rename from src/agentpool/capabilities/registry.py rename to src/wolfharness/capabilities/registry.py index 57da0a6cb..7ea73fde3 100644 --- a/src/agentpool/capabilities/registry.py +++ b/src/wolfharness/capabilities/registry.py @@ -1,12 +1,12 @@ """Entry-point based capability discovery. Provides ``discover_entry_point_capabilities()`` which loads all -capability classes registered via the ``agentpool.capabilities`` entry-point +capability classes registered via the ``wolfharness.capabilities`` entry-point group. External packages can register new capability types by adding an entry to their ``pyproject.toml``: ```toml -[project.entry-points."agentpool.capabilities"] +[project.entry-points."wolfharness.capabilities"] my_cap = "my_package.my_module:MyCapability" ``` @@ -29,7 +29,7 @@ from pydantic_ai.capabilities import AbstractCapability -_ENTRY_POINT_GROUP = "agentpool.capabilities" +_ENTRY_POINT_GROUP = "wolfharness.capabilities" class CapabilityNotFoundError(Exception): @@ -64,7 +64,7 @@ def __init__( def discover_entry_point_capabilities() -> dict[str, type[AbstractCapability[object]]]: """Discover capability classes registered via entry points. - Loads all entry points in the ``agentpool.capabilities`` group and + Loads all entry points in the ``wolfharness.capabilities`` group and returns a mapping from entry-point name to the loaded capability class. Each loaded value must be a subclass of :class:`~pydantic_ai.capabilities.AbstractCapability`. diff --git a/src/agentpool/capabilities/resource_capability.py b/src/wolfharness/capabilities/resource_capability.py similarity index 97% rename from src/agentpool/capabilities/resource_capability.py rename to src/wolfharness/capabilities/resource_capability.py index 74ee4d242..8a3ea0f56 100644 --- a/src/agentpool/capabilities/resource_capability.py +++ b/src/wolfharness/capabilities/resource_capability.py @@ -25,15 +25,15 @@ from pydantic_ai.tools import AgentDepsT, RunContext from pydantic_ai.toolsets import AgentToolset, FunctionToolset -from agentpool.capabilities.extension_registry import Scope, ScopeLevel -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.extension_registry import Scope, ScopeLevel +from wolfharness.capabilities.resource_protocols import ( CompletionArgument, CompletionResult, ) if TYPE_CHECKING: - from agentpool.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.agent_context import AgentContextDeps # Number of header lines (header + separator) before data rows. @@ -138,7 +138,7 @@ def _resolve_agent_context(ctx: RunContext[AgentDepsT]) -> AgentContextDeps: Raises: RuntimeError: If deps is None or AgentContextDeps is not found. """ - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps return resolve_agent_context_from_deps(ctx.deps, capability_name="ResourceCapability") @@ -274,7 +274,7 @@ async def read_resource( ctx: The run context providing agent dependencies. uri: Resource URI to read. """ - from agentpool.capabilities.resource_resolver import resolve_resource_content + from wolfharness.capabilities.resource_resolver import resolve_resource_content agent_ctx = self._resolve_agent_context(ctx) registry = agent_ctx.extension_registry @@ -320,7 +320,7 @@ async def resource_exists( scope = self._make_scope(agent_ctx) if uri.startswith("skill://"): - from agentpool.skills.uri_resolver import ResolvedSkillURI + from wolfharness.skills.uri_resolver import ResolvedSkillURI resolved = ResolvedSkillURI.parse(uri) skill_name = resolved.skill_name @@ -332,7 +332,7 @@ async def resource_exists( resolved.reference_path is not None and resolved.reference_path.upper() != "SKILL.MD" ): - from agentpool.capabilities.resource_resolver import _resolve_skill_reference + from wolfharness.capabilities.resource_resolver import _resolve_skill_reference try: ref_content = await _resolve_skill_reference( diff --git a/src/agentpool/capabilities/resource_protocols.py b/src/wolfharness/capabilities/resource_protocols.py similarity index 98% rename from src/agentpool/capabilities/resource_protocols.py rename to src/wolfharness/capabilities/resource_protocols.py index 72ac0c5db..869ad8c82 100644 --- a/src/agentpool/capabilities/resource_protocols.py +++ b/src/wolfharness/capabilities/resource_protocols.py @@ -9,7 +9,7 @@ ``McpResource`` remains as a ``DeprecatedAlias`` that emits ``DeprecationWarning`` on ``isinstance()`` checks. -ChangeEvent is imported from ``agentpool.capabilities.change_event`` — +ChangeEvent is imported from ``wolfharness.capabilities.change_event`` — this module does NOT define a duplicate. """ @@ -26,7 +26,7 @@ from upath import UPath - from agentpool.capabilities.change_event import ChangeEvent + from wolfharness.capabilities.change_event import ChangeEvent # ---- Dataclasses ---- @@ -409,7 +409,7 @@ class McpResource(metaclass=_McpResourceMeta): ``ResourceAccess`` checks. Note: - ``agentpool_server.opencode_server.models.mcp.McpResource`` (Pydantic + ``wolfharness_server.opencode_server.models.mcp.McpResource`` (Pydantic model) is unrelated to this deprecated Protocol and is NOT affected. """ diff --git a/src/agentpool/capabilities/resource_resolver.py b/src/wolfharness/capabilities/resource_resolver.py similarity index 96% rename from src/agentpool/capabilities/resource_resolver.py rename to src/wolfharness/capabilities/resource_resolver.py index 59c588dcb..84acc4a53 100644 --- a/src/agentpool/capabilities/resource_resolver.py +++ b/src/wolfharness/capabilities/resource_resolver.py @@ -12,17 +12,17 @@ import logfire from pydantic_ai import BinaryContent -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.resource_protocols import ( BlobResourceContent, TextResourceContent, ) -from agentpool.skills.uri_resolver import ResolvedSkillURI, _name_alternatives +from wolfharness.skills.uri_resolver import ResolvedSkillURI, _name_alternatives if TYPE_CHECKING: from pydantic_ai.messages import UserContent - from agentpool.capabilities.resource_protocols import ResourceAccess, SkillResource + from wolfharness.capabilities.resource_protocols import ResourceAccess, SkillResource def _truncate_text(text: str, max_chars: int) -> str: @@ -78,7 +78,7 @@ async def _resolve_skill_reference( """ from upathtools import UPath - from agentpool.skills.exceptions import SecurityError + from wolfharness.skills.exceptions import SecurityError # Defense-in-depth: ResolvedSkillURI.parse() already rejects ".." segments, # but we double-check here in case this helper is called directly. diff --git a/src/agentpool/capabilities/runloop_delegation.py b/src/wolfharness/capabilities/runloop_delegation.py similarity index 93% rename from src/agentpool/capabilities/runloop_delegation.py rename to src/wolfharness/capabilities/runloop_delegation.py index 277b71713..0fbfda90e 100644 --- a/src/agentpool/capabilities/runloop_delegation.py +++ b/src/wolfharness/capabilities/runloop_delegation.py @@ -18,14 +18,14 @@ from typing import TYPE_CHECKING, Any import warnings -from agentpool.observability.spans import safe_span +from wolfharness.observability.spans import safe_span if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.host.context import HostContext - from agentpool.host.registry import AgentRegistry + from wolfharness.host.context import HostContext + from wolfharness.host.registry import AgentRegistry class RunLoopDelegationService: @@ -90,7 +90,7 @@ async def spawn_subagent( DeprecationWarning, stacklevel=2, ) - from agentpool.capabilities.delegation import AgentNotFoundError + from wolfharness.capabilities.delegation import AgentNotFoundError if not self._registry.exists(name): raise AgentNotFoundError(name) @@ -102,7 +102,7 @@ async def spawn_subagent( child_session_id = f"{self._session_id}::child::{name}" - from agentpool.agents.events import RunErrorEvent, StreamCompleteEvent + from wolfharness.agents.events import RunErrorEvent, StreamCompleteEvent with safe_span( "delegation.subagent", @@ -134,7 +134,7 @@ async def spawn_subagent( if isinstance(event, StreamCompleteEvent | RunErrorEvent): break except TimeoutError: - from agentpool.agents.events import RunErrorEvent + from wolfharness.agents.events import RunErrorEvent yield RunErrorEvent( message=f"Subagent '{name}' timed out after 300s", diff --git a/src/agentpool/capabilities/skill_manager_cap.py b/src/wolfharness/capabilities/skill_manager_cap.py similarity index 96% rename from src/agentpool/capabilities/skill_manager_cap.py rename to src/wolfharness/capabilities/skill_manager_cap.py index 767deab13..4ba236a12 100644 --- a/src/agentpool/capabilities/skill_manager_cap.py +++ b/src/wolfharness/capabilities/skill_manager_cap.py @@ -1,23 +1,24 @@ """SkillManagerCap — unified skill management as a pydantic-ai capability. Replaces the deleted per-skill ``SkillCapability`` class and -:class:`~agentpool.capabilities.skill_activation.SkillActivationCapability` +:class:`~wolfharness.capabilities.skill_activation.SkillActivationCapability` with a single capability that: - Holds local skills as ``dict[str, Skill]`` (no per-skill capability wrappers). -- Queries child :class:`~agentpool.capabilities.mcp_server_cap.McpServerCap` +- Queries child :class:`~wolfharness.capabilities.mcp_server_cap.McpServerCap` instances for remote skills and commands. - Provides metadata-only instructions by default (```` XML). - Supports optional ``matcher_fn`` for dynamic per-turn skill injection. - Supports ``always_active`` flag for skills that bypass the matcher. - Aggregates ``SkillResource`` and ``CommandResource`` from local + remote. -- Imports per-skill Python tools via :class:`~agentpool.skills.skill_tool_manager.SkillToolManager`. -- Creates per-skill :class:`~agentpool.capabilities.mcp_server_cap.McpServerCap` instances. +- Imports per-skill Python tools via + :class:`~wolfharness.skills.skill_tool_manager.SkillToolManager`. +- Creates per-skill :class:`~wolfharness.capabilities.mcp_server_cap.McpServerCap` instances. - **Owns** the ``load_skill`` and ``list_skills`` agent-facing tools (D1). - Applies ``allowed_tools`` filtering via ``get_wrapper_toolset()``. - Guards against built-in tool name collisions (D10). - Inherits change stream merging and lifecycle from - :class:`~agentpool.capabilities.combined_toolset.CombinedToolsetCapability`. + :class:`~wolfharness.capabilities.combined_toolset.CombinedToolsetCapability`. """ from __future__ import annotations @@ -38,18 +39,18 @@ PrefixedToolset, ) -from agentpool.capabilities.combined_toolset import CombinedToolsetCapability -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.combined_toolset import CombinedToolsetCapability +from wolfharness.capabilities.resource_protocols import ( ChangeObservable, CommandEntry, CommandResource, SkillEntry, SkillResource, ) -from agentpool.log import get_logger -from agentpool.skills.skill import Skill -from agentpool.skills.skill_tool_manager import SkillToolManager -from agentpool.skills.uri_resolver import ResolvedSkillURI, _name_alternatives +from wolfharness.log import get_logger +from wolfharness.skills.skill import Skill +from wolfharness.skills.skill_tool_manager import SkillToolManager +from wolfharness.skills.uri_resolver import ResolvedSkillURI, _name_alternatives if TYPE_CHECKING: @@ -59,18 +60,18 @@ from pydantic_ai.capabilities import AbstractCapability from pydantic_ai.capabilities.abstract import AgentInstructions # type: ignore[attr-defined] - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool.capabilities.mcp_server_cap import McpServerCap - from agentpool.delegation.pool import AgentPool - from agentpool.skills.uri_resolver import SkillURIResolver - from agentpool.tools.base import Tool + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.mcp_server_cap import McpServerCap + from wolfharness.delegation.pool import AgentPool + from wolfharness.skills.uri_resolver import SkillURIResolver + from wolfharness.tools.base import Tool logger = get_logger(__name__) # --------------------------------------------------------------------------- -# Helper functions (moved from agentpool_toolsets/builtin/skills.py) +# Helper functions (moved from wolfharness_toolsets/builtin/skills.py) # --------------------------------------------------------------------------- @@ -120,7 +121,7 @@ async def _load_reference_content( ReferenceNotFoundError: If the reference file cannot be loaded. SecurityError: If the reference path is a path traversal attempt. """ - from agentpool.skills.exceptions import ReferenceNotFoundError + from wolfharness.skills.exceptions import ReferenceNotFoundError # For virtual paths (PurePosixPath like skill:// URIs), route through # the cap's children (SkillResource providers) for remote reference reads. @@ -146,7 +147,7 @@ async def _load_reference_content( skill_path = cast(UPath, skill.skill_path) # Validate reference_path to prevent path traversal attacks - from agentpool.skills.exceptions import SecurityError + from wolfharness.skills.exceptions import SecurityError decoded_path = reference_path # Check for path traversal attempts and absolute paths @@ -347,7 +348,7 @@ def _resolve_agent_context(ctx: RunContext[AgentDepsT]) -> AgentContextDeps: Raises: RuntimeError: If deps is None or AgentContextDeps is not found. """ - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps return resolve_agent_context_from_deps(ctx.deps, capability_name="SkillManagerCap") @@ -371,7 +372,7 @@ def _resolve_pool(ctx: RunContext[AgentDepsT]) -> tuple[AgentPool[Any] | None, s A tuple of (``AgentPool`` if available else ``None``, node name if available else ``None``). """ - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext deps = ctx.deps if isinstance(deps, RuntimeAgentContext): @@ -412,7 +413,7 @@ def _create_skill_mcp_children(self) -> None: ``to_mcp_server_config()``, creates a ``McpServerCap``, and stores it in ``_skill_mcp_children[skill_name]``. """ - from agentpool.capabilities.mcp_server_cap import McpServerCap + from wolfharness.capabilities.mcp_server_cap import McpServerCap for name, skill in self._local_skills.items(): if not skill.mcp_servers: diff --git a/src/agentpool/capabilities/skill_watcher.py b/src/wolfharness/capabilities/skill_watcher.py similarity index 97% rename from src/agentpool/capabilities/skill_watcher.py rename to src/wolfharness/capabilities/skill_watcher.py index 3e729ed0d..660472972 100644 --- a/src/agentpool/capabilities/skill_watcher.py +++ b/src/wolfharness/capabilities/skill_watcher.py @@ -18,10 +18,10 @@ from collections.abc import AsyncIterator from pathlib import Path - from agentpool.capabilities.change_event import ChangeEvent + from wolfharness.capabilities.change_event import ChangeEvent -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) @@ -171,7 +171,7 @@ async def _fire_after_debounce(self) -> None: break await asyncio.sleep(remaining) - from agentpool.capabilities.change_event import ChangeEvent + from wolfharness.capabilities.change_event import ChangeEvent await self._change_queue.put( ChangeEvent( diff --git a/src/agentpool/capabilities/subagent_capability.py b/src/wolfharness/capabilities/subagent_capability.py similarity index 94% rename from src/agentpool/capabilities/subagent_capability.py rename to src/wolfharness/capabilities/subagent_capability.py index 55aeebdc0..e8a9a988b 100644 --- a/src/agentpool/capabilities/subagent_capability.py +++ b/src/wolfharness/capabilities/subagent_capability.py @@ -21,12 +21,12 @@ from pydantic_ai.tools import AgentDepsT, RunContext from pydantic_ai.toolsets import AgentToolset, FunctionToolset -from agentpool.capabilities.delegation import AgentNotFoundError, DelegationService -from agentpool.observability.spans import safe_span +from wolfharness.capabilities.delegation import AgentNotFoundError, DelegationService +from wolfharness.observability.spans import safe_span if TYPE_CHECKING: - from agentpool.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.agent_context import AgentContextDeps class SubagentCapability(AbstractCapability[AgentDepsT]): @@ -110,7 +110,7 @@ async def spawn_subagent( parent_session_id=agent_ctx.session.session_id, child_agent_name=name, ): - from agentpool.observability.trace import get_trace_id + from wolfharness.observability.trace import get_trace_id logfire.info( "Delegating to subagent", @@ -166,7 +166,7 @@ def _resolve_agent_context(ctx: RunContext[AgentDepsT]) -> AgentContextDeps: Raises: RuntimeError: If deps is None or AgentContextDeps is not found. """ - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps return resolve_agent_context_from_deps(ctx.deps, capability_name="SubagentCapability") diff --git a/src/agentpool/capabilities/team_comm_capability.py b/src/wolfharness/capabilities/team_comm_capability.py similarity index 98% rename from src/agentpool/capabilities/team_comm_capability.py rename to src/wolfharness/capabilities/team_comm_capability.py index e5b5c3edf..a1f2d848f 100644 --- a/src/agentpool/capabilities/team_comm_capability.py +++ b/src/wolfharness/capabilities/team_comm_capability.py @@ -63,8 +63,8 @@ from pydantic_ai.messages import ToolReturn from pydantic_ai.tools import RunContext # noqa: TC002 - needed at runtime for get_type_hints() -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -73,11 +73,11 @@ from pydantic_ai.run import AgentRunResult from pydantic_ai.tools import ToolDefinition - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool.capabilities.file_team_state import FileTeamState - from agentpool.lifecycle.types import DeliveryMode - from agentpool.tools.base import Tool - from agentpool_config.team_mode import TeamModeConfig + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.file_team_state import FileTeamState + from wolfharness.lifecycle.types import DeliveryMode + from wolfharness.tools.base import Tool + from wolfharness_config.team_mode import TeamModeConfig logger = get_logger(__name__) @@ -155,7 +155,7 @@ def __init__( @property def _notice_mode(self) -> DeliveryMode: """Resolve delivery mode from config.""" - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode return ( DeliveryMode.STEER @@ -239,7 +239,7 @@ def _resolve_agent_context(self, ctx: RunContext[Any]) -> AgentContextDeps: Raises: RuntimeError: If ``ctx.deps`` is None or AgentContextDeps is not found. """ - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps return resolve_agent_context_from_deps(ctx.deps, capability_name="TeamCommCapability") @@ -253,7 +253,7 @@ def _get_team_state(self, agent_ctx: AgentContextDeps) -> FileTeamState | None: A FileTeamState rooted at the configured base_dir, or None if no ``team_id`` is present in session metadata. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_id: str | None = agent_ctx.session.metadata.get("team_id") if team_id is None: @@ -305,7 +305,7 @@ async def _create_member_session( Raises: RuntimeError: If SessionPool is not available. """ - from agentpool.agents.events.events import SpawnSessionStart + from wolfharness.agents.events.events import SpawnSessionStart session_pool = agent_ctx.host.session_pool if session_pool is None: @@ -502,7 +502,7 @@ async def send_message( # noqa: PLR0911, PLR0915 if session_pool is None: return ToolReturn(return_value="SessionPool not available") - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState state_path = team_state._state_path(team_id) if not state_path.exists(): @@ -510,7 +510,7 @@ async def send_message( # noqa: PLR0911, PLR0915 state: dict[str, Any] = FileTeamState._read_json(state_path) members: dict[str, dict[str, str]] = state.get("members", {}) - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode mode = self._notice_mode delivered = 0 @@ -564,7 +564,7 @@ async def send_message( # noqa: PLR0911, PLR0915 return ToolReturn(return_value=f"Member '{to}' not found or no session registered") # Bounds: max_member_turns and inbox_max_bytes checks. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState state_path = team_state._state_path(team_id) if state_path.exists(): @@ -596,7 +596,7 @@ async def send_message( # noqa: PLR0911, PLR0915 current_state["members"] = members_state FileTeamState._atomic_write(state_path, current_state) - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode mode = self._notice_mode msg_body = ( @@ -832,7 +832,7 @@ async def task_list( # noqa: PLR0911 Returns: XML task list with owner summary, or error string. """ - from agentpool.capabilities.file_team_state import ( + from wolfharness.capabilities.file_team_state import ( TaskRecord, format_owner_summary, ) @@ -1162,7 +1162,7 @@ async def task_update( # noqa: PLR0911, PLR0915 # --- Handoff notification (tasks 20-26) --- if handoff_to is not None and status == "completed": # Look up the member in team state. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState handoff_state_path = team_state._state_path(team_id) if handoff_state_path.exists(): @@ -1568,7 +1568,7 @@ async def team_status( # noqa: PLR0915 if team_state is None: return ToolReturn(return_value="Not in a team session") - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState state_path = team_state._state_path(team_id) if not state_path.exists(): @@ -1734,7 +1734,7 @@ async def team_create( # noqa: PLR0911, PLR0915 team_id = f"team_{uuid.uuid4().hex[:8]}" lead_session_id: str = agent_ctx.session.session_id - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState base_dir = ( agent_ctx.team_mode_config.effective_base_dir @@ -1766,7 +1766,7 @@ async def team_create( # noqa: PLR0911, PLR0915 if session_pool is None: return ToolReturn(return_value="SessionPool not available") - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode created_sessions: list[str] = [] try: @@ -2013,7 +2013,7 @@ async def team_delete(self, ctx: RunContext[Any]) -> ToolReturn: if team_state is None: return ToolReturn(return_value="Not in a team session") - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState state_path = team_state._state_path(team_id) if not state_path.exists(): @@ -2137,7 +2137,7 @@ async def team_add_member( # noqa: PLR0911, PLR0915 if team_state is None: return ToolReturn(return_value="Not in a team session") - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState # Check name not already in team state members. state_path = team_state._state_path(team_id) @@ -2200,7 +2200,7 @@ async def team_add_member( # noqa: PLR0911, PLR0915 ) # Send initial prompt to member (with existing member roster). - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode team_name: str = state.get("team_name", "unknown") base_prompt = prompt or self._config.protocol_template.format( @@ -2351,7 +2351,7 @@ async def shutdown_request( # noqa: PLR0911 if team_state is None: return ToolReturn(return_value="Not in a team session") - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState member_sid = team_state.get_member_session_id(team_id, member_name) if member_sid is None: @@ -2436,7 +2436,7 @@ def _schedule_ephemeral_cleanup( member_name: Member name for state cleanup. base_dir: Base directory for FileTeamState. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState async def _wait_and_cleanup() -> None: try: @@ -2629,7 +2629,7 @@ async def after_run( # noqa: PLR0911 session_pool = agent_ctx.host.session_pool if session_pool is not None: - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode # Use QUEUE mode: the run is ending, so STEER would be lost. await session_pool.send_message( diff --git a/src/agentpool/capabilities/token_budget.py b/src/wolfharness/capabilities/token_budget.py similarity index 100% rename from src/agentpool/capabilities/token_budget.py rename to src/wolfharness/capabilities/token_budget.py diff --git a/src/agentpool/capabilities/tool_display_capability.py b/src/wolfharness/capabilities/tool_display_capability.py similarity index 95% rename from src/agentpool/capabilities/tool_display_capability.py rename to src/wolfharness/capabilities/tool_display_capability.py index 5c8e02651..142db7549 100644 --- a/src/agentpool/capabilities/tool_display_capability.py +++ b/src/wolfharness/capabilities/tool_display_capability.py @@ -8,7 +8,7 @@ protocol clients (e.g. the OpenCode TUI) that dispatch on a whitelist of standard tool names render the tools properly. - **Rich-info layer** (``emit_diff``): injects a - :class:`~agentpool.agents.events.DiffContentItem` progress event after + :class:`~wolfharness.agents.events.DiffContentItem` progress event after a matching tool executes, so ACP clients (e.g. Zed) render a file diff. @@ -19,7 +19,7 @@ ``DiffContentItem`` uses ``rename_mode=True + emit_diff=False`` (rename only, no duplicate diff). -Modeled on :class:`~agentpool.agents.native_agent.tool_intercept.ToolInterceptCapability` +Modeled on :class:`~wolfharness.agents.native_agent.tool_intercept.ToolInterceptCapability` — a standalone ``AbstractCapability`` overriding ``get_wrapper_toolset`` and ``wrap_tool_execute`` as a global middleware over all assembled tools. @@ -34,7 +34,7 @@ from pydantic_ai.capabilities import AbstractCapability from pydantic_ai.toolsets import AbstractToolset, RenamedToolset -from agentpool.agents.events import DiffContentItem +from wolfharness.agents.events import DiffContentItem if TYPE_CHECKING: @@ -161,8 +161,8 @@ async def wrap_tool_execute( After ``handler`` completes, derives ``(path, old_text, new_text)`` from the call arguments and emits a - :class:`~agentpool.agents.events.ToolCallProgressEvent` carrying a - :class:`~agentpool.agents.events.DiffContentItem` via the run + :class:`~wolfharness.agents.events.ToolCallProgressEvent` carrying a + :class:`~wolfharness.agents.events.DiffContentItem` via the run context's ``events`` emitter — the same channel fsspec tools use, which reaches ACP converters as ``FileEditToolCallContent``. @@ -179,7 +179,7 @@ async def wrap_tool_execute( ``name_map`` before matching. Args: - ctx: The pydantic-ai run context (carries ``deps`` → agentpool + ctx: The pydantic-ai run context (carries ``deps`` → wolfharness ``AgentContext`` with the ``events`` emitter). call: The tool call part. tool_def: The tool definition. diff --git a/src/agentpool/capabilities/tool_output_budget.py b/src/wolfharness/capabilities/tool_output_budget.py similarity index 100% rename from src/agentpool/capabilities/tool_output_budget.py rename to src/wolfharness/capabilities/tool_output_budget.py diff --git a/src/agentpool/capabilities/viking/__init__.py b/src/wolfharness/capabilities/viking/__init__.py similarity index 97% rename from src/agentpool/capabilities/viking/__init__.py rename to src/wolfharness/capabilities/viking/__init__.py index 772c1df51..f8974d5db 100644 --- a/src/agentpool/capabilities/viking/__init__.py +++ b/src/wolfharness/capabilities/viking/__init__.py @@ -11,7 +11,7 @@ remote skill discovery and reading from the Viking server. Configuration is via ``VikingCapabilityConfig`` in -``agentpool_config.capabilities``. +``wolfharness_config.capabilities``. """ from __future__ import annotations @@ -26,8 +26,8 @@ from pydantic_ai.capabilities import AbstractCapability from pydantic_ai.toolsets import AgentToolset, FunctionToolset -from agentpool.capabilities.viking.identity import VikingIdentity, _try_decode_api_key -from agentpool.log import get_logger +from wolfharness.capabilities.viking.identity import VikingIdentity, _try_decode_api_key +from wolfharness.log import get_logger logger = get_logger(__name__) @@ -41,14 +41,14 @@ from pydantic_ai.messages import BinaryContent from pydantic_ai.models import ModelRequestContext - from agentpool.capabilities.change_event import ChangeEvent - from agentpool.capabilities.resource_protocols import ( + from wolfharness.capabilities.change_event import ChangeEvent + from wolfharness.capabilities.resource_protocols import ( BlobResourceContent, ResourceEntry, SkillEntry, TextResourceContent, ) - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities @dataclass @@ -189,7 +189,7 @@ class VikingCapability(AbstractCapability[Any]): """Ingestion mode — ``"async"`` (fire-and-forget) or ``"sync"``.""" auto_ingest_sanitize: bool = True """Strip injected Viking XML blocks from messages before ingestion.""" - auto_ingest_source_type: str = "agentpool" + auto_ingest_source_type: str = "wolfharness" """Source type metadata for ingested sessions.""" auto_ingest_keep_recent_turns: int = 0 """Number of recent turns to retain after commit. 0 = no retention.""" @@ -446,7 +446,7 @@ def get_instructions(self) -> str | None: Returns: The instruction string from ``instructions.py``. """ - from agentpool.capabilities.viking.instructions import _VIKING_INSTRUCTIONS + from wolfharness.capabilities.viking.instructions import _VIKING_INSTRUCTIONS return _VIKING_INSTRUCTIONS @@ -459,7 +459,7 @@ def get_toolset(self) -> AgentToolset[Any] | None: A ``FunctionToolset`` with the mode-appropriate tools, or ``None`` if the tool list is empty. """ - from agentpool.capabilities.viking.tools import build_tools + from wolfharness.capabilities.viking.tools import build_tools tool_fns = build_tools(self) if not tool_fns: @@ -477,8 +477,8 @@ async def get_tools(self) -> Sequence[Any]: Returns: A list of ``FunctionTool`` objects wrapping the tool closures. """ - from agentpool.capabilities.viking.tools import build_tools - from agentpool.tools.base import FunctionTool + from wolfharness.capabilities.viking.tools import build_tools + from wolfharness.tools.base import FunctionTool tool_fns = build_tools(self) return [FunctionTool.from_callable(fn) for fn in tool_fns] @@ -505,7 +505,7 @@ async def list_skills(self) -> list[SkillEntry]: if not isinstance(entries, list): return [] - from agentpool.capabilities.resource_protocols import SkillEntry + from wolfharness.capabilities.resource_protocols import SkillEntry skills: list[SkillEntry] = [] for entry in entries: @@ -650,7 +650,7 @@ async def _handle_compaction( from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.compaction import ( + from wolfharness.capabilities.viking.compaction import ( _estimate_tokens, _replace_old_messages, _serialize_messages, @@ -741,7 +741,7 @@ async def _list_resource_entries_from_uri(self, client: Any, uri: str) -> list[R Returns: A list of ``ResourceEntry`` descriptors for text files. """ - from agentpool.capabilities.resource_protocols import ResourceEntry + from wolfharness.capabilities.resource_protocols import ResourceEntry top_entries = await client.ls(uri) if not isinstance(top_entries, list): @@ -875,7 +875,7 @@ async def read_resource( if not content: return None - from agentpool.capabilities.resource_protocols import ( + from wolfharness.capabilities.resource_protocols import ( TextResourceContent, ) @@ -965,7 +965,7 @@ async def _handle_profile_inject( try: client = await self._ensure_client() - from agentpool.capabilities.viking.profile import ( + from wolfharness.capabilities.viking.profile import ( _derive_context_hint, _format_profile_block, ) @@ -1244,7 +1244,7 @@ async def _handle_auto_recall( or the original context if recall is disabled, no user prompt is found, or an error occurs. """ - from agentpool.capabilities.viking.recall import ( + from wolfharness.capabilities.viking.recall import ( _extract_latest_user_prompt, _format_recall_block, _inject_system_message, @@ -1268,7 +1268,7 @@ async def _handle_auto_recall( memories_uri = self._resolve_memories_uri() # Extract session_id from ctx.deps if available - from agentpool.capabilities.viking.tools import _get_session_id + from wolfharness.capabilities.viking.tools import _get_session_id session_id = _get_session_id(ctx) @@ -1360,7 +1360,7 @@ async def _handle_auto_ingest( if not self.auto_ingest_enabled: return request_context - from agentpool.capabilities.viking.ingest import ( + from wolfharness.capabilities.viking.ingest import ( _extract_conversation_pairs, _ingest_conversation, _sanitize_message, diff --git a/src/agentpool/capabilities/viking/compaction.py b/src/wolfharness/capabilities/viking/compaction.py similarity index 100% rename from src/agentpool/capabilities/viking/compaction.py rename to src/wolfharness/capabilities/viking/compaction.py diff --git a/src/agentpool/capabilities/viking/identity.py b/src/wolfharness/capabilities/viking/identity.py similarity index 100% rename from src/agentpool/capabilities/viking/identity.py rename to src/wolfharness/capabilities/viking/identity.py diff --git a/src/agentpool/capabilities/viking/ingest.py b/src/wolfharness/capabilities/viking/ingest.py similarity index 99% rename from src/agentpool/capabilities/viking/ingest.py rename to src/wolfharness/capabilities/viking/ingest.py index d404bd4cc..f5cc526f5 100644 --- a/src/agentpool/capabilities/viking/ingest.py +++ b/src/wolfharness/capabilities/viking/ingest.py @@ -90,7 +90,7 @@ async def _ingest_conversation( messages: list[dict[str, str]], *, session_id: str, - source_type: str = "agentpool", + source_type: str = "wolfharness", keep_recent_turns: int = 0, ) -> None: """Write conversation messages to a Viking session. diff --git a/src/agentpool/capabilities/viking/instructions.py b/src/wolfharness/capabilities/viking/instructions.py similarity index 100% rename from src/agentpool/capabilities/viking/instructions.py rename to src/wolfharness/capabilities/viking/instructions.py diff --git a/src/agentpool/capabilities/viking/profile.py b/src/wolfharness/capabilities/viking/profile.py similarity index 99% rename from src/agentpool/capabilities/viking/profile.py rename to src/wolfharness/capabilities/viking/profile.py index df0b47d80..1cea15441 100644 --- a/src/agentpool/capabilities/viking/profile.py +++ b/src/wolfharness/capabilities/viking/profile.py @@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any -from agentpool.capabilities.viking.utils import truncate_text +from wolfharness.capabilities.viking.utils import truncate_text _SNIPPET_LIMIT = 500 diff --git a/src/agentpool/capabilities/viking/recall.py b/src/wolfharness/capabilities/viking/recall.py similarity index 99% rename from src/agentpool/capabilities/viking/recall.py rename to src/wolfharness/capabilities/viking/recall.py index 1fb798f51..8e865e3a6 100644 --- a/src/agentpool/capabilities/viking/recall.py +++ b/src/wolfharness/capabilities/viking/recall.py @@ -11,7 +11,7 @@ import hashlib from typing import TYPE_CHECKING, Any -from agentpool.capabilities.viking.utils import truncate_text +from wolfharness.capabilities.viking.utils import truncate_text if TYPE_CHECKING: diff --git a/src/agentpool/capabilities/viking/tools.py b/src/wolfharness/capabilities/viking/tools.py similarity index 99% rename from src/agentpool/capabilities/viking/tools.py rename to src/wolfharness/capabilities/viking/tools.py index b22e0cfc7..e4dbcbfb5 100644 --- a/src/agentpool/capabilities/viking/tools.py +++ b/src/wolfharness/capabilities/viking/tools.py @@ -15,7 +15,7 @@ from pydantic_ai.messages import ToolReturn from pydantic_ai.tools import RunContext # noqa: TC002 - needed at runtime for get_type_hints() -from agentpool.capabilities.viking.utils import ( +from wolfharness.capabilities.viking.utils import ( add_line_numbers, format_glob_results, format_grep_results, @@ -28,7 +28,7 @@ if TYPE_CHECKING: from collections.abc import Awaitable, Callable - from agentpool.capabilities.viking import VikingCapability + from wolfharness.capabilities.viking import VikingCapability def _get_session_id(ctx: RunContext[Any]) -> str | None: diff --git a/src/agentpool/capabilities/viking/utils.py b/src/wolfharness/capabilities/viking/utils.py similarity index 100% rename from src/agentpool/capabilities/viking/utils.py rename to src/wolfharness/capabilities/viking/utils.py diff --git a/src/agentpool/commands/__init__.py b/src/wolfharness/commands/__init__.py similarity index 69% rename from src/agentpool/commands/__init__.py rename to src/wolfharness/commands/__init__.py index 2ab198254..a86327277 100644 --- a/src/agentpool/commands/__init__.py +++ b/src/wolfharness/commands/__init__.py @@ -1,12 +1,12 @@ """Core command base classes. This module provides the ``NodeCommand`` and ``AgentCommand`` base classes -that were previously in ``agentpool_commands.base``. +that were previously in ``wolfharness_commands.base``. They live in the core layer so that protocol servers can import them without creating a server→cli dependency. """ -from agentpool.commands.base import AgentCommand, NodeCommand +from wolfharness.commands.base import AgentCommand, NodeCommand __all__ = ["AgentCommand", "NodeCommand"] diff --git a/src/agentpool/commands/base.py b/src/wolfharness/commands/base.py similarity index 91% rename from src/agentpool/commands/base.py rename to src/wolfharness/commands/base.py index 4a729ebae..39a69a922 100644 --- a/src/agentpool/commands/base.py +++ b/src/wolfharness/commands/base.py @@ -9,7 +9,7 @@ if TYPE_CHECKING: - from agentpool.messaging import MessageNode + from wolfharness.messaging import MessageNode class NodeCommand(SlashedCommand, ABC): @@ -33,6 +33,6 @@ class AgentCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: """Check if node is an Agent.""" - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) diff --git a/src/agentpool/common_types.py b/src/wolfharness/common_types.py similarity index 97% rename from src/agentpool/common_types.py rename to src/wolfharness/common_types.py index ef5a0fa31..d4c5d417f 100644 --- a/src/agentpool/common_types.py +++ b/src/wolfharness/common_types.py @@ -27,9 +27,9 @@ from fsspec.asyn import AsyncFileSystem - from agentpool.agents.context import AgentContext - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.tools.base import Tool + from wolfharness.agents.context import AgentContext + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.tools.base import Tool type AnyTransformFn[T] = Callable[[T], T | Awaitable[T]] type OptionalAwaitable[T] = T | Awaitable[T] @@ -287,7 +287,7 @@ def _validate_type_args(data: Any, args: tuple[Any, ...]) -> None: # if __name__ == "__main__": -# from agentpool.models.manifest import AgentsManifest +# from wolfharness.models.manifest import AgentsManifest # AgentsManifestCode = ConfigCode.for_config( # AgentsManifest, diff --git a/src/agentpool/config_resources/__init__.py b/src/wolfharness/config_resources/__init__.py similarity index 94% rename from src/agentpool/config_resources/__init__.py rename to src/wolfharness/config_resources/__init__.py index befa99e31..50894b85c 100644 --- a/src/agentpool/config_resources/__init__.py +++ b/src/wolfharness/config_resources/__init__.py @@ -5,7 +5,7 @@ import importlib.resources from typing import Final -_RESOURCES = importlib.resources.files("agentpool.config_resources") +_RESOURCES = importlib.resources.files("wolfharness.config_resources") # Template configuration AGENTS_TEMPLATE: Final[str] = str(_RESOURCES / "agents_template.yml") diff --git a/src/agentpool/config_resources/acp_assistant.yml b/src/wolfharness/config_resources/acp_assistant.yml similarity index 92% rename from src/agentpool/config_resources/acp_assistant.yml rename to src/wolfharness/config_resources/acp_assistant.yml index c4c9c62c1..637c0dd9b 100644 --- a/src/agentpool/config_resources/acp_assistant.yml +++ b/src/wolfharness/config_resources/acp_assistant.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json # Default ACP assistant configuration # Used when starting ACP server without explicit config file diff --git a/src/agentpool/config_resources/agents.yml b/src/wolfharness/config_resources/agents.yml similarity index 98% rename from src/agentpool/config_resources/agents.yml rename to src/wolfharness/config_resources/agents.yml index 52a29dcd0..6f9904ec0 100644 --- a/src/agentpool/config_resources/agents.yml +++ b/src/wolfharness/config_resources/agents.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json responses: BasicResult: response_schema: diff --git a/src/agentpool/config_resources/agents_template.yml b/src/wolfharness/config_resources/agents_template.yml similarity index 90% rename from src/agentpool/config_resources/agents_template.yml rename to src/wolfharness/config_resources/agents_template.yml index 2f47b6f97..1beecd322 100644 --- a/src/agentpool/config_resources/agents_template.yml +++ b/src/wolfharness/config_resources/agents_template.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: simple_agent: type: native diff --git a/src/agentpool/config_resources/claude_style_subagent.md b/src/wolfharness/config_resources/claude_style_subagent.md similarity index 100% rename from src/agentpool/config_resources/claude_style_subagent.md rename to src/wolfharness/config_resources/claude_style_subagent.md diff --git a/src/agentpool/config_resources/external_acp_agents.yml b/src/wolfharness/config_resources/external_acp_agents.yml similarity index 96% rename from src/agentpool/config_resources/external_acp_agents.yml rename to src/wolfharness/config_resources/external_acp_agents.yml index 787254eb1..360568d90 100644 --- a/src/agentpool/config_resources/external_acp_agents.yml +++ b/src/wolfharness/config_resources/external_acp_agents.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json agents: codex: type: acp diff --git a/src/agentpool/config_resources/opencode_style_subagent.md b/src/wolfharness/config_resources/opencode_style_subagent.md similarity index 100% rename from src/agentpool/config_resources/opencode_style_subagent.md rename to src/wolfharness/config_resources/opencode_style_subagent.md diff --git a/src/agentpool/config_resources/tts_test_agents.yml b/src/wolfharness/config_resources/tts_test_agents.yml similarity index 92% rename from src/agentpool/config_resources/tts_test_agents.yml rename to src/wolfharness/config_resources/tts_test_agents.yml index 4445efb69..c27ffa549 100644 --- a/src/agentpool/config_resources/tts_test_agents.yml +++ b/src/wolfharness/config_resources/tts_test_agents.yml @@ -1,10 +1,10 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/wolfharness/refs/heads/main/schema/config-schema.json # # Test configuration for TTS synchronization modes. # Each agent demonstrates a different TTS mode. # # Usage: -# uv run agentpool chat --config src/agentpool/config_resources/tts_test_agents.yml --agent tts_sync_sentence +# uv run wolfharness chat --config src/wolfharness/config_resources/tts_test_agents.yml --agent tts_sync_sentence # # Modes: # - sync_sentence: Block per sentence (audio syncs with text output) diff --git a/src/wolfharness/delegation/__init__.py b/src/wolfharness/delegation/__init__.py new file mode 100644 index 000000000..1573a73b8 --- /dev/null +++ b/src/wolfharness/delegation/__init__.py @@ -0,0 +1,6 @@ +"""Agent delegation and collaboration functionality.""" + +from wolfharness.delegation.pool import AgentPool +from wolfharness.delegation.base_team import BaseTeam + +__all__ = ["AgentPool", "BaseTeam"] diff --git a/src/agentpool/delegation/base_team.py b/src/wolfharness/delegation/base_team.py similarity index 95% rename from src/agentpool/delegation/base_team.py rename to src/wolfharness/delegation/base_team.py index 5638a4d02..e9e272612 100644 --- a/src/agentpool/delegation/base_team.py +++ b/src/wolfharness/delegation/base_team.py @@ -21,17 +21,17 @@ from jinja2 import BaseLoader, Environment import logfire -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.events import SpawnSessionStart, SubAgentEvent -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError -from agentpool.log import get_logger -from agentpool.messaging import AgentResponse, ChatMessage, MessageNode, TeamResponse -from agentpool.messaging.context import NodeContext -from agentpool.messaging.messagenode import get_source_type -from agentpool.messaging.processing import finalize_message, prepare_prompts -from agentpool.talk.stats import AggregatedMessageStats -from agentpool.talk.talk import Talk -from agentpool.utils.time_utils import get_now +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.events import SpawnSessionStart, SubAgentEvent +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness.log import get_logger +from wolfharness.messaging import AgentResponse, ChatMessage, MessageNode, TeamResponse +from wolfharness.messaging.context import NodeContext +from wolfharness.messaging.messagenode import get_source_type +from wolfharness.messaging.processing import finalize_message, prepare_prompts +from wolfharness.talk.stats import AggregatedMessageStats +from wolfharness.talk.talk import Talk +from wolfharness.utils.time_utils import get_now logger = get_logger(__name__) @@ -43,17 +43,17 @@ from evented_config import EventConfig from toprompt import AnyPromptType - from agentpool import Agent, AgentPool - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.common_types import ( + from wolfharness import Agent, AgentPool + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.common_types import ( ProcessorCallback, PromptCompatible, ) - from agentpool.delegation.graph_team import ExtendedTeamTalk - from agentpool.talk.stats import AggregatedTalkStats - from agentpool.ui.base import InputProvider - from agentpool_config.mcp_server import MCPServerConfig - from agentpool_config.teams import ExecutionMode, TeamMemberConfig + from wolfharness.delegation.graph_team import ExtendedTeamTalk + from wolfharness.talk.stats import AggregatedTalkStats + from wolfharness.ui.base import InputProvider + from wolfharness_config.mcp_server import MCPServerConfig + from wolfharness_config.teams import ExecutionMode, TeamMemberConfig @dataclass(kw_only=True) @@ -150,7 +150,7 @@ def __init__( member_timeout: Per-member execution timeout in seconds. validator: An optional validator node (sequential mode only). """ - from agentpool.delegation.graph_team import ExtendedTeamTalk + from wolfharness.delegation.graph_team import ExtendedTeamTalk self._name = name or " & ".join([i.name for i in agents]) self._nodes: list[MessageNode[Any, Any]] = [] @@ -180,7 +180,7 @@ def nodes(self) -> list[MessageNode[Any, Any]]: def add_node(self, node: MessageNode[Any, Any]) -> None: """Handler for adding new nodes to the team.""" - from agentpool.agents import Agent + from wolfharness.agents import Agent self._nodes.append(node) if isinstance(node, Agent): @@ -189,7 +189,7 @@ def add_node(self, node: MessageNode[Any, Any]) -> None: def remove_node(self, node: MessageNode[Any, Any]) -> None: """Handler for removing nodes from the team.""" - from agentpool.agents import Agent + from wolfharness.agents import Agent self._nodes.remove(node) if isinstance(node, Agent): @@ -227,7 +227,7 @@ def __or__( other: Agent[Any, Any] | ProcessorCallback[Any] | BaseTeam[Any, Any], ) -> BaseTeam[Any, Any]: """Create a sequential pipeline.""" - from agentpool.agents import Agent + from wolfharness.agents import Agent if callable(other): other = Agent.from_callback(other, agent_pool=self._agent_pool) @@ -255,7 +255,7 @@ def __and__( other: BaseTeam[Any, Any] | Agent[Any, Any] | ProcessorCallback[Any], ) -> BaseTeam[Any, Any]: """Combine teams, preserving type safety for same types.""" - from agentpool.agents import Agent + from wolfharness.agents import Agent if callable(other): other = Agent.from_callback(other, agent_pool=self._agent_pool) @@ -495,7 +495,7 @@ async def execute_iter( from pydantic_graph import GraphBuilder, Step from pydantic_graph.id_types import NodeID - from agentpool.delegation.graph_team import _make_sequential_step, _TeamRunGraphState + from wolfharness.delegation.graph_team import _make_sequential_step, _TeamRunGraphState all_nodes = list(self.nodes) if self.validator: @@ -680,7 +680,7 @@ async def run_stream( @staticmethod def _active_parent_session_id() -> str | None: """Return the active SessionPool session id when running inside a turn.""" - from agentpool.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.base_agent import _current_run_ctx_var run_ctx = _current_run_ctx_var.get() session_id = getattr(run_ctx, "session_id", None) @@ -697,8 +697,8 @@ async def _resolve_scoped_team_nodes( if not parent_session_id or ctx is None or ctx.session_pool is None: return nodes, {} - from agentpool.agents.base_agent import BaseAgent - from agentpool.utils.identifiers import generate_session_id + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.utils.identifiers import generate_session_id session_pool = ctx.session_pool pool_agents = ctx.manifest.agents @@ -806,7 +806,7 @@ async def _load_member_skill_instructions( async def _load_skill_instructions(self, skill_name: str, member_name: str) -> str: pool = self._agent_pool if pool is None or pool.skill_resolver is None: - from agentpool.skills.exceptions import SkillNotFoundError + from wolfharness.skills.exceptions import SkillNotFoundError raise SkillNotFoundError(skill_name) @@ -860,7 +860,7 @@ async def _execute_parallel( **kwargs: Any, ) -> TeamResponse: """Run all agents in parallel via pydantic-graph Fork + Join.""" - from agentpool.delegation.graph_team import _TeamGraphState, run_team_graph + from wolfharness.delegation.graph_team import _TeamGraphState, run_team_graph self._team_talk.clear() default_prompt = list(prompts) @@ -892,7 +892,7 @@ async def _execute_parallel( parent_session_id, team_run_id, ) - from agentpool.talk.talk import Talk + from wolfharness.talk.talk import Talk execution_talks: list[Talk[Any]] = [] member_prompts: dict[str, list[PromptCompatible | None]] = {} @@ -939,7 +939,7 @@ async def _execute_sequential( **kwargs: Any, ) -> TeamResponse: """Run all agents sequentially via pydantic-graph chained steps.""" - from agentpool.delegation.graph_team import run_teamrun_graph + from wolfharness.delegation.graph_team import run_teamrun_graph self._team_talk.clear() start_time = get_now() @@ -1036,7 +1036,7 @@ async def _run_iter_sequential( **kwargs: Any, ) -> AsyncIterator[ChatMessage[Any]]: """Yield messages from the sequential execution chain.""" - from agentpool.talk.talk import Talk + from wolfharness.talk.talk import Talk all_nodes = list(self.nodes) if self.validator: @@ -1053,7 +1053,7 @@ async def _run_iter_sequential( connections.append(talk) self._team_talk.append(talk) - from agentpool.delegation.graph_team import run_teamrun_graph + from wolfharness.delegation.graph_team import run_teamrun_graph responses = await run_teamrun_graph( nodes=all_nodes, @@ -1073,8 +1073,8 @@ async def _run_stream_parallel( **kwargs: Any, ) -> AsyncIterator[RichAgentStreamEvent[Any]]: """Stream responses from all team members in parallel.""" - from agentpool.common_types import SupportsRunStream - from agentpool.utils.identifiers import generate_session_id + from wolfharness.common_types import SupportsRunStream + from wolfharness.utils.identifiers import generate_session_id session_id_kwarg: str | None = kwargs.pop("session_id", None) kwargs.pop("depth", None) @@ -1166,9 +1166,9 @@ async def _run_stream_sequential( **kwargs: Any, ) -> AsyncIterator[RichAgentStreamEvent[Any]]: """Stream responses through the chain of team members.""" - from agentpool.agents.events import StreamCompleteEvent - from agentpool.common_types import SupportsRunStream - from agentpool.utils.identifiers import generate_session_id + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.common_types import SupportsRunStream + from wolfharness.utils.identifiers import generate_session_id session_id_kwarg: str | None = kwargs.pop("session_id", None) kwargs.pop("depth", None) @@ -1261,7 +1261,7 @@ async def _run_stream_sequential( if __name__ == "__main__": async def main() -> None: - from agentpool import Agent, BaseTeam + from wolfharness import Agent, BaseTeam agent = Agent("My Agent", model="openai:gpt-5-nano") agent_2 = Agent("My Agent", model="openai:gpt-5-nano") diff --git a/src/agentpool/delegation/graph_team.py b/src/wolfharness/delegation/graph_team.py similarity index 97% rename from src/agentpool/delegation/graph_team.py rename to src/wolfharness/delegation/graph_team.py index 8b0c327d6..c5bf744e6 100644 --- a/src/agentpool/delegation/graph_team.py +++ b/src/wolfharness/delegation/graph_team.py @@ -14,16 +14,16 @@ from pydantic_graph import GraphBuilder, Step, StepContext, reduce_list_append -from agentpool.log import get_logger -from agentpool.messaging import AgentResponse, ChatMessage, TeamResponse -from agentpool.talk.talk import Talk, TeamTalk -from agentpool.utils.time_utils import get_now +from wolfharness.log import get_logger +from wolfharness.messaging import AgentResponse, ChatMessage, TeamResponse +from wolfharness.talk.talk import Talk, TeamTalk +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: from datetime import datetime - from agentpool.messaging.messagenode import MessageNode + from wolfharness.messaging.messagenode import MessageNode logger = get_logger(__name__) diff --git a/src/agentpool/delegation/message_flow_tracker.py b/src/wolfharness/delegation/message_flow_tracker.py similarity index 92% rename from src/agentpool/delegation/message_flow_tracker.py rename to src/wolfharness/delegation/message_flow_tracker.py index 238840016..34a67628f 100644 --- a/src/agentpool/delegation/message_flow_tracker.py +++ b/src/wolfharness/delegation/message_flow_tracker.py @@ -6,8 +6,8 @@ if TYPE_CHECKING: - from agentpool.messaging import ChatMessage - from agentpool.talk import Talk + from wolfharness.messaging import ChatMessage + from wolfharness.talk import Talk class MessageFlowTracker: diff --git a/src/agentpool/delegation/pool.py b/src/wolfharness/delegation/pool.py similarity index 93% rename from src/agentpool/delegation/pool.py rename to src/wolfharness/delegation/pool.py index 8175ef468..07bb4ccbf 100644 --- a/src/agentpool/delegation/pool.py +++ b/src/wolfharness/delegation/pool.py @@ -14,15 +14,15 @@ import logfire from upathtools import to_upath -from agentpool.capabilities.combined_toolset import _NamedCapability -from agentpool.capabilities.extension_registry import ExtensionRegistry -from agentpool.capabilities.resource_protocols import SkillResource -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.delegation.message_flow_tracker import MessageFlowTracker -from agentpool.log import get_logger -from agentpool.skills.uri_resolver import SkillURIResolver -from agentpool.talk.registry import ConnectionRegistry -from agentpool.tasks import TaskRegistry +from wolfharness.capabilities.combined_toolset import _NamedCapability +from wolfharness.capabilities.extension_registry import ExtensionRegistry +from wolfharness.capabilities.resource_protocols import SkillResource +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.delegation.message_flow_tracker import MessageFlowTracker +from wolfharness.log import get_logger +from wolfharness.skills.uri_resolver import SkillURIResolver +from wolfharness.talk.registry import ConnectionRegistry +from wolfharness.tasks import TaskRegistry if TYPE_CHECKING: @@ -32,16 +32,16 @@ from pydantic_ai.capabilities import AbstractCapability from upathtools import JoinablePathLike, UPath - from agentpool.common_types import AnyEventHandlerType - from agentpool.host.context import HostContext - from agentpool.host.factory import AgentFactory - from agentpool.messaging.compaction import CompactionPipeline - from agentpool.models.manifest import AgentsManifest, AnyAgentConfig - from agentpool.orchestrator import SessionPool - from agentpool.orchestrator.run import RunHandle - from agentpool.ui.base import InputProvider - from agentpool_config.session_pool import SessionPoolConfig - from agentpool_config.task import Job + from wolfharness.common_types import AnyEventHandlerType + from wolfharness.host.context import HostContext + from wolfharness.host.factory import AgentFactory + from wolfharness.messaging.compaction import CompactionPipeline + from wolfharness.models.manifest import AgentsManifest, AnyAgentConfig + from wolfharness.orchestrator import SessionPool + from wolfharness.orchestrator.run import RunHandle + from wolfharness.ui.base import InputProvider + from wolfharness_config.session_pool import SessionPoolConfig + from wolfharness_config.task import Job logger = get_logger(__name__) @@ -92,17 +92,17 @@ def __init__( # noqa: PLR0915 Raises: ValueError: If manifest contains invalid configurations """ - from agentpool.mcp_server.manager import MCPManager - from agentpool.models.manifest import AgentsManifest - from agentpool.observability import registry - from agentpool.prompts.manager import PromptManager - from agentpool.skills.manager import SkillsManager - from agentpool.storage import StorageManager - from agentpool.utils.streams import FileOpsTracker - from agentpool.utils.todos import TodoTracker - from agentpool.vfs_registry import VFSRegistry - from agentpool_config.context import ConfigContextManager - from agentpool_toolsets.builtin.debug import install_memory_handler + from wolfharness.mcp_server.manager import MCPManager + from wolfharness.models.manifest import AgentsManifest + from wolfharness.observability import registry + from wolfharness.prompts.manager import PromptManager + from wolfharness.skills.manager import SkillsManager + from wolfharness.storage import StorageManager + from wolfharness.utils.streams import FileOpsTracker + from wolfharness.utils.todos import TodoTracker + from wolfharness.vfs_registry import VFSRegistry + from wolfharness_config.context import ConfigContextManager + from wolfharness_toolsets.builtin.debug import install_memory_handler # Determine config path first, then load everything with context config_path: UPath | None = None @@ -148,7 +148,7 @@ def __init__( # noqa: PLR0915 # Previously handled by _connect_nodes which was removed along # with pool-level agent creation; this lightweight check preserves # the "Forward target .* not found" ValueError contract. - from agentpool_config.forward_targets import NodeConnectionConfig + from wolfharness_config.forward_targets import NodeConnectionConfig agent_names = set(manifest_obj.agents.keys()) for agent_cfg in manifest_obj.agents.values(): @@ -235,7 +235,7 @@ def get_context(self) -> HostContext: ``session_pool`` is up-to-date. """ if self._host_context is None or self._host_context.session_pool is not self._session_pool: - from agentpool.host.context import HostContext + from wolfharness.host.context import HostContext self._host_context = HostContext( manifest=self.manifest, @@ -266,7 +266,7 @@ def _safe_main_agent_name(self) -> str | None: def _factory(self) -> AgentFactory: """Lazy-initialized AgentFactory, cached on first access.""" if self._factory_instance is None: - from agentpool.host.factory import AgentFactory + from wolfharness.host.factory import AgentFactory self._factory_instance = AgentFactory(pool=self) return self._factory_instance @@ -292,7 +292,7 @@ async def __aenter__(self) -> Self: # Register config-defined capabilities (e.g. Viking, MCP, code # mode) at AGENT scope so they are available in the # ExtensionRegistry before any message handling occurs. - from agentpool.host.factory import AgentFactory + from wolfharness.host.factory import AgentFactory factory = AgentFactory(self) factory.register_config_capabilities(self.manifest, self.get_context()) @@ -305,7 +305,7 @@ async def __aenter__(self) -> Self: if self.storage.providers: self._session_store = self.storage.get_project_provider() # Initialize SessionPool - from agentpool.orchestrator import SessionPool + from wolfharness.orchestrator import SessionPool cfg = self._session_pool_config self._session_pool = SessionPool( @@ -324,7 +324,7 @@ async def __aenter__(self) -> Self: # Start team cleanup background task if team mode is enabled team_mode = self.manifest.team_mode if team_mode is not None and team_mode.enabled: - from agentpool.capabilities.file_team_state import ( + from wolfharness.capabilities.file_team_state import ( start_team_cleanup_task, ) @@ -417,7 +417,7 @@ def sessions(self) -> SessionPool | Any: @sessions.setter def sessions(self, value: Any) -> None: """Setter for test compatibility.""" - from agentpool.orchestrator import SessionPool + from wolfharness.orchestrator import SessionPool if isinstance(value, SessionPool): self._session_pool = value @@ -572,7 +572,7 @@ def is_skill_visible_to_node(self, skill: Any, node_name: str | None) -> bool: async def get_skill_instructions_for_node(self, skill_name: str, node_name: str) -> str: """Load skill instructions using a target node's package scope.""" - from agentpool.skills.exceptions import SkillNotFoundError + from wolfharness.skills.exceptions import SkillNotFoundError if self._skill_resolver is not None: skill = await self._skill_resolver.resolve(skill_name) @@ -641,8 +641,8 @@ async def _rebuild_skill_capabilities(self) -> None: This method is called: - During ``__aenter__`` after skill discovery completes. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.skills.skill_tool_manager import SkillToolManager + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.skills.skill_tool_manager import SkillToolManager # Build local skills dict from SkillsManager. local_skills: dict[str, Any] = {} @@ -730,7 +730,7 @@ async def _setup_resource_capability(self) -> None: via ``@runtime_checkable`` false-positive, leading to ``TypeError`` in ``resolve_resource_content()``. """ - from agentpool.capabilities.resource_capability import ResourceCapability + from wolfharness.capabilities.resource_capability import ResourceCapability cap = ResourceCapability() self._resource_capability = cap @@ -906,7 +906,7 @@ def register_task(self, name: str, task: Job[Any, Any]) -> None: if __name__ == "__main__": async def main() -> None: - path = "src/agentpool/config_resources/agents.yml" + path = "src/wolfharness/config_resources/agents.yml" async with AgentPool(path) as pool: print(f"AgentPool loaded with agents: {list(pool.agent_configs.keys())}") diff --git a/src/agentpool/diagnostics/__init__.py b/src/wolfharness/diagnostics/__init__.py similarity index 86% rename from src/agentpool/diagnostics/__init__.py rename to src/wolfharness/diagnostics/__init__.py index 4c4656335..114f78eec 100644 --- a/src/agentpool/diagnostics/__init__.py +++ b/src/wolfharness/diagnostics/__init__.py @@ -6,9 +6,9 @@ - Integration with execution environments (local, remote, sandboxed) """ -from agentpool.diagnostics.lsp_manager import LSPManager -from agentpool.diagnostics.lsp_proxy import LSPProxy -from agentpool.diagnostics.models import ( +from wolfharness.diagnostics.lsp_manager import LSPManager +from wolfharness.diagnostics.lsp_proxy import LSPProxy +from wolfharness.diagnostics.models import ( COMPLETION_KIND_MAP, CODE_ACTION_KIND_MAP, SYMBOL_KIND_MAP, diff --git a/src/agentpool/diagnostics/lsp_manager.py b/src/wolfharness/diagnostics/lsp_manager.py similarity index 99% rename from src/agentpool/diagnostics/lsp_manager.py rename to src/wolfharness/diagnostics/lsp_manager.py index 3dc57c8b6..95906e0a2 100644 --- a/src/agentpool/diagnostics/lsp_manager.py +++ b/src/wolfharness/diagnostics/lsp_manager.py @@ -17,7 +17,7 @@ import anyenv -from agentpool.diagnostics.models import ( +from wolfharness.diagnostics.models import ( COMPLETION_KIND_MAP, SYMBOL_KIND_MAP, CallHierarchyCall, @@ -42,7 +42,7 @@ from anyenv.lsp_servers import LSPServerInfo from exxec import ExecutionEnvironment - from agentpool.diagnostics.models import ( + from wolfharness.diagnostics.models import ( HoverContents, ) @@ -183,7 +183,7 @@ async def start_server( ValueError: If server_id not registered RuntimeError: If server fails to start """ - from agentpool.diagnostics.lsp_proxy import LSPProxy + from wolfharness.diagnostics.lsp_proxy import LSPProxy if server_id in self._servers: return self._servers[server_id] @@ -1202,7 +1202,7 @@ def _uri_to_language_id(uri: str) -> str: def _convert_diagnostic(diag: Any, server_id: str) -> Diagnostic: - """Convert anyenv Diagnostic to agentpool Diagnostic.""" + """Convert anyenv Diagnostic to wolfharness Diagnostic.""" return Diagnostic( file=diag.file, line=diag.line, diff --git a/src/agentpool/diagnostics/lsp_proxy.py b/src/wolfharness/diagnostics/lsp_proxy.py similarity index 100% rename from src/agentpool/diagnostics/lsp_proxy.py rename to src/wolfharness/diagnostics/lsp_proxy.py diff --git a/src/agentpool/diagnostics/lsp_proxy_script.py b/src/wolfharness/diagnostics/lsp_proxy_script.py similarity index 100% rename from src/agentpool/diagnostics/lsp_proxy_script.py rename to src/wolfharness/diagnostics/lsp_proxy_script.py diff --git a/src/agentpool/diagnostics/models.py b/src/wolfharness/diagnostics/models.py similarity index 100% rename from src/agentpool/diagnostics/models.py rename to src/wolfharness/diagnostics/models.py diff --git a/src/agentpool/docs/__init__.py b/src/wolfharness/docs/__init__.py similarity index 54% rename from src/agentpool/docs/__init__.py rename to src/wolfharness/docs/__init__.py index 969d0fc92..9f9578547 100644 --- a/src/agentpool/docs/__init__.py +++ b/src/wolfharness/docs/__init__.py @@ -1,5 +1,5 @@ """Example / Docs helpers.""" -from agentpool.docs.utils import generate_tool_docs, tool_to_markdown +from wolfharness.docs.utils import generate_tool_docs, tool_to_markdown __all__ = ["generate_tool_docs", "tool_to_markdown"] diff --git a/src/agentpool/docs/gen_examples.py b/src/wolfharness/docs/gen_examples.py similarity index 96% rename from src/agentpool/docs/gen_examples.py rename to src/wolfharness/docs/gen_examples.py index 61515f262..f26635f45 100644 --- a/src/agentpool/docs/gen_examples.py +++ b/src/wolfharness/docs/gen_examples.py @@ -10,7 +10,7 @@ DocStyle = Literal["simple", "full"] -EXAMPLES_DIR = Path("src/agentpool_docs/examples") +EXAMPLES_DIR = Path("src/wolfharness_docs/examples") def create_example_doc(name: str, *, style: DocStyle = "full") -> mk.MkContainer: diff --git a/src/agentpool/docs/utils.py b/src/wolfharness/docs/utils.py similarity index 95% rename from src/agentpool/docs/utils.py rename to src/wolfharness/docs/utils.py index 2bf202b68..dc80ae637 100644 --- a/src/agentpool/docs/utils.py +++ b/src/wolfharness/docs/utils.py @@ -12,11 +12,11 @@ if TYPE_CHECKING: from collections.abc import Awaitable, Iterator - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.tools.base import Tool + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.tools.base import Tool -EXAMPLES_DIR = Path("src/agentpool_docs/examples") +EXAMPLES_DIR = Path("src/wolfharness_docs/examples") def is_pyodide() -> bool: @@ -116,7 +116,7 @@ def iter_examples(root: Path | str | None = None) -> Iterator[Example]: """Iterate over all available examples. Args: - root: Optional root directory (defaults to agentpool_docs/examples) + root: Optional root directory (defaults to wolfharness_docs/examples) """ root = Path(root) if root else EXAMPLES_DIR @@ -195,7 +195,7 @@ def check_docs_for_union( Example: ```python - from agentpool_config.toolsets import ToolsetConfig + from wolfharness_config.toolsets import ToolsetConfig missing, extra = check_docs_for_union( ToolsetConfig, Path("docs/configuration/toolsets"), @@ -317,8 +317,8 @@ def generate_tool_docs(toolset: FunctionToolsetCapability[Any]) -> str: Example: ```python exec="true" - from agentpool_toolsets.builtin.code import CodeTools - from agentpool.docs.utils import generate_tool_docs + from wolfharness_toolsets.builtin.code import CodeTools + from wolfharness.docs.utils import generate_tool_docs toolset = CodeTools() print(generate_tool_docs(toolset)) diff --git a/src/agentpool/functional/__init__.py b/src/wolfharness/functional/__init__.py similarity index 77% rename from src/agentpool/functional/__init__.py rename to src/wolfharness/functional/__init__.py index 990a9e220..19447b70b 100644 --- a/src/agentpool/functional/__init__.py +++ b/src/wolfharness/functional/__init__.py @@ -1,10 +1,10 @@ """High-level functional interfaces for AgentPool.""" -from agentpool.functional.run import ( +from wolfharness.functional.run import ( run_agent, run_agent_sync, ) -from agentpool.functional.structure import ( +from wolfharness.functional.structure import ( get_structured, get_structured_multiple, pick_one, diff --git a/src/agentpool/functional/py.typed b/src/wolfharness/functional/py.typed similarity index 100% rename from src/agentpool/functional/py.typed rename to src/wolfharness/functional/py.typed diff --git a/src/agentpool/functional/run.py b/src/wolfharness/functional/run.py similarity index 92% rename from src/agentpool/functional/run.py rename to src/wolfharness/functional/run.py index cb5d716ed..b8605ec58 100644 --- a/src/agentpool/functional/run.py +++ b/src/wolfharness/functional/run.py @@ -7,12 +7,12 @@ from anyenv import run_sync from pydantic_ai import ImageUrl -from agentpool import Agent +from wolfharness import Agent if TYPE_CHECKING: - from agentpool.agents.native_agent import AgentKwargs - from agentpool.common_types import PromptCompatible + from wolfharness.agents.native_agent import AgentKwargs + from wolfharness.common_types import PromptCompatible @overload diff --git a/src/agentpool/functional/structure.py b/src/wolfharness/functional/structure.py similarity index 96% rename from src/agentpool/functional/structure.py rename to src/wolfharness/functional/structure.py index 0b8ba4738..f2923ae92 100644 --- a/src/agentpool/functional/structure.py +++ b/src/wolfharness/functional/structure.py @@ -5,14 +5,14 @@ from enum import Enum from typing import TYPE_CHECKING, Literal, get_args -from agentpool.agents.native_agent import Agent -from agentpool.log import get_logger +from wolfharness.agents.native_agent import Agent +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import Callable - from agentpool.common_types import ModelType + from wolfharness.common_types import ModelType logger = get_logger(__name__) diff --git a/src/agentpool/hooks/AGENTS.md b/src/wolfharness/hooks/AGENTS.md similarity index 100% rename from src/agentpool/hooks/AGENTS.md rename to src/wolfharness/hooks/AGENTS.md diff --git a/src/wolfharness/hooks/__init__.py b/src/wolfharness/hooks/__init__.py new file mode 100644 index 000000000..a3eb59777 --- /dev/null +++ b/src/wolfharness/hooks/__init__.py @@ -0,0 +1,20 @@ +"""Runtime hook classes for agent lifecycle events.""" + +from __future__ import annotations + +from wolfharness.hooks.agent_hooks import AgentHooks +from wolfharness.hooks.base import Hook, HookEvent, HookInput, HookResult +from wolfharness.hooks.callable import CallableHook +from wolfharness.hooks.command import CommandHook +from wolfharness.hooks.prompt import PromptHook + +__all__ = [ + "AgentHooks", + "CallableHook", + "CommandHook", + "Hook", + "HookEvent", + "HookInput", + "HookResult", + "PromptHook", +] diff --git a/src/agentpool/hooks/agent_hooks.py b/src/wolfharness/hooks/agent_hooks.py similarity index 98% rename from src/agentpool/hooks/agent_hooks.py rename to src/wolfharness/hooks/agent_hooks.py index 6bd837193..76414c418 100644 --- a/src/agentpool/hooks/agent_hooks.py +++ b/src/wolfharness/hooks/agent_hooks.py @@ -6,8 +6,8 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any -from agentpool.hooks.base import HookInput, HookResult -from agentpool.log import get_logger +from wolfharness.hooks.base import HookInput, HookResult +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -15,7 +15,7 @@ from exxec import ExecutionEnvironment - from agentpool.hooks.base import Hook + from wolfharness.hooks.base import Hook logger = get_logger(__name__) diff --git a/src/agentpool/hooks/base.py b/src/wolfharness/hooks/base.py similarity index 99% rename from src/agentpool/hooks/base.py rename to src/wolfharness/hooks/base.py index 4b005483d..4651dcc5b 100644 --- a/src/agentpool/hooks/base.py +++ b/src/wolfharness/hooks/base.py @@ -6,7 +6,7 @@ import re from typing import TYPE_CHECKING, Any, Literal, TypedDict -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool/hooks/callable.py b/src/wolfharness/hooks/callable.py similarity index 95% rename from src/agentpool/hooks/callable.py rename to src/wolfharness/hooks/callable.py index 6b57fc8ea..705b9e4e7 100644 --- a/src/agentpool/hooks/callable.py +++ b/src/wolfharness/hooks/callable.py @@ -5,9 +5,9 @@ import asyncio from typing import TYPE_CHECKING, Any -from agentpool.hooks.base import Hook, HookResult -from agentpool.log import get_logger -from agentpool.utils.importing import import_callable +from wolfharness.hooks.base import Hook, HookResult +from wolfharness.log import get_logger +from wolfharness.utils.importing import import_callable if TYPE_CHECKING: @@ -15,7 +15,7 @@ from exxec import ExecutionEnvironment - from agentpool.hooks.base import HookEvent, HookInput + from wolfharness.hooks.base import HookEvent, HookInput logger = get_logger(__name__) diff --git a/src/agentpool/hooks/command.py b/src/wolfharness/hooks/command.py similarity index 97% rename from src/agentpool/hooks/command.py rename to src/wolfharness/hooks/command.py index 8e709fbe7..9db9f43e0 100644 --- a/src/agentpool/hooks/command.py +++ b/src/wolfharness/hooks/command.py @@ -9,14 +9,14 @@ import anyenv -from agentpool.hooks.base import Hook, HookResult -from agentpool.log import get_logger +from wolfharness.hooks.base import Hook, HookResult +from wolfharness.log import get_logger if TYPE_CHECKING: from exxec import ExecutionEnvironment - from agentpool.hooks.base import HookEvent, HookInput + from wolfharness.hooks.base import HookEvent, HookInput logger = get_logger(__name__) diff --git a/src/agentpool/hooks/prompt.py b/src/wolfharness/hooks/prompt.py similarity index 95% rename from src/agentpool/hooks/prompt.py rename to src/wolfharness/hooks/prompt.py index cc48a1f97..ba2fbe963 100644 --- a/src/agentpool/hooks/prompt.py +++ b/src/wolfharness/hooks/prompt.py @@ -9,14 +9,14 @@ from pydantic import Field from schemez import Schema -from agentpool.hooks.base import Hook, HookResult -from agentpool.log import get_logger +from wolfharness.hooks.base import Hook, HookResult +from wolfharness.log import get_logger if TYPE_CHECKING: from exxec import ExecutionEnvironment - from agentpool.hooks.base import HookEvent, HookInput + from wolfharness.hooks.base import HookEvent, HookInput logger = get_logger(__name__) @@ -125,7 +125,7 @@ async def _query_llm(self, prompt: str) -> HookDecision: Returns: Structured hook decision. """ - from agentpool import Agent + from wolfharness import Agent agent = Agent( model=self.model, diff --git a/src/agentpool/host/__init__.py b/src/wolfharness/host/__init__.py similarity index 100% rename from src/agentpool/host/__init__.py rename to src/wolfharness/host/__init__.py diff --git a/src/agentpool/host/context.py b/src/wolfharness/host/context.py similarity index 75% rename from src/agentpool/host/context.py rename to src/wolfharness/host/context.py index 4bdfa35df..8bc3caa4e 100644 --- a/src/agentpool/host/context.py +++ b/src/wolfharness/host/context.py @@ -10,7 +10,7 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING -from agentpool.host.stubs import CapabilityCache, ModelCache, ModelRegistry +from wolfharness.host.stubs import CapabilityCache, ModelCache, ModelRegistry if TYPE_CHECKING: @@ -19,17 +19,17 @@ from anyenv import ProcessManager from upathtools import UPath - from agentpool.capabilities.extension_registry import ExtensionRegistry - from agentpool.mcp_server.manager import MCPManager - from agentpool.models.manifest import AgentsManifest - from agentpool.orchestrator import SessionPool - from agentpool.prompts.manager import PromptManager - from agentpool.skills.manager import SkillsManager - from agentpool.storage import StorageManager - from agentpool.talk.registry import ConnectionRegistry - from agentpool.utils.streams import FileOpsTracker - from agentpool.utils.todos import TodoTracker - from agentpool.vfs_registry import VFSRegistry + from wolfharness.capabilities.extension_registry import ExtensionRegistry + from wolfharness.mcp_server.manager import MCPManager + from wolfharness.models.manifest import AgentsManifest + from wolfharness.orchestrator import SessionPool + from wolfharness.prompts.manager import PromptManager + from wolfharness.skills.manager import SkillsManager + from wolfharness.storage import StorageManager + from wolfharness.talk.registry import ConnectionRegistry + from wolfharness.utils.streams import FileOpsTracker + from wolfharness.utils.todos import TodoTracker + from wolfharness.vfs_registry import VFSRegistry @dataclass(frozen=True) diff --git a/src/agentpool/host/factory.py b/src/wolfharness/host/factory.py similarity index 91% rename from src/agentpool/host/factory.py rename to src/wolfharness/host/factory.py index 072aa7d50..624d90325 100644 --- a/src/agentpool/host/factory.py +++ b/src/wolfharness/host/factory.py @@ -26,7 +26,7 @@ Entry-Point Discovery (M3 Todo 12): ``compile()`` calls ``discover_entry_point_capabilities()`` to load all capability classes registered via the - ``agentpool.capabilities`` entry-point group. The discovered + ``wolfharness.capabilities`` entry-point group. The discovered mapping is stored in ``_entry_point_capabilities`` and used by ``resolve_capability_type()`` to resolve YAML ``type:`` references to capability classes. External packages can @@ -39,7 +39,7 @@ import asyncio from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -47,15 +47,15 @@ from pydantic_ai.capabilities import AbstractCapability - from agentpool.agents.base_agent import BaseAgent - from agentpool.agents.native_agent import Agent as NativeAgent - from agentpool.capabilities.change_event import ChangeEvent - from agentpool.delegation.pool import AgentPool - from agentpool.host.context import HostContext - from agentpool.host.registry import AgentRegistry - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest, AnyAgentConfig - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.agents.native_agent import Agent as NativeAgent + from wolfharness.capabilities.change_event import ChangeEvent + from wolfharness.delegation.pool import AgentPool + from wolfharness.host.context import HostContext + from wolfharness.host.registry import AgentRegistry + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest, AnyAgentConfig + from wolfharness.orchestrator.session_controller import SessionState logger = get_logger(__name__) @@ -111,7 +111,7 @@ def entry_point_capabilities(self) -> dict[str, type[AbstractCapability[object]] """Return the discovered entry-point capability types. Maps type names to capability classes discovered via the - ``agentpool.capabilities`` entry-point group. Populated by + ``wolfharness.capabilities`` entry-point group. Populated by ``compile()``. """ return self._entry_point_capabilities @@ -129,7 +129,7 @@ def compile( ``AggregatedResourceSource`` per agent. Discovers entry-point capabilities via the - ``agentpool.capabilities`` entry-point group and stores them + ``wolfharness.capabilities`` entry-point group and stores them for resolving YAML ``type:`` references to capability classes. Returns an empty ``AgentRegistry`` — agents are created lazily @@ -142,8 +142,8 @@ def compile( Returns: An empty AgentRegistry. """ - from agentpool.capabilities.registry import discover_entry_point_capabilities - from agentpool.host.registry import AgentRegistry + from wolfharness.capabilities.registry import discover_entry_point_capabilities + from wolfharness.host.registry import AgentRegistry self._capability_registry = {} self._entry_point_capabilities = discover_entry_point_capabilities() @@ -154,7 +154,7 @@ def compile( # Register capabilities at AGENT scope — these are compiled # from the agent's config and are specific to this agent. - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel agent_scope = Scope(level=ScopeLevel.AGENT, agent_name=agent_name) for cap in caps: @@ -186,9 +186,9 @@ def register_config_capabilities( manifest: The agents manifest to register caps from. host_context: The host context with shared services. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.models.agents import NativeAgentConfig - from agentpool_config.capabilities import build_config_capabilities + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.models.agents import NativeAgentConfig + from wolfharness_config.capabilities import build_config_capabilities for agent_name, cfg in manifest.agents.items(): if not isinstance(cfg, NativeAgentConfig) or not cfg.capabilities: @@ -208,7 +208,7 @@ def _build_agent_descriptions( Uses ``description`` field if set, otherwise falls back to the first non-empty line of ``system_prompt`` (for string prompts). """ - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig descriptions: dict[str, str] = {} agents = host_context.manifest.agents @@ -263,8 +263,8 @@ def _compile_agent_capabilities( Returns: List of compiled ``AbstractCapability`` instances. """ - from agentpool.capabilities.subagent_capability import SubagentCapability - from agentpool.models.agents import NativeAgentConfig + from wolfharness.capabilities.subagent_capability import SubagentCapability + from wolfharness.models.agents import NativeAgentConfig caps: list[AbstractCapability[Any]] = [] @@ -293,12 +293,12 @@ def _compile_agent_capabilities( # _extra_capabilities (line ~438) and conflict with the # agent's own _external_capabilities (duplicate tools). if isinstance(cfg, NativeAgentConfig) and cfg.capabilities: - from agentpool_config.capabilities import build_config_capabilities + from wolfharness_config.capabilities import build_config_capabilities config_caps = build_config_capabilities(cfg.capabilities) # Register directly at AGENT scope — do NOT add to `caps`. - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel agent_scope = Scope(level=ScopeLevel.AGENT, agent_name=agent_name) for c in config_caps: @@ -306,7 +306,7 @@ def _compile_agent_capabilities( # 4. Team communication capability — shared instance with session_metadata=None. # Per-session instance with actual metadata is created in create_session_agent(). - from agentpool_config.team_mode import resolve_team_mode + from wolfharness_config.team_mode import resolve_team_mode global_tm = host_context.manifest.team_mode agent_tm = cfg.team_mode @@ -314,7 +314,7 @@ def _compile_agent_capabilities( if resolved_tm is not None and resolved_tm.enabled: eligible = resolved_tm.lead_eligible + resolved_tm.member_eligible if agent_name in eligible: - from agentpool.capabilities.team_comm_capability import TeamCommCapability + from wolfharness.capabilities.team_comm_capability import TeamCommCapability agent_descs = self._build_agent_descriptions(host_context, eligible) caps.append( @@ -327,13 +327,13 @@ def _compile_agent_capabilities( # MCP servers are NOT compiled here — they are handled by MCPManager # which creates MCPCapability instances. MCPCapability is now - # deprecated; McpServerCap (agentpool.capabilities.mcp_server_cap) + # deprecated; McpServerCap (wolfharness.capabilities.mcp_server_cap) # is the replacement. Full migration to McpServerCap with # SessionConnectionPool injection happens in Phase 2/4. # Phase 4 task 4.13 will migrate MCPManager to use McpServerCap # via ExtensionRegistry. - from agentpool.capabilities.combined_toolset import _NamedCapability + from wolfharness.capabilities.combined_toolset import _NamedCapability logger.debug( "Compiled capabilities for agent", @@ -356,7 +356,7 @@ def _has_subagent_toolset(self, cfg: AnyAgentConfig) -> bool: Returns: True if the config has a tool with ``type == "subagent"``. """ - from agentpool_config.toolsets import SubagentToolsetConfig + from wolfharness_config.toolsets import SubagentToolsetConfig return any(isinstance(tool_config, SubagentToolsetConfig) for tool_config in cfg.tools) @@ -380,7 +380,7 @@ def resolve_capability_type( CapabilityNotFoundError: If ``type_name`` is not a registered entry-point capability. """ - from agentpool.capabilities.registry import resolve_capability_type + from wolfharness.capabilities.registry import resolve_capability_type return resolve_capability_type(type_name, self._entry_point_capabilities) @@ -422,7 +422,7 @@ async def create_session_agent( Returns: The created agent instance (already entered via __aenter__). """ - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig # Fix config name if missing. if cfg.name is None: @@ -463,13 +463,13 @@ async def create_session_agent( # (ACP, etc.) receive capabilities through their own protocol. caps = self._capability_registry.get(agent_name, []) if caps: - from agentpool.agents.native_agent import Agent as _NativeAgent + from wolfharness.agents.native_agent import Agent as _NativeAgent if isinstance(agent, _NativeAgent): agent._extra_capabilities = list(caps) # Per-session TeamCommCapability with actual session metadata. - from agentpool_config.team_mode import resolve_team_mode + from wolfharness_config.team_mode import resolve_team_mode global_tm = host_context.manifest.team_mode agent_tm = cfg.team_mode @@ -477,8 +477,8 @@ async def create_session_agent( if resolved_tm is not None and resolved_tm.enabled: eligible = resolved_tm.lead_eligible + resolved_tm.member_eligible if agent_name in eligible: - from agentpool.agents.native_agent import Agent as _NativeAgent2 - from agentpool.capabilities.team_comm_capability import TeamCommCapability + from wolfharness.agents.native_agent import Agent as _NativeAgent2 + from wolfharness.capabilities.team_comm_capability import TeamCommCapability # Set team_role metadata so tools can check lead/member permissions. # Protocol servers don't set this — factory is the right place. @@ -512,7 +512,7 @@ async def create_session_agent( agent._extra_capabilities = new_caps # Register the per-session TeamCommCapability at SESSION scope. - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel session_scope = Scope(level=ScopeLevel.SESSION, session_id=session_id) self._pool.extension_registry.register(team_cap, session_scope) @@ -553,7 +553,7 @@ async def _create_native_child( Path A: Inherits env, filesystem, MCP snapshot, and ACP transports from the parent agent. Model is NOT inherited. """ - from agentpool_config.context import ConfigContextManager + from wolfharness_config.context import ConfigContextManager with ConfigContextManager(host_context.config_file_path): agent: NativeAgent[Any, Any] = cfg.get_agent( @@ -571,8 +571,8 @@ async def _create_native_child( await agent.__aenter__() # MCP snapshot strategy A: INHERIT from parent. - from agentpool.agents.native_agent import Agent as _NativeAgent - from agentpool.mcp_server.config_snapshot import ( + from wolfharness.agents.native_agent import Agent as _NativeAgent + from wolfharness.mcp_server.config_snapshot import ( McpConfigSnapshot, ) @@ -643,7 +643,7 @@ async def _create_native_main( Path B: Builds MCP snapshot from the agent's own pool and agent configs. Loads conversation history from storage. """ - from agentpool_config.context import ConfigContextManager + from wolfharness_config.context import ConfigContextManager with ConfigContextManager(host_context.config_file_path): agent: NativeAgent[Any, Any] = cfg.get_agent( @@ -663,7 +663,7 @@ async def _create_native_main( ) # MCP snapshot strategy B: BUILD from agent. - from agentpool.mcp_server.config_snapshot import ( + from wolfharness.mcp_server.config_snapshot import ( McpConfigSnapshot, ) @@ -697,11 +697,11 @@ async def _create_non_native( Path C: Builds MCP snapshot manually from pool MCPManager and agent config's ``get_mcp_servers()``. """ - from agentpool.mcp_server.config_snapshot import ( + from wolfharness.mcp_server.config_snapshot import ( McpConfigEntry, McpConfigSnapshot, ) - from agentpool_config.context import ConfigContextManager + from wolfharness_config.context import ConfigContextManager with ConfigContextManager(host_context.config_file_path): agent: BaseAgent[Any, Any] = cfg.get_agent( @@ -762,7 +762,7 @@ async def _start_hot_swap_listeners( agent: The agent instance whose capabilities to monitor. caps: The compiled capability list to monitor. """ - from agentpool.capabilities.combined_toolset import ( + from wolfharness.capabilities.combined_toolset import ( _NamedCapability, _OnChangeCapable, ) @@ -829,7 +829,7 @@ async def _hot_swap_loop( except asyncio.CancelledError: raise except Exception: - from agentpool.capabilities.combined_toolset import _NamedCapability + from wolfharness.capabilities.combined_toolset import _NamedCapability cap_name = cap.name if isinstance(cap, _NamedCapability) else type(cap).__name__ logger.exception( diff --git a/src/agentpool/host/registry.py b/src/wolfharness/host/registry.py similarity index 96% rename from src/agentpool/host/registry.py rename to src/wolfharness/host/registry.py index 1b11ed90b..79031c1a7 100644 --- a/src/agentpool/host/registry.py +++ b/src/wolfharness/host/registry.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: from collections.abc import Iterator - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent class AgentRegistry: diff --git a/src/agentpool/host/stubs.py b/src/wolfharness/host/stubs.py similarity index 99% rename from src/agentpool/host/stubs.py rename to src/wolfharness/host/stubs.py index 299fd268d..f6e1f76e7 100644 --- a/src/agentpool/host/stubs.py +++ b/src/wolfharness/host/stubs.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities logger = structlog.get_logger(__name__) diff --git a/src/agentpool/jinja_filters.py b/src/wolfharness/jinja_filters.py similarity index 98% rename from src/agentpool/jinja_filters.py rename to src/wolfharness/jinja_filters.py index 6a66c37e2..f63c0f73a 100644 --- a/src/agentpool/jinja_filters.py +++ b/src/wolfharness/jinja_filters.py @@ -1,4 +1,4 @@ -"""Jinja filters for agentpool documentation.""" +"""Jinja filters for wolfharness documentation.""" from __future__ import annotations diff --git a/src/agentpool/lifecycle/AGENTS.md b/src/wolfharness/lifecycle/AGENTS.md similarity index 92% rename from src/agentpool/lifecycle/AGENTS.md rename to src/wolfharness/lifecycle/AGENTS.md index 27a0509d3..3f1cd820b 100644 --- a/src/agentpool/lifecycle/AGENTS.md +++ b/src/wolfharness/lifecycle/AGENTS.md @@ -25,5 +25,5 @@ ## Anti-Patterns -- **Mixing `lifecycle.EventEnvelope` with `orchestrator.event_bus.EventEnvelope`**: Import from `agentpool.lifecycle.types` for lifecycle transport; from `agentpool.orchestrator.event_bus` for internal EventBus. +- **Mixing `lifecycle.EventEnvelope` with `orchestrator.event_bus.EventEnvelope`**: Import from `wolfharness.lifecycle.types` for lifecycle transport; from `wolfharness.orchestrator.event_bus` for internal EventBus. - **Do NOT instrument MemoryJournal / MemorySnapshotStore**: Pure in-memory implementations, no telemetry needed. diff --git a/src/agentpool/lifecycle/__init__.py b/src/wolfharness/lifecycle/__init__.py similarity index 75% rename from src/agentpool/lifecycle/__init__.py rename to src/wolfharness/lifecycle/__init__.py index 8d1646696..347e2cacc 100644 --- a/src/agentpool/lifecycle/__init__.py +++ b/src/wolfharness/lifecycle/__init__.py @@ -12,28 +12,28 @@ from __future__ import annotations -from agentpool.lifecycle.comm_channel import DirectChannel, ProtocolChannel -from agentpool.lifecycle.event_transport import InProcessTransport -from agentpool.lifecycle.factory import create_dimensions -from agentpool.lifecycle.journal import DurableJournal, MemoryJournal -from agentpool.lifecycle.protocols import ( +from wolfharness.lifecycle.comm_channel import DirectChannel, ProtocolChannel +from wolfharness.lifecycle.event_transport import InProcessTransport +from wolfharness.lifecycle.factory import create_dimensions +from wolfharness.lifecycle.journal import DurableJournal, MemoryJournal +from wolfharness.lifecycle.protocols import ( CommChannel, EventTransport, Journal, SnapshotStore, TriggerSource, ) -from agentpool.lifecycle.snapshot_store import ( +from wolfharness.lifecycle.snapshot_store import ( DurableSnapshotStore, MemorySnapshotStore, ) -from agentpool.lifecycle.triggers import ( +from wolfharness.lifecycle.triggers import ( ChannelTrigger, ImmediateTrigger, ProtocolTrigger, ScheduledTrigger, ) -from agentpool.lifecycle.types import ( +from wolfharness.lifecycle.types import ( DeliveryMode, EventEnvelope, Feedback, diff --git a/src/agentpool/lifecycle/comm_channel.py b/src/wolfharness/lifecycle/comm_channel.py similarity index 98% rename from src/agentpool/lifecycle/comm_channel.py rename to src/wolfharness/lifecycle/comm_channel.py index 946b14470..0ff2da869 100644 --- a/src/agentpool/lifecycle/comm_channel.py +++ b/src/wolfharness/lifecycle/comm_channel.py @@ -21,19 +21,19 @@ from collections import deque from typing import TYPE_CHECKING, Any -from agentpool.agents.events import ( +from wolfharness.agents.events import ( MessageReplacementEvent, PlanUpdateEvent, StateUpdate, ToolCallUpdateEvent, ) -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool.lifecycle.protocols import Journal - from agentpool.lifecycle.types import Feedback, RunState - from agentpool.orchestrator.event_bus import EventBus + from wolfharness.lifecycle.protocols import Journal + from wolfharness.lifecycle.types import Feedback, RunState + from wolfharness.orchestrator.event_bus import EventBus logger = get_logger(__name__) diff --git a/src/agentpool/lifecycle/event_transport.py b/src/wolfharness/lifecycle/event_transport.py similarity index 99% rename from src/agentpool/lifecycle/event_transport.py rename to src/wolfharness/lifecycle/event_transport.py index 0ee43a7ba..75f64ba2f 100644 --- a/src/agentpool/lifecycle/event_transport.py +++ b/src/wolfharness/lifecycle/event_transport.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.lifecycle.types import EventEnvelope + from wolfharness.lifecycle.types import EventEnvelope class InProcessTransport: diff --git a/src/agentpool/lifecycle/factory.py b/src/wolfharness/lifecycle/factory.py similarity index 92% rename from src/agentpool/lifecycle/factory.py rename to src/wolfharness/lifecycle/factory.py index 99b7741c2..b231b4463 100644 --- a/src/agentpool/lifecycle/factory.py +++ b/src/wolfharness/lifecycle/factory.py @@ -7,7 +7,7 @@ Usage:: - from agentpool.lifecycle.factory import create_dimensions + from wolfharness.lifecycle.factory import create_dimensions trigger, journal, snapshot, comm, transport = create_dimensions( lifecycle_config, @@ -33,28 +33,28 @@ from platformdirs import user_state_dir from sqlalchemy import Engine, create_engine -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.event_transport import InProcessTransport -from agentpool.lifecycle.journal import DurableJournal, MemoryJournal -from agentpool.lifecycle.snapshot_store import ( +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.event_transport import InProcessTransport +from wolfharness.lifecycle.journal import DurableJournal, MemoryJournal +from wolfharness.lifecycle.snapshot_store import ( DurableSnapshotStore, MemorySnapshotStore, ) if TYPE_CHECKING: - from agentpool.lifecycle.protocols import ( + from wolfharness.lifecycle.protocols import ( CommChannel, EventTransport, Journal, SnapshotStore, TriggerSource, ) - from agentpool_config.lifecycle import LifecycleConfig + from wolfharness_config.lifecycle import LifecycleConfig -_APP_NAME = "agentpool" -_APP_AUTHOR = "agentpool" +_APP_NAME = "wolfharness" +_APP_AUTHOR = "wolfharness" _STATE_DIR: Path = Path(user_state_dir(_APP_NAME, _APP_AUTHOR)) _LIFECYCLE_DB: Path = _STATE_DIR / "lifecycle.db" diff --git a/src/agentpool/lifecycle/journal.py b/src/wolfharness/lifecycle/journal.py similarity index 98% rename from src/agentpool/lifecycle/journal.py rename to src/wolfharness/lifecycle/journal.py index 97f6cbf10..00cbee719 100644 --- a/src/agentpool/lifecycle/journal.py +++ b/src/wolfharness/lifecycle/journal.py @@ -28,8 +28,8 @@ from sqlmodel import Field, SQLModel from sqlmodel.main import SQLModelConfig # type: ignore[attr-defined] -from agentpool.lifecycle.types import ResumeResult, ToolExecutionRecord -from agentpool.log import get_logger +from wolfharness.lifecycle.types import ResumeResult, ToolExecutionRecord +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -37,7 +37,7 @@ from sqlalchemy.engine import Engine - from agentpool.lifecycle.protocols import SnapshotStore + from wolfharness.lifecycle.protocols import SnapshotStore logger = get_logger(__name__) @@ -673,7 +673,7 @@ def close(self) -> None: # Register known event types for deserialization reconstruction. -from agentpool.agents.events.events import ( # noqa: E402 +from wolfharness.agents.events.events import ( # noqa: E402 MessageReplacementEvent, PlanUpdateEvent, RunErrorEvent, diff --git a/src/agentpool/lifecycle/protocols.py b/src/wolfharness/lifecycle/protocols.py similarity index 99% rename from src/agentpool/lifecycle/protocols.py rename to src/wolfharness/lifecycle/protocols.py index 805591336..29516ee90 100644 --- a/src/agentpool/lifecycle/protocols.py +++ b/src/wolfharness/lifecycle/protocols.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.lifecycle.types import ( + from wolfharness.lifecycle.types import ( EventEnvelope, Feedback, Prompt, diff --git a/src/agentpool/lifecycle/snapshot_store.py b/src/wolfharness/lifecycle/snapshot_store.py similarity index 99% rename from src/agentpool/lifecycle/snapshot_store.py rename to src/wolfharness/lifecycle/snapshot_store.py index ab8d45f0c..4446e66eb 100644 --- a/src/agentpool/lifecycle/snapshot_store.py +++ b/src/wolfharness/lifecycle/snapshot_store.py @@ -4,7 +4,7 @@ DurableSnapshotStore provides crash-safe SQL persistence with fsync on write. Both implementations conform to the ``SnapshotStore`` Protocol defined in -``agentpool.lifecycle.protocols``. +``wolfharness.lifecycle.protocols``. """ from __future__ import annotations @@ -23,7 +23,7 @@ import logfire -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) diff --git a/src/agentpool/lifecycle/triggers.py b/src/wolfharness/lifecycle/triggers.py similarity index 97% rename from src/agentpool/lifecycle/triggers.py rename to src/wolfharness/lifecycle/triggers.py index b24f1f061..beb2a36d5 100644 --- a/src/agentpool/lifecycle/triggers.py +++ b/src/wolfharness/lifecycle/triggers.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: - from agentpool.lifecycle.types import Prompt + from wolfharness.lifecycle.types import Prompt class ImmediateTrigger: @@ -60,7 +60,7 @@ def poll(self) -> Prompt | None: if self._delivered: return None self._delivered = True - from agentpool.lifecycle.types import Prompt + from wolfharness.lifecycle.types import Prompt return Prompt(content=self._prompt) @@ -100,7 +100,7 @@ async def deliver(self, content: str, priority: str = "normal") -> None: content: The prompt text to deliver. priority: Delivery priority (``"normal"`` or ``"asap"``). """ - from agentpool.lifecycle.types import Prompt + from wolfharness.lifecycle.types import Prompt prompt = Prompt(content=content, priority=priority) await self._queue.put(prompt) diff --git a/src/agentpool/lifecycle/types.py b/src/wolfharness/lifecycle/types.py similarity index 100% rename from src/agentpool/lifecycle/types.py rename to src/wolfharness/lifecycle/types.py diff --git a/src/agentpool/log.py b/src/wolfharness/log.py similarity index 97% rename from src/agentpool/log.py rename to src/wolfharness/log.py index b71395547..fda2f61d2 100644 --- a/src/agentpool/log.py +++ b/src/wolfharness/log.py @@ -1,4 +1,4 @@ -"""Logging configuration for agentpool with structlog support.""" +"""Logging configuration for wolfharness with structlog support.""" from __future__ import annotations @@ -203,7 +203,7 @@ def get_logger(name: str, log_level: LogLevel | None = None) -> structlog.stdlib """Get a structlog logger for the given name. Args: - name: The name of the logger, will be prefixed with 'agentpool.' + name: The name of the logger, will be prefixed with 'wolfharness.' log_level: The logging level to set for the logger Returns: @@ -228,13 +228,13 @@ def get_logger(name: str, log_level: LogLevel | None = None) -> structlog.stdlib cache_logger_on_first_use=True, ) - logger = structlog.get_logger(f"agentpool.{name}") + logger = structlog.get_logger(f"wolfharness.{name}") if log_level is not None: if isinstance(log_level, str): log_level = getattr(logging, log_level.upper()) assert log_level # Set level on underlying stdlib logger - stdlib_logger = logging.getLogger(f"agentpool.{name}") + stdlib_logger = logging.getLogger(f"wolfharness.{name}") stdlib_logger.setLevel(log_level) return logger # type: ignore[no-any-return] diff --git a/src/wolfharness/mcp_server/__init__.py b/src/wolfharness/mcp_server/__init__.py new file mode 100644 index 000000000..e63d9946e --- /dev/null +++ b/src/wolfharness/mcp_server/__init__.py @@ -0,0 +1,6 @@ +"""MCP server integration for AgentPool.""" + +from wolfharness.mcp_server.client import MCPClient +from wolfharness.mcp_server.tool_bridge import ToolManagerBridge + +__all__ = ["MCPClient", "ToolManagerBridge"] diff --git a/src/agentpool/mcp_server/client.py b/src/wolfharness/mcp_server/client.py similarity index 97% rename from src/agentpool/mcp_server/client.py rename to src/wolfharness/mcp_server/client.py index 00731a443..34582b639 100644 --- a/src/agentpool/mcp_server/client.py +++ b/src/wolfharness/mcp_server/client.py @@ -19,15 +19,15 @@ from pydantic_ai import BinaryContent, RunContext, ToolReturn from schemez import FunctionSchema -from agentpool.agents.context import AgentContext -from agentpool.log import get_logger -from agentpool.mcp_server.constants import MCP_TO_LOGGING -from agentpool.mcp_server.helpers import extract_text_content, mcp_tool_to_fn_schema -from agentpool.mcp_server.message_handler import MCPMessageHandler -from agentpool.tools import CallDeferred -from agentpool.tools.base import FunctionTool -from agentpool.utils.signatures import create_modified_signature -from agentpool_config.mcp_server import ( +from wolfharness.agents.context import AgentContext +from wolfharness.log import get_logger +from wolfharness.mcp_server.constants import MCP_TO_LOGGING +from wolfharness.mcp_server.helpers import extract_text_content, mcp_tool_to_fn_schema +from wolfharness.mcp_server.message_handler import MCPMessageHandler +from wolfharness.tools import CallDeferred +from wolfharness.tools.base import FunctionTool +from wolfharness.utils.signatures import create_modified_signature +from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, @@ -60,7 +60,7 @@ ) from upathtools.filesystems import MCPFileSystem, MCPToolsFileSystem - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness_config.mcp_server import MCPServerConfig logger = get_logger(__name__) @@ -299,7 +299,7 @@ def _get_client( icons = [Icon(src=self._client_icon_path)] client_info = Implementation( name=self._client_name, - version=version("agentpool"), + version=version("wolfharness"), title=self._client_title, websiteUrl=self._client_website_url, icons=icons, @@ -341,7 +341,7 @@ def _get_client_from_transport(self, transport: ClientTransport) -> fastmcp.Clie icons = [Icon(src=self._client_icon_path)] client_info = Implementation( name=self._client_name, - version=version("agentpool"), + version=version("wolfharness"), title=self._client_title, websiteUrl=self._client_website_url, icons=icons, @@ -570,7 +570,7 @@ async def call_tool( # noqa: PLR0915 agent_ctx: AgentContext[Any] | None = None, ) -> ToolReturn | str | Any: """Call an MCP tool with full PydanticAI return type support.""" - from agentpool.mcp_server.conversions import from_mcp_content + from wolfharness.mcp_server.conversions import from_mcp_content self._ensure_connected() # Create progress handler that bridges to AgentContext if available @@ -716,7 +716,7 @@ def _mcp_content_return_value( if __name__ == "__main__": - path = "/home/phil65/dev/oss/agentpool/tests/mcp_server/server.py" + path = "/home/phil65/dev/oss/wolfharness/tests/mcp_server/server.py" # path = Path(__file__).parent / "test_mcp_server.py" config = StdioMCPServerConfig(command="uv", args=["run", str(path)]) diff --git a/src/agentpool/mcp_server/config_snapshot.py b/src/wolfharness/mcp_server/config_snapshot.py similarity index 98% rename from src/agentpool/mcp_server/config_snapshot.py rename to src/wolfharness/mcp_server/config_snapshot.py index 8ae0fac0e..3daf49e5b 100644 --- a/src/agentpool/mcp_server/config_snapshot.py +++ b/src/wolfharness/mcp_server/config_snapshot.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: - from agentpool_config.mcp_server import BaseMCPServerConfig + from wolfharness_config.mcp_server import BaseMCPServerConfig SourceScope = Literal["pool", "agent", "session", "skill"] diff --git a/src/agentpool/mcp_server/constants.py b/src/wolfharness/mcp_server/constants.py similarity index 100% rename from src/agentpool/mcp_server/constants.py rename to src/wolfharness/mcp_server/constants.py diff --git a/src/agentpool/mcp_server/conversions.py b/src/wolfharness/mcp_server/conversions.py similarity index 98% rename from src/agentpool/mcp_server/conversions.py rename to src/wolfharness/mcp_server/conversions.py index 2af91b071..d5cceabb3 100644 --- a/src/agentpool/mcp_server/conversions.py +++ b/src/wolfharness/mcp_server/conversions.py @@ -14,8 +14,8 @@ UserPromptPart, ) -from agentpool.log import get_logger -from agentpool.utils.pydantic_ai_helpers import url_from_mime_type +from wolfharness.log import get_logger +from wolfharness.utils.pydantic_ai_helpers import url_from_mime_type if TYPE_CHECKING: diff --git a/src/agentpool/mcp_server/global_pool.py b/src/wolfharness/mcp_server/global_pool.py similarity index 98% rename from src/agentpool/mcp_server/global_pool.py rename to src/wolfharness/mcp_server/global_pool.py index 225f423d0..ff1098839 100644 --- a/src/agentpool/mcp_server/global_pool.py +++ b/src/wolfharness/mcp_server/global_pool.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool_config.mcp_server import BaseMCPServerConfig + from wolfharness_config.mcp_server import BaseMCPServerConfig logger = logging.getLogger(__name__) @@ -123,7 +123,7 @@ async def get_transport( RuntimeError: If the owner task fails to initialize the connection. TimeoutError: If the owner task does not become ready in time. """ - from agentpool_config.mcp_server import ( + from wolfharness_config.mcp_server import ( AcpMCPServerConfig, StdioMCPServerConfig, ) diff --git a/src/agentpool/mcp_server/helpers.py b/src/wolfharness/mcp_server/helpers.py similarity index 96% rename from src/agentpool/mcp_server/helpers.py rename to src/wolfharness/mcp_server/helpers.py index 88d3812b3..9f29eb960 100644 --- a/src/agentpool/mcp_server/helpers.py +++ b/src/wolfharness/mcp_server/helpers.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool/mcp_server/manager.py b/src/wolfharness/mcp_server/manager.py similarity index 97% rename from src/agentpool/mcp_server/manager.py rename to src/wolfharness/mcp_server/manager.py index 804e69663..dae61ed34 100644 --- a/src/agentpool/mcp_server/manager.py +++ b/src/wolfharness/mcp_server/manager.py @@ -13,12 +13,12 @@ import anyio from pydantic_ai.capabilities import AbstractCapability -from agentpool.capabilities.combined_toolset import CombinedToolsetCapability -from agentpool.capabilities.mcp_server_cap import McpServerCap -from agentpool.common_types import MCPServerStatus -from agentpool.log import get_logger -from agentpool.mcp_server.global_pool import GlobalConnectionPool -from agentpool_config.mcp_server import AcpMCPServerConfig, BaseMCPServerConfig +from wolfharness.capabilities.combined_toolset import CombinedToolsetCapability +from wolfharness.capabilities.mcp_server_cap import McpServerCap +from wolfharness.common_types import MCPServerStatus +from wolfharness.log import get_logger +from wolfharness.mcp_server.global_pool import GlobalConnectionPool +from wolfharness_config.mcp_server import AcpMCPServerConfig, BaseMCPServerConfig if TYPE_CHECKING: @@ -31,11 +31,11 @@ from mcp.types import ElicitRequestParams, SamplingMessage from pydantic_ai.capabilities import MCP - from agentpool.mcp_server.config_snapshot import McpConfigSnapshot - from agentpool.mcp_server.session_pool import SessionConnectionPool - from agentpool.ui.base import InputProvider - from agentpool_config.mcp_server import MCPServerConfig - from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager + from wolfharness.mcp_server.config_snapshot import McpConfigSnapshot + from wolfharness.mcp_server.session_pool import SessionConnectionPool + from wolfharness.ui.base import InputProvider + from wolfharness_config.mcp_server import MCPServerConfig + from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager logger = get_logger(__name__) @@ -256,7 +256,7 @@ def get_or_create_session(self, session_id: str) -> McpSessionContext: Returns: The ``McpSessionContext`` for this session. """ - from agentpool.mcp_server.session_pool import SessionConnectionPool + from wolfharness.mcp_server.session_pool import SessionConnectionPool ctx = self._session_contexts.get(session_id) if ctx is None: @@ -355,8 +355,8 @@ async def _sampling_callback( context: RequestContext[Any, Any, Any], ) -> str: """Handle MCP sampling by creating a new agent with specified preferences.""" - from agentpool.agents import Agent - from agentpool.mcp_server.conversions import sampling_messages_to_user_content + from wolfharness.agents import Agent + from wolfharness.mcp_server.conversions import sampling_messages_to_user_content prompts = sampling_messages_to_user_content(messages) model = self.sampling_model @@ -409,7 +409,7 @@ async def setup_server( ) return None - from agentpool.mcp_server.client import MCPClient + from wolfharness.mcp_server.client import MCPClient try: client = MCPClient( @@ -691,7 +691,7 @@ async def get_capabilities( # noqa: PLR0915 from pydantic_ai.capabilities import MCP from pydantic_ai.mcp import MCPToolset - from agentpool_config.mcp_server import ( + from wolfharness_config.mcp_server import ( SSEMCPServerConfig, StdioMCPServerConfig, StreamableHTTPMCPServerConfig, diff --git a/src/agentpool/mcp_server/message_handler.py b/src/wolfharness/mcp_server/message_handler.py similarity index 96% rename from src/agentpool/mcp_server/message_handler.py rename to src/wolfharness/mcp_server/message_handler.py index 1564d29eb..7b085caca 100644 --- a/src/agentpool/mcp_server/message_handler.py +++ b/src/wolfharness/mcp_server/message_handler.py @@ -1,11 +1,11 @@ -"""FastMCP message handler for agentpool.""" +"""FastMCP message handler for wolfharness.""" from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -14,14 +14,14 @@ from mcp.shared.session import RequestResponder import mcp.types - from agentpool.mcp_server import MCPClient + from wolfharness.mcp_server import MCPClient logger = get_logger(__name__) @dataclass class MCPMessageHandler: - """Custom message handler that bridges FastMCP to agentpool notifications.""" + """Custom message handler that bridges FastMCP to wolfharness notifications.""" client: MCPClient """The MCP client instance.""" diff --git a/src/agentpool/mcp_server/registries/__init__.py b/src/wolfharness/mcp_server/registries/__init__.py similarity index 100% rename from src/agentpool/mcp_server/registries/__init__.py rename to src/wolfharness/mcp_server/registries/__init__.py diff --git a/src/agentpool/mcp_server/registries/official_registry_client.py b/src/wolfharness/mcp_server/registries/official_registry_client.py similarity index 100% rename from src/agentpool/mcp_server/registries/official_registry_client.py rename to src/wolfharness/mcp_server/registries/official_registry_client.py diff --git a/src/agentpool/mcp_server/registries/pulsemcp_client.py b/src/wolfharness/mcp_server/registries/pulsemcp_client.py similarity index 100% rename from src/agentpool/mcp_server/registries/pulsemcp_client.py rename to src/wolfharness/mcp_server/registries/pulsemcp_client.py diff --git a/src/agentpool/mcp_server/session_pool.py b/src/wolfharness/mcp_server/session_pool.py similarity index 96% rename from src/agentpool/mcp_server/session_pool.py rename to src/wolfharness/mcp_server/session_pool.py index bb21c6f54..e68fb5730 100644 --- a/src/agentpool/mcp_server/session_pool.py +++ b/src/wolfharness/mcp_server/session_pool.py @@ -24,14 +24,14 @@ import threading from typing import TYPE_CHECKING -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: from fastmcp.client import ClientTransport - from agentpool.mcp_server.client import MCPClient - from agentpool_config.mcp_server import BaseMCPServerConfig, MCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness_config.mcp_server import BaseMCPServerConfig, MCPServerConfig logger = get_logger(__name__) @@ -78,7 +78,7 @@ def _create_transport(config: BaseMCPServerConfig) -> ClientTransport: from fastmcp.client import SSETransport, StreamableHttpTransport from fastmcp.client.transports import StdioTransport - from agentpool_config.mcp_server import ( + from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, @@ -90,7 +90,7 @@ def _create_transport(config: BaseMCPServerConfig) -> ClientTransport: env = config.get_env_vars() return StdioTransport(command=command, args=args, env=env) case SSEMCPServerConfig(url=url, headers=headers): - from agentpool_config.mcp_server import make_mcp_httpx_client_factory + from wolfharness_config.mcp_server import make_mcp_httpx_client_factory return SSETransport( url=str(url), @@ -98,7 +98,7 @@ def _create_transport(config: BaseMCPServerConfig) -> ClientTransport: httpx_client_factory=make_mcp_httpx_client_factory(read_timeout=config.timeout), ) case StreamableHTTPMCPServerConfig(url=url, headers=headers): - from agentpool_config.mcp_server import make_mcp_httpx_client_factory + from wolfharness_config.mcp_server import make_mcp_httpx_client_factory return StreamableHttpTransport( url=str(url), @@ -227,7 +227,7 @@ async def get_client( Returns: A connected ``MCPClient`` wrapping the pooled transport. """ - from agentpool.mcp_server.client import MCPClient + from wolfharness.mcp_server.client import MCPClient transport = await self.get_transport(config, skill_name) client = MCPClient(config=config, transport=transport) diff --git a/src/agentpool/mcp_server/session_stream_pair.py b/src/wolfharness/mcp_server/session_stream_pair.py similarity index 100% rename from src/agentpool/mcp_server/session_stream_pair.py rename to src/wolfharness/mcp_server/session_stream_pair.py diff --git a/src/agentpool/mcp_server/tool_bridge.py b/src/wolfharness/mcp_server/tool_bridge.py similarity index 96% rename from src/agentpool/mcp_server/tool_bridge.py rename to src/wolfharness/mcp_server/tool_bridge.py index dda58ee0c..65e82b709 100644 --- a/src/agentpool/mcp_server/tool_bridge.py +++ b/src/wolfharness/mcp_server/tool_bridge.py @@ -25,10 +25,10 @@ from pydantic_ai.models.test import TestModel from pydantic_ai.usage import RunUsage -from agentpool.agents import Agent -from agentpool.capabilities.change_event import ChangeEvent -from agentpool.log import get_logger -from agentpool.utils.signatures import filter_schema_params, get_params_matching_predicate +from wolfharness.agents import Agent +from wolfharness.capabilities.change_event import ChangeEvent +from wolfharness.log import get_logger +from wolfharness.utils.signatures import filter_schema_params, get_params_matching_predicate if TYPE_CHECKING: @@ -39,9 +39,9 @@ from pydantic_ai.messages import UserContent from uvicorn import Server - from agentpool.agents import AgentContext - from agentpool.agents.base_agent import BaseAgent - from agentpool.tools.base import Tool + from wolfharness.agents import AgentContext + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.tools.base import Tool _ = ChangeEvent # Used at runtime in method signature @@ -132,7 +132,7 @@ def _convert_to_tool_result(result: Any) -> FastMCPToolResult: """ from fastmcp.tools.tool import ToolResult as FastMCPToolResult - from agentpool.tools.base import ToolResult as AgentPoolToolResult + from wolfharness.tools.base import ToolResult as AgentPoolToolResult match result: case FastMCPToolResult(): @@ -169,7 +169,7 @@ def _append_injection_to_result(result: Any, injection: str) -> Any: - dict: Add 'injected_context' key - Other: Wrap in dict with original and injection """ - from agentpool.tools.base import ToolResult as AgentPoolToolResult + from wolfharness.tools.base import ToolResult as AgentPoolToolResult match result: case str(): @@ -300,7 +300,7 @@ async def stop(self) -> None: def _subscribe_to_tool_changes(self) -> None: """Subscribe to tool changes from all observable capabilities.""" - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel node = self.node host_ctx = node.host_context @@ -328,7 +328,7 @@ def _subscribe_to_tool_changes(self) -> None: def _unsubscribe_from_tool_changes(self) -> None: """Disconnect from tool change signals on all observable capabilities.""" - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel node = self.node host_ctx = node.host_context @@ -416,7 +416,7 @@ def url(self) -> str: @property def resolved_server_name(self) -> str: """Get the server name.""" - return self.server_name or f"agentpool-{self.node.name}-tools" + return self.server_name or f"wolfharness-{self.node.name}-tools" async def _register_tools(self) -> None: """Register all node tools with the FastMCP server.""" @@ -436,7 +436,7 @@ def _register_single_tool(self, tool: Tool) -> None: from fastmcp.tools import Tool as FastMCPTool class _BridgeTool(FastMCPTool): - """Custom FastMCP Tool that wraps a agentpool Tool. + """Custom FastMCP Tool that wraps a wolfharness Tool. This allows us to use our own schema and invoke tools with AgentContext. """ @@ -467,7 +467,7 @@ async def run(self, arguments: dict[str, Any]) -> FastMCPToolResult: """Execute the wrapped tool with context bridging.""" from fastmcp.server.dependencies import get_context - from agentpool.tools.base import ToolResult as AgentPoolToolResult + from wolfharness.tools.base import ToolResult as AgentPoolToolResult args = arguments.copy() # Validate args against tool's schema @@ -552,7 +552,7 @@ async def invoke_tool_with_context( Handles tools that expect AgentContext, RunContext, or neither. Runs pre/post tool hooks if configured on the node. """ - from agentpool.tasks import ToolSkippedError + from wolfharness.tasks import ToolSkippedError if hooks := self.node.hooks: pre_result = await hooks.run_pre_tool_hooks( diff --git a/src/agentpool/messaging/__init__.py b/src/wolfharness/messaging/__init__.py similarity index 69% rename from src/agentpool/messaging/__init__.py rename to src/wolfharness/messaging/__init__.py index ae08cbd15..3c1aeb041 100644 --- a/src/agentpool/messaging/__init__.py +++ b/src/wolfharness/messaging/__init__.py @@ -1,12 +1,12 @@ """Core messsaging classes for AgentPool.""" -from agentpool.messaging.chat_filesystem import ChatMessageFileSystem -from agentpool.messaging.messages import ChatMessage, TokenCost, AgentResponse, TeamResponse -from agentpool.messaging.message_container import ChatMessageList -from agentpool.messaging.event_manager import EventManager -from agentpool.messaging.messagenode import MessageNode, SourceType, get_source_type -from agentpool.messaging.message_history import MessageHistory -from agentpool.messaging.compaction import ( +from wolfharness.messaging.chat_filesystem import ChatMessageFileSystem +from wolfharness.messaging.messages import ChatMessage, TokenCost, AgentResponse, TeamResponse +from wolfharness.messaging.message_container import ChatMessageList +from wolfharness.messaging.event_manager import EventManager +from wolfharness.messaging.messagenode import MessageNode, SourceType, get_source_type +from wolfharness.messaging.message_history import MessageHistory +from wolfharness.messaging.compaction import ( CompactionPipeline, CompactionStep, FilterBinaryContent, diff --git a/src/agentpool/messaging/chat_filesystem.py b/src/wolfharness/messaging/chat_filesystem.py similarity index 99% rename from src/agentpool/messaging/chat_filesystem.py rename to src/wolfharness/messaging/chat_filesystem.py index 71b4fab84..2106bbeee 100644 --- a/src/agentpool/messaging/chat_filesystem.py +++ b/src/wolfharness/messaging/chat_filesystem.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: - from agentpool.messaging.message_container import ChatMessageList + from wolfharness.messaging.message_container import ChatMessageList class ChatMessageFileSystem(AsyncFileSystem): # type: ignore[misc] diff --git a/src/agentpool/messaging/compaction.py b/src/wolfharness/messaging/compaction.py similarity index 98% rename from src/agentpool/messaging/compaction.py rename to src/wolfharness/messaging/compaction.py index a99e5a8da..496f1f4a3 100644 --- a/src/agentpool/messaging/compaction.py +++ b/src/wolfharness/messaging/compaction.py @@ -6,7 +6,7 @@ Example: ```python - from agentpool.messaging.compaction import ( + from wolfharness.messaging.compaction import ( CompactionPipeline, FilterThinking, TruncateToolOutputs, @@ -75,7 +75,7 @@ from pydantic_ai import ModelRequestPart, ModelResponsePart - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory # Type aliases ModelMessage = ModelRequest | ModelResponse @@ -658,7 +658,7 @@ async def compact_conversation( Returns: Tuple of (original_count, compacted_count) of model messages """ - from agentpool.messaging.messages import ChatMessage + from wolfharness.messaging.messages import ChatMessage chat_messages = conversation.get_history() if not chat_messages: @@ -713,7 +713,7 @@ def _extract_text_content(msg: ModelMessage) -> str: ): parts_text.append(str(content)) case UserPromptPart(content=list() as content): - from agentpool.agents.native_agent.helpers import _summarize_content_block + from wolfharness.agents.native_agent.helpers import _summarize_content_block for item in content: parts_text.append(_summarize_content_block(item)) # noqa: PERF401 diff --git a/src/agentpool/messaging/connection_manager.py b/src/wolfharness/messaging/connection_manager.py similarity index 96% rename from src/agentpool/messaging/connection_manager.py rename to src/wolfharness/messaging/connection_manager.py index 79c17a49c..2eeadba8f 100644 --- a/src/agentpool/messaging/connection_manager.py +++ b/src/wolfharness/messaging/connection_manager.py @@ -9,18 +9,18 @@ from anyenv.signals import Signal -from agentpool.log import get_logger -from agentpool.talk import AggregatedTalkStats, Talk, TeamTalk -from agentpool.utils.inspection import get_fn_name +from wolfharness.log import get_logger +from wolfharness.talk import AggregatedTalkStats, Talk, TeamTalk +from wolfharness.utils.inspection import get_fn_name if TYPE_CHECKING: from collections.abc import AsyncIterator from datetime import timedelta - from agentpool.common_types import AgentName, AnyTransformFn, AsyncFilterFn, QueueStrategy - from agentpool.messaging import ChatMessage, MessageNode - from agentpool_config.forward_targets import ConnectionType + from wolfharness.common_types import AgentName, AnyTransformFn, AsyncFilterFn, QueueStrategy + from wolfharness.messaging import ChatMessage, MessageNode + from wolfharness_config.forward_targets import ConnectionType logger = get_logger(__name__) @@ -295,7 +295,7 @@ def get_mermaid_diagram( if __name__ == "__main__": - from agentpool.agents import Agent + from wolfharness.agents import Agent agent = Agent("test_agent", model="openai:gpt-5-nano") agent_2 = Agent("test_agent_2", model="openai:gpt-5-nano") diff --git a/src/agentpool/messaging/context.py b/src/wolfharness/messaging/context.py similarity index 85% rename from src/agentpool/messaging/context.py rename to src/wolfharness/messaging/context.py index 80511bdaf..dfc2830fa 100644 --- a/src/agentpool/messaging/context.py +++ b/src/wolfharness/messaging/context.py @@ -7,11 +7,11 @@ if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.agents.base_agent import BaseAgent - from agentpool.messaging import MessageNode - from agentpool.prompts.manager import PromptManager - from agentpool.ui.base import InputProvider + from wolfharness import AgentPool + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.messaging import MessageNode + from wolfharness.prompts.manager import PromptManager + from wolfharness.ui.base import InputProvider @dataclass(kw_only=True) @@ -38,7 +38,7 @@ def node_name(self) -> str: @property def agent(self) -> BaseAgent[TDeps, Any]: """Return agent node, type-narrowed to BaseAgent.""" - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent assert isinstance(self.node, BaseAgent) return self.node # ty: ignore[invalid-return-type] @@ -48,7 +48,7 @@ def get_input_provider(self) -> InputProvider: if self.input_provider: return self.input_provider # 2. Session-bound provider (authoritative for pooled sessions) - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext if isinstance(self, AgentContext): session_state = self.get_session_state() @@ -63,7 +63,7 @@ def get_input_provider(self) -> InputProvider: # turn. This catches cases where session.input_provider was not set # (e.g., run_stream path) or the agent was cached before the provider # was available. - from agentpool.mcp_server.manager import _current_input_provider + from wolfharness.mcp_server.manager import _current_input_provider contextvar_provider = _current_input_provider.get() if contextvar_provider is not None: diff --git a/src/agentpool/messaging/event_manager.py b/src/wolfharness/messaging/event_manager.py similarity index 98% rename from src/agentpool/messaging/event_manager.py rename to src/wolfharness/messaging/event_manager.py index bb2d6bbf0..9598e992f 100644 --- a/src/agentpool/messaging/event_manager.py +++ b/src/wolfharness/messaging/event_manager.py @@ -14,9 +14,9 @@ from evented_config import EmailConfig, FileWatchConfig, TimeEventConfig, WebhookConfig from pydantic import SecretStr -from agentpool.log import get_logger -from agentpool.utils.inspection import execute, get_fn_name -from agentpool.utils.time_utils import get_now +from wolfharness.log import get_logger +from wolfharness.utils.inspection import execute, get_fn_name +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: @@ -28,7 +28,7 @@ from evented.timed_watcher import TimeEventSource from evented_config import EventConfig - from agentpool.agents.events import RichAgentStreamEvent, SubAgentEvent + from wolfharness.agents.events import RichAgentStreamEvent, SubAgentEvent logger = get_logger(__name__) @@ -106,7 +106,7 @@ async def emit_agent_event( event: The agent stream event to emit source_session_id: Optional ID of the session that produced the event """ - from agentpool.agents.events import SubAgentEvent + from wolfharness.agents.events import SubAgentEvent if not self.enabled: return diff --git a/src/agentpool/messaging/events.py b/src/wolfharness/messaging/events.py similarity index 86% rename from src/agentpool/messaging/events.py rename to src/wolfharness/messaging/events.py index ac81bbc53..20332fbd0 100644 --- a/src/agentpool/messaging/events.py +++ b/src/wolfharness/messaging/events.py @@ -8,9 +8,9 @@ if TYPE_CHECKING: - from agentpool.messaging import ChatMessage - from agentpool.talk.talk import Talk - from agentpool_config.events import ConnectionEventType + from wolfharness.messaging import ChatMessage + from wolfharness.talk.talk import Talk + from wolfharness_config.events import ConnectionEventType ChangeType = Literal["added", "modified", "deleted"] diff --git a/src/agentpool/messaging/graph_adapter.py b/src/wolfharness/messaging/graph_adapter.py similarity index 96% rename from src/agentpool/messaging/graph_adapter.py rename to src/wolfharness/messaging/graph_adapter.py index 26164a44d..dfc167502 100644 --- a/src/agentpool/messaging/graph_adapter.py +++ b/src/wolfharness/messaging/graph_adapter.py @@ -14,8 +14,8 @@ if TYPE_CHECKING: from pydantic_graph.graph_builder import Graph - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.messaging import ChatMessage + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.messaging import ChatMessage @dataclass diff --git a/src/agentpool/messaging/message_container.py b/src/wolfharness/messaging/message_container.py similarity index 92% rename from src/agentpool/messaging/message_container.py rename to src/wolfharness/messaging/message_container.py index 47cde3bea..deeebbf39 100644 --- a/src/agentpool/messaging/message_container.py +++ b/src/wolfharness/messaging/message_container.py @@ -7,18 +7,18 @@ from psygnal.containers import EventedList -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.messaging.chat_filesystem import ChatMessageFileSystem -from agentpool.utils.count_tokens import batch_count_tokens +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.messaging.chat_filesystem import ChatMessageFileSystem +from wolfharness.utils.count_tokens import batch_count_tokens if TYPE_CHECKING: from datetime import datetime - from agentpool.common_types import MessageRole - from agentpool.messaging.messages import FormatStyle - from agentpool.utils.dag import DAGNode + from wolfharness.common_types import MessageRole + from wolfharness.messaging.messages import FormatStyle + from wolfharness.utils.dag import DAGNode logger = get_logger(__name__) @@ -152,7 +152,7 @@ def to_mermaid_graph( rankdir: Literal["TB", "BT", "LR", "RL"] = "LR", ) -> str: """Convert message flow to mermaid graph.""" - from agentpool.utils.dag import dag_to_list + from wolfharness.utils.dag import dag_to_list dag = self._build_flow_dag(message) if not dag: diff --git a/src/agentpool/messaging/message_history.py b/src/wolfharness/messaging/message_history.py similarity index 93% rename from src/agentpool/messaging/message_history.py rename to src/wolfharness/messaging/message_history.py index d94d359de..412f01b25 100644 --- a/src/agentpool/messaging/message_history.py +++ b/src/wolfharness/messaging/message_history.py @@ -9,9 +9,9 @@ from upathtools import read_path, to_upath -from agentpool.log import get_logger -from agentpool.storage import StorageManager -from agentpool_config.session import SessionQuery +from wolfharness.log import get_logger +from wolfharness.storage import StorageManager +from wolfharness_config.session import SessionQuery if TYPE_CHECKING: @@ -22,10 +22,10 @@ from toprompt import AnyPromptType from upathtools import JoinablePathLike - from agentpool.messaging import ChatMessage - from agentpool.prompts.conversion_manager import ConversionManager - from agentpool.prompts.prompts import PromptType - from agentpool_config.session import MemoryConfig + from wolfharness.messaging import ChatMessage + from wolfharness.prompts.conversion_manager import ConversionManager + from wolfharness.prompts.prompts import PromptType + from wolfharness_config.session import MemoryConfig logger = get_logger(__name__) @@ -51,9 +51,9 @@ def __init__( session_config: Optional MemoryConfig resources: Optional paths to load as context """ - from agentpool.messaging import ChatMessageList - from agentpool.prompts.conversion_manager import ConversionManager - from agentpool_config.storage import MemoryStorageConfig, StorageConfig + from wolfharness.messaging import ChatMessageList + from wolfharness.prompts.conversion_manager import ConversionManager + from wolfharness_config.storage import MemoryStorageConfig, StorageConfig storage_cfg = StorageConfig(providers=[MemoryStorageConfig()]) self._storage = storage or StorageManager(config=storage_cfg) @@ -160,7 +160,7 @@ async def format_history( async def load_context_source(self, source: PromptType | str) -> None: """Load context from a single source.""" - from agentpool.prompts.prompts import BasePrompt + from wolfharness.prompts.prompts import BasePrompt try: match source: @@ -193,7 +193,7 @@ def set_history(self, history: list[ChatMessage[Any]]) -> None: async def clear(self) -> None: """Clear conversation history and prompts.""" - from agentpool.messaging import ChatMessageList + from wolfharness.messaging import ChatMessageList self.chat_messages = ChatMessageList() self._last_messages = [] @@ -215,7 +215,7 @@ async def temporary_state( """ from toprompt import to_prompt - from agentpool.messaging import ChatMessage, ChatMessageList + from wolfharness.messaging import ChatMessage, ChatMessageList old_history = self.chat_messages.copy() messages: Sequence[ChatMessage[Any]] = ChatMessageList() @@ -338,7 +338,7 @@ def get_last_message_id(self) -> str | None: if __name__ == "__main__": import anyio - from agentpool import Agent + from wolfharness import Agent async def main() -> None: async with Agent(model="openai:gpt-5-nano") as agent: diff --git a/src/agentpool/messaging/message_utils.py b/src/wolfharness/messaging/message_utils.py similarity index 94% rename from src/agentpool/messaging/message_utils.py rename to src/wolfharness/messaging/message_utils.py index 8e6599da7..fc498bba8 100644 --- a/src/agentpool/messaging/message_utils.py +++ b/src/wolfharness/messaging/message_utils.py @@ -6,8 +6,8 @@ if TYPE_CHECKING: - from agentpool.agents.base_agent import BaseAgent - from agentpool.messaging import ChatMessage + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.messaging import ChatMessage def build_message_index( diff --git a/src/agentpool/messaging/messagenode.py b/src/wolfharness/messaging/messagenode.py similarity index 94% rename from src/agentpool/messaging/messagenode.py rename to src/wolfharness/messaging/messagenode.py index a2154c07e..1c4e2c435 100644 --- a/src/agentpool/messaging/messagenode.py +++ b/src/wolfharness/messaging/messagenode.py @@ -10,9 +10,9 @@ from anyenv.signals import Signal -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.talk import AggregatedTalkStats +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.talk import AggregatedTalkStats if TYPE_CHECKING: @@ -23,23 +23,23 @@ from evented.event_data import EventData from evented_config import EventConfig - from agentpool.agents.events import RichAgentStreamEvent - from agentpool.common_types import ( + from wolfharness.agents.events import RichAgentStreamEvent + from wolfharness.common_types import ( AnyTransformFn, AsyncFilterFn, ProcessorCallback, QueueStrategy, ) - from agentpool.delegation import AgentPool - from agentpool.host.context import HostContext - from agentpool.messaging.context import NodeContext - from agentpool.storage import StorageManager - from agentpool.talk import Talk, TeamTalk - from agentpool.talk.stats import AggregatedMessageStats, MessageStats - from agentpool.tools.base import FunctionTool - from agentpool.ui.base import InputProvider - from agentpool_config.forward_targets import ConnectionType - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness.delegation import AgentPool + from wolfharness.host.context import HostContext + from wolfharness.messaging.context import NodeContext + from wolfharness.storage import StorageManager + from wolfharness.talk import Talk, TeamTalk + from wolfharness.talk.stats import AggregatedMessageStats, MessageStats + from wolfharness.tools.base import FunctionTool + from wolfharness.ui.base import InputProvider + from wolfharness_config.forward_targets import ConnectionType + from wolfharness_config.mcp_server import MCPServerConfig logger = get_logger(__name__) @@ -67,7 +67,7 @@ def get_source_type(node: MessageNode[Any, Any]) -> SourceType: Returns: The corresponding :data:`SourceType` value. """ - from agentpool.delegation.base_team import BaseTeam + from wolfharness.delegation.base_team import BaseTeam if isinstance(node, BaseTeam): return "team_parallel" if node.mode == "parallel" else "team_sequential" @@ -99,9 +99,9 @@ def __init__( """Initialize message node.""" super().__init__() - from agentpool.mcp_server.manager import MCPManager - from agentpool.messaging import EventManager - from agentpool.messaging.connection_manager import ConnectionManager + from wolfharness.mcp_server.manager import MCPManager + from wolfharness.messaging import EventManager + from wolfharness.messaging.connection_manager import ConnectionManager async def _event_handler(event: EventData) -> None: if prompt := event.to_prompt(): @@ -373,7 +373,7 @@ def to_tool( Returns: Tool instance that can be registered """ - from agentpool.tools.base import FunctionTool + from wolfharness.tools.base import FunctionTool async def wrapped(prompt: str) -> TResult: result = await self.run(prompt) @@ -500,8 +500,8 @@ def connect_to( ) -> Talk[Any] | TeamTalk: """Create connection(s) to target(s).""" # Handle callable case - from agentpool.agents import Agent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents import Agent + from wolfharness.delegation.base_team import BaseTeam if callable(target): target = Agent.from_callback(target) @@ -564,7 +564,7 @@ def _build_single_node_graph( Returns: An immutable Graph ready for execution. """ - from agentpool.messaging.graph_adapter import MessageNodeStep + from wolfharness.messaging.graph_adapter import MessageNodeStep return MessageNodeStep(self).build_single_node_graph() @@ -609,8 +609,8 @@ async def run(self, *prompts: Any, **kwargs: Any) -> ChatMessage[TResult]: """ from pydantic_graph.id_types import NodeID - from agentpool.messaging.graph_adapter import AgentPoolState - from agentpool.messaging.signal_adapter import SignalEmittingGraphRun + from wolfharness.messaging.graph_adapter import AgentPoolState + from wolfharness.messaging.signal_adapter import SignalEmittingGraphRun graph = self._build_single_node_graph() state = AgentPoolState(node=self, prompts=prompts, kwargs=kwargs) @@ -647,9 +647,9 @@ async def run_stream( """ from pydantic_graph.id_types import NodeID - from agentpool.agents.events import RunErrorEvent, StreamCompleteEvent - from agentpool.messaging.graph_adapter import AgentPoolState - from agentpool.messaging.signal_adapter import SignalEmittingGraphRun + from wolfharness.agents.events import RunErrorEvent, StreamCompleteEvent + from wolfharness.messaging.graph_adapter import AgentPoolState + from wolfharness.messaging.signal_adapter import SignalEmittingGraphRun graph = self._build_single_node_graph() state = AgentPoolState(node=self, prompts=prompts, kwargs=kwargs) @@ -721,7 +721,7 @@ async def get_message_history( Returns: List of chat messages from the session. """ - from agentpool_config.session import SessionQuery + from wolfharness_config.session import SessionQuery if not self.enable_db_logging or not self.storage or not session_id: return [] diff --git a/src/agentpool/messaging/messages.py b/src/wolfharness/messaging/messages.py similarity index 97% rename from src/agentpool/messaging/messages.py rename to src/wolfharness/messaging/messages.py index 820ddd892..d03e56048 100644 --- a/src/agentpool/messaging/messages.py +++ b/src/wolfharness/messaging/messages.py @@ -28,11 +28,11 @@ ) import tokonomics -from agentpool.common_types import MessageRole, SimpleJsonType # noqa: TC001 -from agentpool.log import get_logger -from agentpool.utils.inspection import dataclasses_no_defaults_repr -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict -from agentpool.utils.time_utils import get_now +from wolfharness.common_types import MessageRole, SimpleJsonType # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.utils.inspection import dataclasses_no_defaults_repr +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: @@ -47,7 +47,7 @@ RunUsage, ) - from agentpool.tools.tool_call_info import ToolCallInfo + from wolfharness.tools.tool_call_info import ToolCallInfo TContent = TypeVar("TContent", str, BaseModel) @@ -503,7 +503,7 @@ def get_tool_calls( tools: Original Tool set to enrich ToolCallInfos with additional info agent_name: Name of the caller """ - from agentpool.tools import ToolCallInfo + from wolfharness.tools import ToolCallInfo tools = tools or {} parts = [part for message in self.messages for part in message.parts] @@ -579,7 +579,7 @@ def format( def get_token_count(self) -> int: """Get token count, either from token usage or cost data.""" - from agentpool.utils.count_tokens import count_tokens + from wolfharness.utils.count_tokens import count_tokens if info := self.cost_info: return info.token_usage.total_tokens diff --git a/src/agentpool/messaging/processing.py b/src/wolfharness/messaging/processing.py similarity index 86% rename from src/agentpool/messaging/processing.py rename to src/wolfharness/messaging/processing.py index 1837bccc6..74dd5de66 100644 --- a/src/agentpool/messaging/processing.py +++ b/src/wolfharness/messaging/processing.py @@ -4,16 +4,16 @@ from typing import TYPE_CHECKING, Any -from agentpool.messaging import ChatMessage -from agentpool.prompts.convert import convert_prompts +from wolfharness.messaging import ChatMessage +from wolfharness.prompts.convert import convert_prompts if TYPE_CHECKING: from pydantic_ai import UserContent - from agentpool.common_types import PromptCompatible - from agentpool.messaging import MessageNode - from agentpool.messaging.connection_manager import ConnectionManager + from wolfharness.common_types import PromptCompatible + from wolfharness.messaging import MessageNode + from wolfharness.messaging.connection_manager import ConnectionManager async def prepare_prompts( diff --git a/src/agentpool/messaging/signal_adapter.py b/src/wolfharness/messaging/signal_adapter.py similarity index 97% rename from src/agentpool/messaging/signal_adapter.py rename to src/wolfharness/messaging/signal_adapter.py index 23aeef66d..3c0ac7302 100644 --- a/src/agentpool/messaging/signal_adapter.py +++ b/src/wolfharness/messaging/signal_adapter.py @@ -16,15 +16,15 @@ import logfire from pydantic_graph.graph_builder import EndMarker, GraphRun, GraphTask -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage if TYPE_CHECKING: from pydantic_graph.id_types import NodeID - from agentpool.messaging.messagenode import MessageNode - from agentpool.talk import Talk + from wolfharness.messaging.messagenode import MessageNode + from wolfharness.talk import Talk logger = get_logger(__name__) @@ -151,7 +151,7 @@ async def _emit_tasks_completed(self, tasks: Sequence[GraphTask]) -> None: actual output of the node execution rather than the (often ``None``) task inputs. """ - from agentpool.messaging.graph_adapter import AgentPoolState + from wolfharness.messaging.graph_adapter import AgentPoolState for task in tasks: node = self._node_mapping.get(task.node_id) diff --git a/src/agentpool/messaging/streaming_adapter.py b/src/wolfharness/messaging/streaming_adapter.py similarity index 98% rename from src/agentpool/messaging/streaming_adapter.py rename to src/wolfharness/messaging/streaming_adapter.py index 7ba6b5fc7..f6ee2e888 100644 --- a/src/agentpool/messaging/streaming_adapter.py +++ b/src/wolfharness/messaging/streaming_adapter.py @@ -38,7 +38,7 @@ from pydantic_graph.graph_builder import EndMarker, ErrorMarker, GraphRun -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent, PartStartEvent, RichAgentStreamEvent, @@ -49,8 +49,8 @@ ToolCallCompleteEvent, ToolCallStartEvent, ) -from agentpool.log import get_logger -from agentpool.messaging.messages import ChatMessage +from wolfharness.log import get_logger +from wolfharness.messaging.messages import ChatMessage logger = get_logger(__name__) diff --git a/src/agentpool/mime_utils.py b/src/wolfharness/mime_utils.py similarity index 98% rename from src/agentpool/mime_utils.py rename to src/wolfharness/mime_utils.py index 160efd8fd..6297b3359 100644 --- a/src/agentpool/mime_utils.py +++ b/src/wolfharness/mime_utils.py @@ -1,4 +1,4 @@ -"""Centralized MIME type utilities for agentpool. +"""Centralized MIME type utilities for wolfharness. Uses Python's stdlib mimetypes for cross-platform coverage of file types. """ diff --git a/src/wolfharness/models/__init__.py b/src/wolfharness/models/__init__.py new file mode 100644 index 000000000..d10908091 --- /dev/null +++ b/src/wolfharness/models/__init__.py @@ -0,0 +1,20 @@ +"""Core data models for AgentPool.""" + +from __future__ import annotations + +from wolfharness.models.acp_agents import ACPAgentConfig, ACPAgentConfigTypes, BaseACPAgentConfig +from wolfharness.models.agents import AnyToolConfig, NativeAgentConfig # noqa: F401 +from wolfharness.models.manifest import AgentsManifest, AnyAgentConfig +from wolfharness.models.pending_interaction import PendingPermission, PendingQuestion + + +__all__ = [ + "ACPAgentConfig", + "ACPAgentConfigTypes", + "AgentsManifest", + "AnyAgentConfig", + "BaseACPAgentConfig", + "NativeAgentConfig", + "PendingPermission", + "PendingQuestion", +] diff --git a/src/agentpool/models/acp_agents/__init__.py b/src/wolfharness/models/acp_agents/__init__.py similarity index 91% rename from src/agentpool/models/acp_agents/__init__.py rename to src/wolfharness/models/acp_agents/__init__.py index a96eb4175..a959bfe09 100644 --- a/src/agentpool/models/acp_agents/__init__.py +++ b/src/wolfharness/models/acp_agents/__init__.py @@ -6,7 +6,7 @@ from pydantic import Field -from agentpool.models.acp_agents.base import ( +from wolfharness.models.acp_agents.base import ( ACPAgentConfig, BaseACPAgentConfig, RegistryACPAgentConfig, diff --git a/src/agentpool/models/acp_agents/base.py b/src/wolfharness/models/acp_agents/base.py similarity index 93% rename from src/agentpool/models/acp_agents/base.py rename to src/wolfharness/models/acp_agents/base.py index 43696abf4..be505dc08 100644 --- a/src/agentpool/models/acp_agents/base.py +++ b/src/wolfharness/models/acp_agents/base.py @@ -12,20 +12,20 @@ ) from pydantic import ConfigDict, Field -from agentpool.models.fields import EnvVarsField # noqa: TC001 -from agentpool_config import AnyToolConfig, BaseToolConfig -from agentpool_config.nodes import BaseAgentConfig -from agentpool_config.toolsets import BaseToolsetConfig +from wolfharness.models.fields import EnvVarsField # noqa: TC001 +from wolfharness_config import AnyToolConfig, BaseToolConfig +from wolfharness_config.nodes import BaseAgentConfig +from wolfharness_config.toolsets import BaseToolsetConfig if TYPE_CHECKING: from exxec import ExecutionEnvironment from pydantic_ai.capabilities import AbstractCapability - from agentpool.agents.acp_agent import ACPAgent - from agentpool.common_types import AnyEventHandlerType - from agentpool.delegation import AgentPool - from agentpool.ui.base import InputProvider + from wolfharness.agents.acp_agent import ACPAgent + from wolfharness.common_types import AnyEventHandlerType + from wolfharness.delegation import AgentPool + from wolfharness.ui.base import InputProvider class BaseACPAgentConfig(BaseAgentConfig): @@ -143,8 +143,8 @@ def get_registry_id(self) -> str | None: def get_tool_providers(self) -> list[AbstractCapability]: """Get all resource providers for this agent's tools.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.tools.base import Tool + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.tools.base import Tool providers: list[AbstractCapability] = [] static_tools: list[Tool] = [] @@ -191,7 +191,7 @@ def get_agent[TDeps]( pool: AgentPool[Any] | None = None, deps_type: type[TDeps] | None = None, # type: ignore[valid-type] ) -> ACPAgent[TDeps]: - from agentpool.agents.acp_agent import ACPAgent + from wolfharness.agents.acp_agent import ACPAgent return ACPAgent[TDeps].from_config( self, diff --git a/src/agentpool/models/agents.py b/src/wolfharness/models/agents.py similarity index 87% rename from src/agentpool/models/agents.py rename to src/wolfharness/models/agents.py index 50bca8bb9..a8920d2f7 100644 --- a/src/agentpool/models/agents.py +++ b/src/wolfharness/models/agents.py @@ -13,19 +13,19 @@ from tokonomics.model_names import ModelId # noqa: TC002 from toprompt import render_prompt -from agentpool import log -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.common_types import EndStrategy # noqa: TC001 -from agentpool.models.fields import OutputTypeField, SystemPromptField # noqa: TC001 -from agentpool.models.model_configs import AnyModelConfig # noqa: TC001 -from agentpool.prompts.prompts import PromptMessage, StaticPrompt -from agentpool_config import BaseToolConfig, NativeAgentToolConfig -from agentpool_config.builtin_tools import BaseBuiltinToolConfig -from agentpool_config.knowledge import Knowledge # noqa: TC001 -from agentpool_config.nodes import BaseAgentConfig -from agentpool_config.session import MemoryConfig, SessionQuery -from agentpool_config.toolsets import BaseToolsetConfig, ToolsetConfig -from agentpool_config.workers import WorkerConfig # noqa: TC001 +from wolfharness import log +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.common_types import EndStrategy # noqa: TC001 +from wolfharness.models.fields import OutputTypeField, SystemPromptField # noqa: TC001 +from wolfharness.models.model_configs import AnyModelConfig # noqa: TC001 +from wolfharness.prompts.prompts import PromptMessage, StaticPrompt +from wolfharness_config import BaseToolConfig, NativeAgentToolConfig +from wolfharness_config.builtin_tools import BaseBuiltinToolConfig +from wolfharness_config.knowledge import Knowledge # noqa: TC001 +from wolfharness_config.nodes import BaseAgentConfig +from wolfharness_config.session import MemoryConfig, SessionQuery +from wolfharness_config.toolsets import BaseToolsetConfig, ToolsetConfig +from wolfharness_config.workers import WorkerConfig # noqa: TC001 if TYPE_CHECKING: @@ -33,13 +33,13 @@ from pydantic_ai.capabilities import AbstractCapability - from agentpool.agents.native_agent import Agent - from agentpool.common_types import AnyEventHandlerType - from agentpool.delegation import AgentPool - from agentpool.prompts.prompts import BasePrompt - from agentpool.tools.base import Tool - from agentpool.ui.base import InputProvider - from agentpool_config.workers import AgentWorkerConfig + from wolfharness.agents.native_agent import Agent + from wolfharness.common_types import AnyEventHandlerType + from wolfharness.delegation import AgentPool + from wolfharness.prompts.prompts import BasePrompt + from wolfharness.tools.base import Tool + from wolfharness.ui.base import InputProvider + from wolfharness_config.workers import AgentWorkerConfig ToolMode = Literal["codemode"] @@ -55,7 +55,7 @@ class NativeAgentConfig(BaseAgentConfig): Defines an agent's complete configuration including its model, environment, and behavior settings. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/agent_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/agent_configuration/ """ model_config = ConfigDict( @@ -73,13 +73,13 @@ class NativeAgentConfig(BaseAgentConfig): examples=["openai:gpt-5-nano"], title="Model configuration or name", json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/model_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/model_configuration/" }, ) """The model to use for this agent. Can be either a simple model name string (e.g. 'openai:gpt-5') or a structured model definition. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/model_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/model_configuration/ """ tools: Sequence[AnyToolConfig | str] = Field( @@ -100,7 +100,7 @@ class NativeAgentConfig(BaseAgentConfig): ], title="Tool configurations", json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/tool_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/tool_configuration/" }, ) """A list of tools and toolsets to register with this agent. @@ -108,7 +108,7 @@ class NativeAgentConfig(BaseAgentConfig): Supports both single tools (bash, import, web_search, etc.) and toolsets (file_access, process_management, code, etc.). - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/tool_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/tool_configuration/ """ session: str | SessionQuery | MemoryConfig | None = Field( @@ -116,12 +116,12 @@ class NativeAgentConfig(BaseAgentConfig): examples=["main_session", "user_123"], title="Session configuration", json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/session_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/session_configuration/" }, ) """Session configuration for conversation recovery. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/session_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/session_configuration/ """ output_type: OutputTypeField = None @@ -149,7 +149,7 @@ class NativeAgentConfig(BaseAgentConfig): system_prompt: SystemPromptField = None """System prompt for the agent. Can be a string or list of strings/prompt configs. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/system_prompts_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/system_prompts_configuration/ """ # context_sources: list[ContextSource] = Field(default_factory=list) @@ -159,12 +159,12 @@ class NativeAgentConfig(BaseAgentConfig): default=None, title="Knowledge sources", json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/knowledge_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/knowledge_configuration/" }, ) """Knowledge sources for this agent. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/knowledge_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/knowledge_configuration/ """ workers: list[WorkerConfig] = Field( @@ -175,12 +175,12 @@ class NativeAgentConfig(BaseAgentConfig): ], title="Worker agents", json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/worker_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/worker_configuration/" }, ) """Worker agents which will be available as tools. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/worker_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/worker_configuration/ """ usage_limits: UsageLimits | None = Field(default=None, title="Usage limits") @@ -260,14 +260,14 @@ def handle_capabilities(cls, data: dict[str, Any]) -> dict[str, Any]: against the typed built-in config model. 2. **Entry-point name** (e.g. ``"mermaid_lint"``) → wrapped in :class:`EntryPointCapabilityConfig` for later resolution via the - ``agentpool.capabilities`` entry-point group. + ``wolfharness.capabilities`` entry-point group. 3. **Python import path** (e.g. ``"pydantic_ai.capabilities.Instrumentation"``) → wrapped in :class:`GenericCapabilityConfig` for ``__import__`` resolution. """ from pydantic import TypeAdapter - from agentpool_config.capabilities import ( + from wolfharness_config.capabilities import ( CapabilityConfig, EntryPointCapabilityConfig, GenericCapabilityConfig, @@ -288,7 +288,7 @@ def handle_capabilities(cls, data: dict[str, Any]) -> dict[str, Any]: # Lazily discover entry-point names only when needed. if ep_names is None: try: - from agentpool.capabilities.registry import ( + from wolfharness.capabilities.registry import ( discover_entry_point_capabilities, ) @@ -332,7 +332,7 @@ def get_agent[TDeps]( pool: AgentPool[Any] | None = None, deps_type: builtins.type[TDeps] | None = None, ) -> Agent[TDeps, Any]: - from agentpool.agents.native_agent import Agent + from wolfharness.agents.native_agent import Agent return Agent[TDeps].from_config( self, @@ -352,7 +352,7 @@ def get_tool_providers(self) -> list[AbstractCapability]: Returns: List of AbstractCapability instances """ - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool providers: list[AbstractCapability] = [] static_tools: list[Tool] = [] @@ -433,7 +433,7 @@ def get_history_processors(self) -> list[Callable[..., Any]]: """ import inspect - from agentpool.utils.importing import import_callable + from wolfharness.utils.importing import import_callable # Get session config memory_cfg = self.get_session_config() @@ -467,7 +467,7 @@ def get_history_processors(self) -> list[Callable[..., Any]]: def get_system_prompts(self) -> list[BasePrompt]: """Get all system prompts as BasePrompts.""" - from agentpool_config.system_prompts import ( + from wolfharness_config.system_prompts import ( FilePromptConfig, FunctionPromptConfig, LibraryPromptConfig, @@ -528,7 +528,7 @@ def get_system_prompts(self) -> list[BasePrompt]: def render_system_prompts(self, context: dict[str, Any] | None = None) -> list[str]: """Render system prompts with context.""" - from agentpool_config.system_prompts import ( + from wolfharness_config.system_prompts import ( FilePromptConfig, FunctionPromptConfig, LibraryPromptConfig, diff --git a/src/agentpool/models/fields.py b/src/wolfharness/models/fields.py similarity index 86% rename from src/agentpool/models/fields.py rename to src/wolfharness/models/fields.py index 6e4646f7c..6016087b1 100644 --- a/src/agentpool/models/fields.py +++ b/src/wolfharness/models/fields.py @@ -8,8 +8,8 @@ from exxec_config import E2bExecutionEnvironmentConfig, ExecutionEnvironmentConfig from pydantic import Field -from agentpool_config.output_types import StructuredResponseConfig -from agentpool_config.system_prompts import PromptConfig +from wolfharness_config.output_types import StructuredResponseConfig +from wolfharness_config.system_prompts import PromptConfig OutputTypeField = Annotated[ @@ -56,7 +56,7 @@ ["You are an AI assistant.", "Always be concise."], ], json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/system_prompts_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/system_prompts_configuration/" }, description="""System prompt for the agent. Can be a string or list of strings/prompt configs""", diff --git a/src/agentpool/models/file_agents.py b/src/wolfharness/models/file_agents.py similarity index 100% rename from src/agentpool/models/file_agents.py rename to src/wolfharness/models/file_agents.py diff --git a/src/agentpool/models/file_parsing.py b/src/wolfharness/models/file_parsing.py similarity index 96% rename from src/agentpool/models/file_parsing.py rename to src/wolfharness/models/file_parsing.py index d0e7c316b..7a26ae97b 100644 --- a/src/agentpool/models/file_parsing.py +++ b/src/wolfharness/models/file_parsing.py @@ -12,12 +12,12 @@ from upathtools import to_upath -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.file_agents import FileAgentConfig + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.file_agents import FileAgentConfig logger = get_logger(__name__) @@ -164,7 +164,7 @@ def parse_opencode_format( if system_prompt: config_kwargs["system_prompt"] = system_prompt - # Pass through agentpool specific fields + # Pass through wolfharness specific fields for field in PASSTHROUGH_FIELDS: if field in metadata: config_kwargs[field] = metadata[field] @@ -217,7 +217,7 @@ def parse_agent_file( Raises: ValueError: If file cannot be parsed """ - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig path = to_upath(file_path) content = path.read_text("utf-8") diff --git a/src/agentpool/models/fixed_args_test_model.py b/src/wolfharness/models/fixed_args_test_model.py similarity index 100% rename from src/agentpool/models/fixed_args_test_model.py rename to src/wolfharness/models/fixed_args_test_model.py diff --git a/src/agentpool/models/manifest.py b/src/wolfharness/models/manifest.py similarity index 89% rename from src/agentpool/models/manifest.py rename to src/wolfharness/models/manifest.py index 57d27df7f..46a3b1200 100644 --- a/src/agentpool/models/manifest.py +++ b/src/wolfharness/models/manifest.py @@ -12,36 +12,36 @@ from upathtools_config import FilesystemConfigType from upathtools_config.base import URIFileSystemConfig -from agentpool import log -from agentpool.models.acp_agents import ACPAgentConfigTypes -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.file_agents import FileAgentConfig -from agentpool.models.model_configs import AnyModelConfig, StringModelConfig -from agentpool_config.commands import CommandConfig, StaticCommandConfig -from agentpool_config.compaction import CompactionConfig -from agentpool_config.context import ConfigContextManager -from agentpool_config.converters import ConversionConfig -from agentpool_config.graph_config import GraphConfig -from agentpool_config.graph_translation import translate_config_to_graph -from agentpool_config.mcp_server import BaseMCPServerConfig, MCPServerConfig -from agentpool_config.observability import ObservabilityConfig -from agentpool_config.output_types import StructuredResponseConfig -from agentpool_config.pool_server import ACPPoolServerConfig, MCPPoolServerConfig -from agentpool_config.session_pool import ACPConfig, OpenCodeConfig, SessionPoolConfig -from agentpool_config.skills import SkillsConfig -from agentpool_config.storage import StorageConfig -from agentpool_config.system_prompts import PromptLibraryConfig -from agentpool_config.task import Job -from agentpool_config.team_mode import TeamModeConfig -from agentpool_config.teams import TeamConfig +from wolfharness import log +from wolfharness.models.acp_agents import ACPAgentConfigTypes +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.file_agents import FileAgentConfig +from wolfharness.models.model_configs import AnyModelConfig, StringModelConfig +from wolfharness_config.commands import CommandConfig, StaticCommandConfig +from wolfharness_config.compaction import CompactionConfig +from wolfharness_config.context import ConfigContextManager +from wolfharness_config.converters import ConversionConfig +from wolfharness_config.graph_config import GraphConfig +from wolfharness_config.graph_translation import translate_config_to_graph +from wolfharness_config.mcp_server import BaseMCPServerConfig, MCPServerConfig +from wolfharness_config.observability import ObservabilityConfig +from wolfharness_config.output_types import StructuredResponseConfig +from wolfharness_config.pool_server import ACPPoolServerConfig, MCPPoolServerConfig +from wolfharness_config.session_pool import ACPConfig, OpenCodeConfig, SessionPoolConfig +from wolfharness_config.skills import SkillsConfig +from wolfharness_config.storage import StorageConfig +from wolfharness_config.system_prompts import PromptLibraryConfig +from wolfharness_config.task import Job +from wolfharness_config.team_mode import TeamModeConfig +from wolfharness_config.teams import TeamConfig if TYPE_CHECKING: from upathtools import JoinablePathLike - from agentpool.messaging.compaction import CompactionPipeline - from agentpool.models.acp_agents import BaseACPAgentConfig - from agentpool_config.nodes import NodeConfig + from wolfharness.messaging.compaction import CompactionPipeline + from wolfharness.models.acp_agents import BaseACPAgentConfig + from wolfharness_config.nodes import NodeConfig logger = log.get_logger(__name__) @@ -131,7 +131,7 @@ class AgentsManifest(Schema): agents: dict[str, AnyAgentConfig] = Field( default_factory=dict, json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/agent_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/agent_configuration/" }, ) """Mapping of agent IDs to their configurations. @@ -165,7 +165,7 @@ class AgentsManifest(Schema): endpoint: http://localhost:8000/agent/run ``` - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/agent_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/agent_configuration/ """ default_agent: str | None = None @@ -223,12 +223,12 @@ class AgentsManifest(Schema): teams: dict[str, TeamConfig] = Field( default_factory=dict, json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/team_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/team_configuration/" }, ) """Mapping of team IDs to their configurations. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/team_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/team_configuration/ """ team_mode: TeamModeConfig | None = Field( @@ -245,12 +245,12 @@ class AgentsManifest(Schema): storage: StorageConfig = Field( default_factory=StorageConfig, json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/storage_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/storage_configuration/" }, ) """Storage provider configuration. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/storage_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/storage_configuration/ """ observability: ObservabilityConfig = Field(default_factory=ObservabilityConfig) @@ -262,12 +262,12 @@ class AgentsManifest(Schema): responses: dict[str, StructuredResponseConfig] = Field( default_factory=dict, json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/response_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/response_configuration/" }, ) """Mapping of response names to their definitions. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/response_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/response_configuration/ """ model_variants: dict[str, AnyModelConfig] = Field( @@ -326,14 +326,14 @@ class AgentsManifest(Schema): [{"type": "streamable-http", "url": "http://mcp.example.com"}], ], json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/mcp_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/mcp_configuration/" }, ) """List of MCP server configurations: These MCP servers are used to provide tools and other resources to the nodes. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/mcp_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/mcp_configuration/ """ pool_server: MCPPoolServerConfig | ACPPoolServerConfig = Field( default_factory=MCPPoolServerConfig @@ -346,14 +346,14 @@ class AgentsManifest(Schema): prompts: PromptLibraryConfig = Field( default_factory=PromptLibraryConfig, json_schema_extra={ - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/prompt_configuration/" + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/prompt_configuration/" }, ) """Prompt library configuration. This configuration defines the prompt library, which is used to provide prompts to the nodes. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/prompt_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/prompt_configuration/ """ skills: SkillsConfig = Field(default_factory=SkillsConfig) @@ -468,7 +468,7 @@ class AgentsManifest(Schema): json_schema_extra={ "x-icon": "octicon:file-code-16", "x-doc-title": "Manifest Overview", - "documentation_url": "https://phil65.github.io/agentpool/YAML%20Configuration/manifest_configuration/", + "documentation_url": "https://phil65.github.io/wolfharness/YAML%20Configuration/manifest_configuration/", "patternProperties": { # Allow YAML anchors (dot prefix) r"^\.": { @@ -567,7 +567,7 @@ def _loaded_file_agents(self) -> dict[str, NativeAgentConfig]: Parses markdown files in Claude Code, OpenCode, or AgentPool format and converts them to NativeAgentConfig. Results are cached. """ - from agentpool.models.file_parsing import parse_file_agent_reference + from wolfharness.models.file_parsing import parse_file_agent_reference loaded: dict[str, NativeAgentConfig] = {} for name, reference in self.file_agents.items(): @@ -597,7 +597,7 @@ def nodes(self) -> dict[str, NodeConfig]: @property def acp_agents(self) -> dict[str, BaseACPAgentConfig]: """Get ACP agents filtered from unified agents dict.""" - from agentpool.models.acp_agents import BaseACPAgentConfig + from wolfharness.models.acp_agents import BaseACPAgentConfig return {k: v for k, v in self.agents.items() if isinstance(v, BaseACPAgentConfig)} @@ -723,9 +723,9 @@ def from_resolved( """Load agent configuration with layered inheritance. Resolves configuration from multiple sources in precedence order: - 1. Global config (~/.config/agentpool/agentpool.yml) + 1. Global config (~/.config/wolfharness/wolfharness.yml) 2. Custom config (AGENTPOOL_CONFIG env var) - 3. Project config (agentpool.yml in project/git root) + 3. Project config (wolfharness.yml in project/git root) 4. Explicit config (highest precedence) The fallback_config is only used if NO other config defines any agents. @@ -744,7 +744,7 @@ def from_resolved( ValueError: If explicit_path is provided but cannot be loaded, or if merged config is invalid """ - from agentpool_config.resolution import resolve_config + from wolfharness_config.resolution import resolve_config resolved = resolve_config( explicit_path=explicit_path, @@ -853,7 +853,7 @@ def validate_extra_fields(self) -> Self: if __name__ == "__main__": - from agentpool.models.model_configs import InputModelConfig + from wolfharness.models.model_configs import InputModelConfig model = InputModelConfig() agent_cfg = NativeAgentConfig(name="test_agent", model=model) diff --git a/src/agentpool/models/model_configs.py b/src/wolfharness/models/model_configs.py similarity index 98% rename from src/agentpool/models/model_configs.py rename to src/wolfharness/models/model_configs.py index 332077bbd..e310a4a86 100644 --- a/src/agentpool/models/model_configs.py +++ b/src/wolfharness/models/model_configs.py @@ -1,7 +1,7 @@ """Model configuration. Replacement for llmling_models_config.configs. -Provides Pydantic-based model configurations for agentpool agents. +Provides Pydantic-based model configurations for wolfharness agents. """ from __future__ import annotations @@ -19,7 +19,7 @@ from tokonomics.model_names.gemini import GeminiModelName from tokonomics.model_names.openai import OpenaiModelName -from agentpool_config.model_capabilities import ModelCapabilities +from wolfharness_config.model_capabilities import ModelCapabilities if TYPE_CHECKING: @@ -306,7 +306,7 @@ def get_model_settings(self) -> PyAIModelSettings: return ModelSettings(**{k: v for k, v in settings.items() if v is not None}) # type: ignore[typeddict-item, no-any-return] def get_model(self) -> Model: - from agentpool.utils.model_helpers import _get_openai_based_model, infer_model + from wolfharness.utils.model_helpers import _get_openai_based_model, infer_model # If base_url is explicitly configured, create an OpenAI-compatible # model pointing to that endpoint — this is the most common pattern @@ -353,7 +353,7 @@ class FunctionModelConfig(BaseModelConfig): """Function identifier for the model.""" def get_model(self) -> Any: - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model return function_to_model(self.function) @@ -466,7 +466,7 @@ class TestModelConfig(BaseModelConfig): def get_model(self) -> Any: if self.tool_args: - from agentpool.models.fixed_args_test_model import FixedArgsTestModel + from wolfharness.models.fixed_args_test_model import FixedArgsTestModel return FixedArgsTestModel( tool_args=self.tool_args, @@ -796,7 +796,7 @@ def get_model_settings(self) -> OpenAIResponsesModelSettings | OpenAIChatModelSe return OpenAIResponsesModelSettings(**filtered_responses) # type: ignore[typeddict-item, no-any-return] def get_model(self) -> Any: - from agentpool.utils.model_helpers import infer_model + from wolfharness.utils.model_helpers import infer_model prefix = "openai:" if self.api_type == "responses" else "openai-chat:" return infer_model(prefix + self.identifier) @@ -1004,14 +1004,14 @@ def get_model(self) -> Any: if self.auth_method == "oauth": from pydantic_ai.models.anthropic import AnthropicModel - from agentpool.auth.anthropic_auth import ( + from wolfharness.auth.anthropic_auth import ( AnthropicMaxProvider, ) provider = AnthropicMaxProvider() return AnthropicModel(self.identifier, provider=provider) # type: ignore[arg-type] - from agentpool.utils.model_helpers import infer_model + from wolfharness.utils.model_helpers import infer_model return infer_model(self.identifier) @@ -1162,7 +1162,7 @@ def get_model_settings(self) -> GeminiModelSettings: return GeminiModelSettings(**{k: v for k, v in settings.items() if v is not None}) # type: ignore[typeddict-item, no-any-return] def get_model(self) -> Any: - from agentpool.utils.model_helpers import infer_model + from wolfharness.utils.model_helpers import infer_model return infer_model("gemini:" + self.identifier) diff --git a/src/agentpool/models/pending_interaction.py b/src/wolfharness/models/pending_interaction.py similarity index 100% rename from src/agentpool/models/pending_interaction.py rename to src/wolfharness/models/pending_interaction.py diff --git a/src/agentpool/observability/__init__.py b/src/wolfharness/observability/__init__.py similarity index 64% rename from src/agentpool/observability/__init__.py rename to src/wolfharness/observability/__init__.py index ab5dc2479..01902d5fc 100644 --- a/src/agentpool/observability/__init__.py +++ b/src/wolfharness/observability/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from agentpool.observability.observability_registry import ObservabilityRegistry +from wolfharness.observability.observability_registry import ObservabilityRegistry registry = ObservabilityRegistry() diff --git a/src/agentpool/observability/observability_registry.py b/src/wolfharness/observability/observability_registry.py similarity index 96% rename from src/agentpool/observability/observability_registry.py rename to src/wolfharness/observability/observability_registry.py index bb540ed98..2f1b5f9f4 100644 --- a/src/agentpool/observability/observability_registry.py +++ b/src/wolfharness/observability/observability_registry.py @@ -7,11 +7,11 @@ import logfire -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool_config.observability import BaseObservabilityConfig, ObservabilityConfig + from wolfharness_config.observability import BaseObservabilityConfig, ObservabilityConfig logger = get_logger(__name__) diff --git a/src/agentpool/observability/spans.py b/src/wolfharness/observability/spans.py similarity index 99% rename from src/agentpool/observability/spans.py rename to src/wolfharness/observability/spans.py index bf1d0f3ac..f7d7f3cc2 100644 --- a/src/agentpool/observability/spans.py +++ b/src/wolfharness/observability/spans.py @@ -32,7 +32,7 @@ import logfire -from agentpool.log import get_logger +from wolfharness.log import get_logger _log = get_logger(__name__) diff --git a/src/agentpool/observability/trace.py b/src/wolfharness/observability/trace.py similarity index 100% rename from src/agentpool/observability/trace.py rename to src/wolfharness/observability/trace.py diff --git a/src/agentpool/orchestrator/AGENTS.md b/src/wolfharness/orchestrator/AGENTS.md similarity index 100% rename from src/agentpool/orchestrator/AGENTS.md rename to src/wolfharness/orchestrator/AGENTS.md diff --git a/src/agentpool/orchestrator/__init__.py b/src/wolfharness/orchestrator/__init__.py similarity index 75% rename from src/agentpool/orchestrator/__init__.py rename to src/wolfharness/orchestrator/__init__.py index c462d30ec..9206e3331 100644 --- a/src/agentpool/orchestrator/__init__.py +++ b/src/wolfharness/orchestrator/__init__.py @@ -11,16 +11,16 @@ from __future__ import annotations -from agentpool.orchestrator.event_bus import ( +from wolfharness.orchestrator.event_bus import ( DEFAULT_QUEUE_MAXSIZE, EventEnvelope, EventBus, drain_and_merge, ) -from agentpool.orchestrator.metrics import MetricsCollector, SessionPoolMetrics -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.runtime_registry import RuntimeAgentRegistry -from agentpool.orchestrator.session_controller import ( +from wolfharness.orchestrator.metrics import MetricsCollector, SessionPoolMetrics +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.runtime_registry import RuntimeAgentRegistry +from wolfharness.orchestrator.session_controller import ( DEFAULT_SESSION_TTL_SECONDS, CheckpointMismatchError, SessionBusyError, @@ -29,7 +29,7 @@ SessionNotFoundError, SessionState, ) -from agentpool.orchestrator.session_pool import ( +from wolfharness.orchestrator.session_pool import ( DEFAULT_MAX_AUTO_RESUME, SessionPool, ) diff --git a/src/agentpool/orchestrator/core.py b/src/wolfharness/orchestrator/core.py similarity index 87% rename from src/agentpool/orchestrator/core.py rename to src/wolfharness/orchestrator/core.py index b3448dea2..f829537a5 100644 --- a/src/agentpool/orchestrator/core.py +++ b/src/wolfharness/orchestrator/core.py @@ -11,7 +11,7 @@ from __future__ import annotations -from agentpool.orchestrator.event_bus import ( +from wolfharness.orchestrator.event_bus import ( DEFAULT_QUEUE_MAXSIZE, EventBus, EventEnvelope, @@ -25,8 +25,8 @@ _rebind, # noqa: F401 drain_and_merge, ) -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.session_controller import ( +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.session_controller import ( DEFAULT_SESSION_TTL_SECONDS, CheckpointMismatchError, SessionBusyError, @@ -36,7 +36,7 @@ SessionNotFoundError, SessionState, ) -from agentpool.orchestrator.session_pool import ( +from wolfharness.orchestrator.session_pool import ( DEFAULT_MAX_AUTO_RESUME, SessionPool, ) diff --git a/src/agentpool/orchestrator/event_bus.py b/src/wolfharness/orchestrator/event_bus.py similarity index 99% rename from src/agentpool/orchestrator/event_bus.py rename to src/wolfharness/orchestrator/event_bus.py index ea2aff307..06ba4d9aa 100644 --- a/src/agentpool/orchestrator/event_bus.py +++ b/src/wolfharness/orchestrator/event_bus.py @@ -19,7 +19,7 @@ from pydantic_ai import TextPartDelta, ThinkingPartDelta, ToolCallPartDelta -from agentpool.agents.events import ( +from wolfharness.agents.events import ( CompactionEvent, ElicitationDeferredEvent, PartDeltaEvent, @@ -36,13 +36,13 @@ ToolCallProgressEvent, ToolCallStartEvent, ) -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController logger = get_logger(__name__) diff --git a/src/agentpool/orchestrator/event_mapper.py b/src/wolfharness/orchestrator/event_mapper.py similarity index 99% rename from src/agentpool/orchestrator/event_mapper.py rename to src/wolfharness/orchestrator/event_mapper.py index 423f463a1..c230ede25 100644 --- a/src/agentpool/orchestrator/event_mapper.py +++ b/src/wolfharness/orchestrator/event_mapper.py @@ -45,7 +45,7 @@ except ImportError: EnqueuedMessagesEvent = None # type: ignore[assignment,misc] -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( PartDeltaEvent, PartStartEvent, RichAgentStreamEvent, @@ -54,8 +54,8 @@ ToolCallStartEvent, UserMessageInsertedEvent, ) -from agentpool.tools.base import ToolKind -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.tools.base import ToolKind +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict ENQUEUED_MESSAGES_AVAILABLE = EnqueuedMessagesEvent is not None diff --git a/src/agentpool/orchestrator/metrics.py b/src/wolfharness/orchestrator/metrics.py similarity index 78% rename from src/agentpool/orchestrator/metrics.py rename to src/wolfharness/orchestrator/metrics.py index 4502bb945..75c712011 100644 --- a/src/agentpool/orchestrator/metrics.py +++ b/src/wolfharness/orchestrator/metrics.py @@ -6,11 +6,11 @@ import math from typing import TYPE_CHECKING -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool.orchestrator.core import SessionPool + from wolfharness.orchestrator.core import SessionPool logger = get_logger(__name__) @@ -41,7 +41,7 @@ class SessionPoolMetrics: def to_prometheus(self) -> str: """Return metrics in Prometheus exposition format. - All metric names use the ``agentpool_`` prefix and the same + All metric names use the ``wolfharness_`` prefix and the same label names as the existing Grafana dashboards so no dashboard changes are required. @@ -50,30 +50,30 @@ def to_prometheus(self) -> str: """ lines: list[str] = [] - lines.append("# TYPE agentpool_sessions_total gauge") - lines.append(f"agentpool_sessions_total {self.active_sessions}") + lines.append("# TYPE wolfharness_sessions_total gauge") + lines.append(f"wolfharness_sessions_total {self.active_sessions}") - lines.append("# TYPE agentpool_active_turns_total gauge") - lines.append(f"agentpool_active_turns_total {self.active_turns}") + lines.append("# TYPE wolfharness_active_turns_total gauge") + lines.append(f"wolfharness_active_turns_total {self.active_turns}") - lines.append("# TYPE agentpool_auto_resume_total counter") - lines.append(f"agentpool_auto_resume_total {self.auto_resume_count}") + lines.append("# TYPE wolfharness_auto_resume_total counter") + lines.append(f"wolfharness_auto_resume_total {self.auto_resume_count}") - lines.append("# TYPE agentpool_event_bus_subscribers gauge") + lines.append("# TYPE wolfharness_event_bus_subscribers gauge") for session_id, count in self.event_bus_queue_depth.items(): sid = session_id.replace('"', '\\"') - lines.append(f'agentpool_event_bus_subscribers{{session_id="{sid}"}} {count}') + lines.append(f'wolfharness_event_bus_subscribers{{session_id="{sid}"}} {count}') - lines.append("# TYPE agentpool_session_lifetime_seconds gauge") - lines.append(f"agentpool_session_lifetime_seconds {self.session_lifetime_seconds:.3f}") + lines.append("# TYPE wolfharness_session_lifetime_seconds gauge") + lines.append(f"wolfharness_session_lifetime_seconds {self.session_lifetime_seconds:.3f}") - lines.append("# TYPE agentpool_turn_latency_ms summary") - lines.append(f'agentpool_turn_latency_ms{{quantile="0.99"}} {self.turn_latency_p99:.3f}') + lines.append("# TYPE wolfharness_turn_latency_ms summary") + lines.append(f'wolfharness_turn_latency_ms{{quantile="0.99"}} {self.turn_latency_p99:.3f}') - lines.append("# TYPE agentpool_active_runs_by_agent_type gauge") + lines.append("# TYPE wolfharness_active_runs_by_agent_type gauge") for agent_type, count in self.active_runs_by_agent_type.items(): at = agent_type.replace('"', '\\"') - lines.append(f'agentpool_active_runs_by_agent_type{{agent_type="{at}"}} {count}') + lines.append(f'wolfharness_active_runs_by_agent_type{{agent_type="{at}"}} {count}') return "\n".join(lines) diff --git a/src/agentpool/orchestrator/run.py b/src/wolfharness/orchestrator/run.py similarity index 97% rename from src/agentpool/orchestrator/run.py rename to src/wolfharness/orchestrator/run.py index 0951651d8..1c944aa77 100644 --- a/src/agentpool/orchestrator/run.py +++ b/src/wolfharness/orchestrator/run.py @@ -15,18 +15,18 @@ import logfire -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( RunErrorEvent, RunFailedEvent, RunStartedEvent, StreamCompleteEvent, UserMessageInsertedEvent, ) -from agentpool.lifecycle import RunOutcome -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.observability.spans import safe_span +from wolfharness.lifecycle import RunOutcome +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.observability.spans import safe_span if TYPE_CHECKING: @@ -36,12 +36,12 @@ from pydantic_ai import AgentRun from pydantic_ai.messages import ModelMessage - from agentpool.agents.base_agent import BaseAgent - from agentpool.agents.events.events import RichAgentStreamEvent - from agentpool.host.context import HostContext - from agentpool.host.registry import AgentRegistry - from agentpool.lifecycle.protocols import CommChannel - from agentpool.orchestrator.core import EventBus, SessionState + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.agents.events.events import RichAgentStreamEvent + from wolfharness.host.context import HostContext + from wolfharness.host.registry import AgentRegistry + from wolfharness.lifecycle.protocols import CommChannel + from wolfharness.orchestrator.core import EventBus, SessionState logger = get_logger(__name__) @@ -308,9 +308,9 @@ def _inject_agent_context(self) -> None: """ if self._host_context is None: return - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool.capabilities.runloop_delegation import RunLoopDelegationService - from agentpool.host.context import RunScope + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.runloop_delegation import RunLoopDelegationService + from wolfharness.host.context import RunScope registry = self._agent_registry if registry is None: @@ -401,7 +401,7 @@ async def start( session_id=self.session_id, agent_type=self.agent_type, ): - from agentpool.observability.trace import get_trace_id + from wolfharness.observability.trace import get_trace_id logfire.info( "RunHandle started (per-prompt)", @@ -556,7 +556,7 @@ async def _execute_turn( # noqa: PLR0915 # Set _current_input_provider ContextVar so MCP elicitation can # access it during turn execution. if session.input_provider is not None: - from agentpool.mcp_server.manager import _current_input_provider + from wolfharness.mcp_server.manager import _current_input_provider _current_input_provider.set(session.input_provider) # Save user prompt to agent conversation before execution. @@ -566,7 +566,7 @@ async def _execute_turn( # noqa: PLR0915 # staged_content (consumed by NativeTurn), and saving an empty user # message would pollute conversation history (issue #284). if current_prompts: - from agentpool.agents.native_agent.helpers import _summarize_content_block + from wolfharness.agents.native_agent.helpers import _summarize_content_block prompt_text = "\n".join( p if isinstance(p, str) else " ".join(_summarize_content_block(b) for b in p) @@ -684,7 +684,7 @@ def steer( The ``message_id`` string on success, ``None`` if no agent_run is active and the message was queued. """ - from agentpool.lifecycle.types import Feedback + from wolfharness.lifecycle.types import Feedback # Construct Feedback with message_id and content_blocks. fb_kwargs: dict[str, Any] = {} @@ -797,7 +797,7 @@ def followup( A ``message_id`` string on success, ``None`` if no session is attached. """ - from agentpool.utils.identifiers import ascending + from wolfharness.utils.identifiers import ascending message_id = ascending("message") session = self.session @@ -894,7 +894,7 @@ async def _emit_user_message_inserted( session_id=self.session_id, ): try: - from agentpool.utils.identifiers import ascending + from wolfharness.utils.identifiers import ascending event: UserMessageInsertedEvent[Any] = UserMessageInsertedEvent( session_id=self.session_id, diff --git a/src/agentpool/orchestrator/runtime_registry.py b/src/wolfharness/orchestrator/runtime_registry.py similarity index 96% rename from src/agentpool/orchestrator/runtime_registry.py rename to src/wolfharness/orchestrator/runtime_registry.py index 1328eb9bb..3aa953982 100644 --- a/src/agentpool/orchestrator/runtime_registry.py +++ b/src/wolfharness/orchestrator/runtime_registry.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: - from agentpool.models.manifest import AnyAgentConfig + from wolfharness.models.manifest import AnyAgentConfig class RuntimeAgentRegistry: diff --git a/src/agentpool/orchestrator/session_controller.py b/src/wolfharness/orchestrator/session_controller.py similarity index 96% rename from src/agentpool/orchestrator/session_controller.py rename to src/wolfharness/orchestrator/session_controller.py index 85217ddf3..ae958462c 100644 --- a/src/agentpool/orchestrator/session_controller.py +++ b/src/wolfharness/orchestrator/session_controller.py @@ -14,29 +14,29 @@ import anyio -from agentpool.log import get_logger -from agentpool.orchestrator.runtime_registry import RuntimeAgentRegistry -from agentpool.utils.time_utils import get_now +from wolfharness.log import get_logger +from wolfharness.orchestrator.runtime_registry import RuntimeAgentRegistry +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: from collections.abc import Awaitable, Callable from datetime import datetime - from agentpool.delegation import AgentPool - from agentpool.host.context import HostContext - from agentpool.host.registry import AgentRegistry - from agentpool.lifecycle.protocols import ( + from wolfharness.delegation import AgentPool + from wolfharness.host.context import HostContext + from wolfharness.host.registry import AgentRegistry + from wolfharness.lifecycle.protocols import ( CommChannel, EventTransport, Journal, SnapshotStore, TriggerSource, ) - from agentpool.lifecycle.types import Feedback, ResumeResult - from agentpool.models.pending_interaction import PendingPermission - from agentpool.orchestrator.event_bus import EventBus - from agentpool_storage.protocols import SessionPersistence + from wolfharness.lifecycle.types import Feedback, ResumeResult + from wolfharness.models.pending_interaction import PendingPermission + from wolfharness.orchestrator.event_bus import EventBus + from wolfharness_storage.protocols import SessionPersistence logger = get_logger(__name__) @@ -309,8 +309,8 @@ def set_current_run_id(self, run_id: str | None) -> None: if old == run_id: return # Publish StateUpdate via CommChannel when available. - from agentpool.agents.events import StateUpdate - from agentpool.lifecycle.types import RunState + from wolfharness.agents.events import StateUpdate + from wolfharness.lifecycle.types import RunState comm = self._comm_channel if comm is None: @@ -360,13 +360,15 @@ def revoke(self, message_id: str) -> bool: # Mixin imports placed after SessionState/exception definitions to avoid # circular import issues (mixins need SessionState at runtime). -from agentpool.orchestrator.session_controller_agent import ( # noqa: E402 +from wolfharness.orchestrator.session_controller_agent import ( # noqa: E402 SessionControllerAgentMixin, ) -from agentpool.orchestrator.session_controller_close import ( # noqa: E402 +from wolfharness.orchestrator.session_controller_close import ( # noqa: E402 SessionControllerCloseMixin, ) -from agentpool.orchestrator.session_controller_runs import SessionControllerRunsMixin # noqa: E402 +from wolfharness.orchestrator.session_controller_runs import ( # noqa: E402 + SessionControllerRunsMixin, +) class SessionController( diff --git a/src/agentpool/orchestrator/session_controller_agent.py b/src/wolfharness/orchestrator/session_controller_agent.py similarity index 96% rename from src/agentpool/orchestrator/session_controller_agent.py rename to src/wolfharness/orchestrator/session_controller_agent.py index a0a7442c5..b562633df 100644 --- a/src/agentpool/orchestrator/session_controller_agent.py +++ b/src/wolfharness/orchestrator/session_controller_agent.py @@ -11,22 +11,22 @@ import anyio -from agentpool.log import get_logger -from agentpool_server.opencode_server.models.session_info import SessionInfo +from wolfharness.log import get_logger +from wolfharness_server.opencode_server.models.session_info import SessionInfo if TYPE_CHECKING: import asyncio - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation import AgentPool - from agentpool.orchestrator.runtime_registry import RuntimeAgentRegistry - from agentpool_storage.protocols import SessionPersistence + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation import AgentPool + from wolfharness.orchestrator.runtime_registry import RuntimeAgentRegistry + from wolfharness_storage.protocols import SessionPersistence # SessionState is imported at runtime (not under TYPE_CHECKING) because it is # used in _get_or_create_session_locked. This works because session_controller.py # defines SessionState before importing this module. -from agentpool.orchestrator.session_controller import SessionState +from wolfharness.orchestrator.session_controller import SessionState logger = get_logger(__name__) @@ -102,7 +102,7 @@ def _state_to_data(self, state: SessionState) -> Any: """ from datetime import UTC, datetime - from agentpool.sessions.models import SessionData + from wolfharness.sessions.models import SessionData # Derive created_at from created_at_ns (epoch nanoseconds) so the # persisted timestamp matches the session ID's embedded timestamp. @@ -149,7 +149,7 @@ async def _get_or_create_session_locked( A tuple of (session_state, was_created) where was_created is True if the session was newly created, False if it already existed. """ - from agentpool.orchestrator.session_controller import SessionLifecyclePolicy + from wolfharness.orchestrator.session_controller import SessionLifecyclePolicy if session_id in self._sessions: state = self._sessions[session_id] @@ -180,7 +180,7 @@ async def _get_or_create_session_locked( # This ensures time.created order matches session ID lexicographic # order, since both come from the same now_ms() call inside # generate_session_id() / ascending() / descending(). - from agentpool.utils.identifiers import extract_timestamp_ms + from wolfharness.utils.identifiers import extract_timestamp_ms ts_ms = extract_timestamp_ms(session_id) if ts_ms is not None: @@ -342,7 +342,7 @@ async def _initialize_lifecycle_and_recovery( session: The session state to initialize dimensions on. agent: The agent instance for this session. """ - from agentpool.lifecycle import ( + from wolfharness.lifecycle import ( DirectChannel, InProcessTransport, MemoryJournal, @@ -386,7 +386,7 @@ async def _initialize_lifecycle_and_recovery( session._lifecycle_session_id = session.session_id # Wire HostContext and AgentRegistry on SessionState (task 1.6). - from agentpool.host.registry import AgentRegistry + from wolfharness.host.registry import AgentRegistry host_ctx = self.pool.get_context() agent_registry = AgentRegistry( diff --git a/src/agentpool/orchestrator/session_controller_close.py b/src/wolfharness/orchestrator/session_controller_close.py similarity index 96% rename from src/agentpool/orchestrator/session_controller_close.py rename to src/wolfharness/orchestrator/session_controller_close.py index f55d84cda..e6fc24b2f 100644 --- a/src/agentpool/orchestrator/session_controller_close.py +++ b/src/wolfharness/orchestrator/session_controller_close.py @@ -11,18 +11,18 @@ import time from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: import anyio - from agentpool.delegation.pool import AgentPool - from agentpool.orchestrator.event_bus import EventBus - from agentpool.orchestrator.run import RunHandle - from agentpool.orchestrator.session_controller import SessionState - from agentpool.sessions.models import PendingDeferredCall, SessionData - from agentpool_storage.protocols import SessionPersistence + from wolfharness.delegation.pool import AgentPool + from wolfharness.orchestrator.event_bus import EventBus + from wolfharness.orchestrator.run import RunHandle + from wolfharness.orchestrator.session_controller import SessionState + from wolfharness.sessions.models import PendingDeferredCall, SessionData + from wolfharness_storage.protocols import SessionPersistence logger = get_logger(__name__) @@ -107,7 +107,7 @@ async def _close_session_unlocked( # noqa: PLR0915 # MCP tool calls unblock immediately. registry = run_handle.run_ctx.elicitation_registry if registry is not None: - from agentpool.orchestrator.session_controller import SessionClosedError + from wolfharness.orchestrator.session_controller import SessionClosedError registry.reject_all(SessionClosedError(session_id)) # Step 2: Await RunHandle completion (10s timeout) diff --git a/src/agentpool/orchestrator/session_controller_runs.py b/src/wolfharness/orchestrator/session_controller_runs.py similarity index 97% rename from src/agentpool/orchestrator/session_controller_runs.py rename to src/wolfharness/orchestrator/session_controller_runs.py index 6d790339d..c5b44ee55 100644 --- a/src/agentpool/orchestrator/session_controller_runs.py +++ b/src/wolfharness/orchestrator/session_controller_runs.py @@ -17,25 +17,25 @@ import logfire from opentelemetry.context import attach, detach -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( RunErrorEvent, RunFailedEvent, UserMessageInsertedEvent, ) -from agentpool.lifecycle.comm_channel import ProtocolChannel -from agentpool.log import get_logger -from agentpool.observability.spans import safe_span -from agentpool.orchestrator.run import RunHandle, inject_cancelled_tool_results +from wolfharness.lifecycle.comm_channel import ProtocolChannel +from wolfharness.log import get_logger +from wolfharness.observability.spans import safe_span +from wolfharness.orchestrator.run import RunHandle, inject_cancelled_tool_results if TYPE_CHECKING: from pydantic_ai.messages import ModelMessage - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation import AgentPool - from agentpool.orchestrator.event_bus import EventBus - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation import AgentPool + from wolfharness.orchestrator.event_bus import EventBus + from wolfharness.orchestrator.session_controller import SessionState logger = get_logger(__name__) @@ -105,7 +105,7 @@ async def _emit_user_message_inserted( source=source, ): try: - from agentpool.utils.identifiers import ascending + from wolfharness.utils.identifiers import ascending event = UserMessageInsertedEvent( session_id=session_id, @@ -432,7 +432,7 @@ def _start_run_handle( from opentelemetry import trace as otel_trace from opentelemetry.context import Context - tracer = otel_trace.get_tracer("agentpool.run") + tracer = otel_trace.get_tracer("wolfharness.run") run_span = tracer.start_span( "run.message", context=Context(), # Empty context → new trace, no parent @@ -449,7 +449,7 @@ def _start_run_handle( session.set_current_run_id(run_handle.run_id) # Generate message_id for the initial prompt. - from agentpool.lifecycle.types import Feedback + from wolfharness.lifecycle.types import Feedback fb_kwargs: dict[str, Any] = {} if message_id is not None: @@ -529,7 +529,7 @@ async def _route_message( # Generate message_id once so both _emit_user_message_inserted() and # steer()/followup() share the same ID for dedup. if message_id is None: - from agentpool.utils.identifiers import ascending + from wolfharness.utils.identifiers import ascending message_id = ascending("message") async with session._request_lock: @@ -586,7 +586,7 @@ async def _route_message( message_id=message_id, meta=meta, ) - from agentpool.lifecycle.types import Feedback + from wolfharness.lifecycle.types import Feedback fb_kwargs: dict[str, Any] = {} if message_id is not None: @@ -671,7 +671,7 @@ async def wait_for_completion(self, session_id: str, timeout: float | None = 300 asyncio.TimeoutError: If the run does not complete within ``timeout`` seconds. """ - from agentpool.orchestrator.session_controller import SessionNotFoundError + from wolfharness.orchestrator.session_controller import SessionNotFoundError if timeout is None: timeout = 300 diff --git a/src/agentpool/orchestrator/session_pool.py b/src/wolfharness/orchestrator/session_pool.py similarity index 96% rename from src/agentpool/orchestrator/session_pool.py rename to src/wolfharness/orchestrator/session_pool.py index dc0dae9e6..1287a8a17 100644 --- a/src/agentpool/orchestrator/session_pool.py +++ b/src/wolfharness/orchestrator/session_pool.py @@ -12,31 +12,35 @@ import time from typing import TYPE_CHECKING, Any, Final -from agentpool.agents.events import ( +from wolfharness.agents.events import ( SessionResumeEvent, ) -from agentpool.log import get_logger -from agentpool.orchestrator.event_bus import EventBus -from agentpool.orchestrator.session_controller import ( +from wolfharness.log import get_logger +from wolfharness.orchestrator.event_bus import EventBus +from wolfharness.orchestrator.session_controller import ( CheckpointMismatchError, SessionBusyError, SessionController, SessionNotFoundError, SessionState, ) -from agentpool.orchestrator.session_pool_config import SessionPoolConfig +from wolfharness.orchestrator.session_pool_config import SessionPoolConfig if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.agents.base_agent import BaseAgent - from agentpool.agents.native_agent import Agent - from agentpool.agents.native_agent.checkpoint import CheckpointData - from agentpool.delegation import AgentPool - from agentpool.messaging import ChatMessage - from agentpool.sessions.models import ElicitationResumePayload, PendingDeferredCall, SessionData - from agentpool_storage.protocols import SessionPersistence + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.agents.native_agent import Agent + from wolfharness.agents.native_agent.checkpoint import CheckpointData + from wolfharness.delegation import AgentPool + from wolfharness.messaging import ChatMessage + from wolfharness.sessions.models import ( + ElicitationResumePayload, + PendingDeferredCall, + SessionData, + ) + from wolfharness_storage.protocols import SessionPersistence logger = get_logger(__name__) @@ -44,9 +48,9 @@ DEFAULT_MAX_AUTO_RESUME: Final[int] = 10 # Mixin imports placed after session_controller imports to avoid circular issues. -from agentpool.orchestrator.session_pool_messaging import SessionPoolMessagingMixin # noqa: E402 -from agentpool.orchestrator.session_pool_runs import SessionPoolRunsMixin # noqa: E402 -from agentpool.orchestrator.session_pool_teams import SessionPoolTeamsMixin # noqa: E402 +from wolfharness.orchestrator.session_pool_messaging import SessionPoolMessagingMixin # noqa: E402 +from wolfharness.orchestrator.session_pool_runs import SessionPoolRunsMixin # noqa: E402 +from wolfharness.orchestrator.session_pool_teams import SessionPoolTeamsMixin # noqa: E402 class SessionPool( @@ -183,7 +187,7 @@ async def create_child_session( This is a first-class API for hierarchical session creation. The child session inherits the parent's ``project_id`` and ``cwd`` automatically. A sortable session ID is generated via - :func:`agentpool.utils.identifiers.generate_session_id` when not + :func:`wolfharness.utils.identifiers.generate_session_id` when not provided. Args: @@ -197,7 +201,7 @@ async def create_child_session( Returns: The child session state. """ - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id child_session_id = session_id or generate_session_id() metadata.setdefault("agent_type", agent_type) @@ -282,7 +286,7 @@ async def _load_checkpoint_data(self, session_id: str) -> CheckpointData: Raises: SessionNotFoundError: If no checkpoint exists for the session. """ - from agentpool.agents.native_agent.checkpoint import CheckpointManager + from wolfharness.agents.native_agent.checkpoint import CheckpointManager storage = self.pool.storage if storage is None: @@ -311,8 +315,8 @@ async def _reconstruct_native_agent( Raises: SessionNotFoundError: If the agent config is not found. """ - from agentpool.models.agents import NativeAgentConfig - from agentpool_config.context import ConfigContextManager + from wolfharness.models.agents import NativeAgentConfig + from wolfharness_config.context import ConfigContextManager cfg = self.pool.manifest.agents.get(agent_name) if cfg is None: @@ -357,7 +361,7 @@ async def _reconstruct_acp_agent( Raises: SessionNotFoundError: If the agent config is not found. """ - from agentpool_config.context import ConfigContextManager + from wolfharness_config.context import ConfigContextManager cfg = self.pool.manifest.agents.get(agent_name) if cfg is None: @@ -436,7 +440,7 @@ async def _resume_native_agent( # noqa: PLR0915 # we skip the comparison and proceed with resume. if session_data.agent_config_hash: try: - from agentpool.agents.native_agent.checkpoint import ( + from wolfharness.agents.native_agent.checkpoint import ( compute_agent_config_hash, ) @@ -522,7 +526,7 @@ async def _resume_native_agent( # noqa: PLR0915 if elicitation_payloads: from mcp.types import ElicitResult as MCPElicitResult - from agentpool.ui.elicitation import normalize_elicit_content + from wolfharness.ui.elicitation import normalize_elicit_content for payload in elicitation_payloads: # Map elicitation handle to ToolCallPart.tool_call_id diff --git a/src/agentpool/orchestrator/session_pool_config.py b/src/wolfharness/orchestrator/session_pool_config.py similarity index 100% rename from src/agentpool/orchestrator/session_pool_config.py rename to src/wolfharness/orchestrator/session_pool_config.py diff --git a/src/agentpool/orchestrator/session_pool_messaging.py b/src/wolfharness/orchestrator/session_pool_messaging.py similarity index 96% rename from src/agentpool/orchestrator/session_pool_messaging.py rename to src/wolfharness/orchestrator/session_pool_messaging.py index a890ac38d..fb85c017a 100644 --- a/src/agentpool/orchestrator/session_pool_messaging.py +++ b/src/wolfharness/orchestrator/session_pool_messaging.py @@ -12,21 +12,21 @@ import logfire -from agentpool.agents.events import StreamCompleteEvent -from agentpool.lifecycle.types import DeliveryMode, Feedback -from agentpool.log import get_logger -from agentpool.utils.pydantic_ai_helpers import flatten_prompts +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.lifecycle.types import DeliveryMode, Feedback +from wolfharness.log import get_logger +from wolfharness.utils.pydantic_ai_helpers import flatten_prompts if TYPE_CHECKING: from collections import OrderedDict - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation import AgentPool - from agentpool.messaging import ChatMessage - from agentpool.orchestrator.event_bus import EventBus - from agentpool.orchestrator.run import RunHandle - from agentpool.orchestrator.session_controller import SessionController, SessionState + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation import AgentPool + from wolfharness.messaging import ChatMessage + from wolfharness.orchestrator.event_bus import EventBus + from wolfharness.orchestrator.run import RunHandle + from wolfharness.orchestrator.session_controller import SessionController, SessionState logger = get_logger(__name__) @@ -117,7 +117,7 @@ async def _process_prompt_run_turn( ``start()`` to completion. If a run is active, steers the message into it. """ - from agentpool.agents.events import RunErrorEvent, StreamCompleteEvent + from wolfharness.agents.events import RunErrorEvent, StreamCompleteEvent session, _ = await self.sessions.get_or_create_session(session_id) if session.is_closing: @@ -289,8 +289,8 @@ async def run_agent( Exception: Any error from the agent run is re-raised after session cleanup. """ - from agentpool.agents.events import RunErrorEvent - from agentpool.utils.identifiers import generate_session_id + from wolfharness.agents.events import RunErrorEvent + from wolfharness.utils.identifiers import generate_session_id session_id = generate_session_id() await self.create_session( @@ -439,8 +439,8 @@ async def steer_from_background_task(self, session_id: str, message: str) -> str Returns: message_id if delivered, None otherwise. """ - from agentpool.agents.events.events import UserMessageInsertedEvent - from agentpool.utils.identifiers import ascending + from wolfharness.agents.events.events import UserMessageInsertedEvent + from wolfharness.utils.identifiers import ascending session = self.sessions._sessions.get(session_id) if session is None: diff --git a/src/agentpool/orchestrator/session_pool_runs.py b/src/wolfharness/orchestrator/session_pool_runs.py similarity index 95% rename from src/agentpool/orchestrator/session_pool_runs.py rename to src/wolfharness/orchestrator/session_pool_runs.py index e194525c5..4da37e9a4 100644 --- a/src/agentpool/orchestrator/session_pool_runs.py +++ b/src/wolfharness/orchestrator/session_pool_runs.py @@ -11,14 +11,14 @@ from typing import TYPE_CHECKING, Any import uuid -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( RunErrorEvent, StreamCompleteEvent, ) -from agentpool.log import get_logger -from agentpool.orchestrator.run import RunHandle -from agentpool.utils.pydantic_ai_helpers import flatten_prompts +from wolfharness.log import get_logger +from wolfharness.orchestrator.run import RunHandle +from wolfharness.utils.pydantic_ai_helpers import flatten_prompts if TYPE_CHECKING: @@ -26,10 +26,10 @@ from pydantic_ai.messages import ModelMessage - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation import AgentPool - from agentpool.orchestrator.event_bus import EventBus - from agentpool.orchestrator.session_controller import SessionController, SessionState + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation import AgentPool + from wolfharness.orchestrator.event_bus import EventBus + from wolfharness.orchestrator.session_controller import SessionController, SessionState logger = get_logger(__name__) @@ -112,7 +112,7 @@ def _create_run_handle( SessionBusyError: If the session already has an active run that is not completed. """ - from agentpool.orchestrator.session_controller import SessionBusyError + from wolfharness.orchestrator.session_controller import SessionBusyError # Staleness check: prevent silently overwriting an active run. if session.current_run_id is not None and session.current_run_id in self.sessions._runs: @@ -142,7 +142,7 @@ def _create_run_handle( # Handle both list[ModelMessage] and MessageHistory objects. # Some callers (e.g. subagent_tools.py) pass MessageHistory() # which is not directly iterable as list[ModelMessage]. - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory if isinstance(message_history, MessageHistory): model_msgs: list[ModelMessage] = [] @@ -158,7 +158,7 @@ def _create_run_handle( if conversation is not None: for chat_msg in conversation.get_history(): model_messages.extend(chat_msg.messages) - from agentpool.orchestrator.run import inject_cancelled_tool_results + from wolfharness.orchestrator.run import inject_cancelled_tool_results run_handle._message_history = inject_cancelled_tool_results(model_messages) if deferred_tool_results is not None: diff --git a/src/agentpool/orchestrator/session_pool_teams.py b/src/wolfharness/orchestrator/session_pool_teams.py similarity index 91% rename from src/agentpool/orchestrator/session_pool_teams.py rename to src/wolfharness/orchestrator/session_pool_teams.py index b87f78163..7b7fc7bf7 100644 --- a/src/agentpool/orchestrator/session_pool_teams.py +++ b/src/wolfharness/orchestrator/session_pool_teams.py @@ -8,15 +8,15 @@ from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.delegation.base_team import BaseTeam - from agentpool.messaging.messagenode import MessageNode - from agentpool_config.teams import TeamConfig + from wolfharness.delegation.base_team import BaseTeam + from wolfharness.messaging.messagenode import MessageNode + from wolfharness_config.teams import TeamConfig logger = get_logger(__name__) @@ -32,7 +32,7 @@ def _build_team_from_config( This replaces the deprecated ``TeamConfig.get_team()`` method, keeping team instantiation in the core layer instead of the config layer. """ - from agentpool.delegation.base_team import BaseTeam + from wolfharness.delegation.base_team import BaseTeam member_configs = team_config.get_member_configs() @@ -84,7 +84,7 @@ async def create_team_from_config( ValueError: If a member name is not found in the manifest agents or teams section. """ - from agentpool_config.context import ConfigContextManager + from wolfharness_config.context import ConfigContextManager member_names = [team_config.get_member_name(m) for m in team_config.members] diff --git a/src/agentpool/orchestrator/turn.py b/src/wolfharness/orchestrator/turn.py similarity index 97% rename from src/agentpool/orchestrator/turn.py rename to src/wolfharness/orchestrator/turn.py index d371aa563..ebe46ccf3 100644 --- a/src/agentpool/orchestrator/turn.py +++ b/src/wolfharness/orchestrator/turn.py @@ -11,11 +11,11 @@ from pydantic_ai.messages import ModelMessage - from agentpool.agents.context import AgentRunContext - from agentpool.agents.events.events import RichAgentStreamEvent - from agentpool.hooks import AgentHooks - from agentpool.hooks.base import HookResult - from agentpool.messaging import ChatMessage + from wolfharness.agents.context import AgentRunContext + from wolfharness.agents.events.events import RichAgentStreamEvent + from wolfharness.hooks import AgentHooks + from wolfharness.hooks.base import HookResult + from wolfharness.messaging import ChatMessage class Turn(ABC): @@ -243,7 +243,7 @@ def _log_tool_execution( status: Execution status (``"completed"``, ``"cancelled"``, ``"failed"``, etc.). Defaults to ``"completed"``. """ - from agentpool.lifecycle.types import ToolExecutionRecord + from wolfharness.lifecycle.types import ToolExecutionRecord # Per-Turn idempotency guard using _logged_tools set. # This Turn instance is not reused across turns — _logged_tools diff --git a/src/agentpool/prompts/__init__.py b/src/wolfharness/prompts/__init__.py similarity index 100% rename from src/agentpool/prompts/__init__.py rename to src/wolfharness/prompts/__init__.py diff --git a/src/agentpool/prompts/base.py b/src/wolfharness/prompts/base.py similarity index 100% rename from src/agentpool/prompts/base.py rename to src/wolfharness/prompts/base.py diff --git a/src/agentpool/prompts/builtin_provider.py b/src/wolfharness/prompts/builtin_provider.py similarity index 91% rename from src/agentpool/prompts/builtin_provider.py rename to src/wolfharness/prompts/builtin_provider.py index 684653f18..c332b8748 100644 --- a/src/agentpool/prompts/builtin_provider.py +++ b/src/wolfharness/prompts/builtin_provider.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Any -from agentpool.prompts.base import BasePromptProvider -from agentpool_config.system_prompts import StaticPromptConfig +from wolfharness.prompts.base import BasePromptProvider +from wolfharness_config.system_prompts import StaticPromptConfig if TYPE_CHECKING: - from agentpool_config.system_prompts import StaticPromptConfig + from wolfharness_config.system_prompts import StaticPromptConfig class BuiltinPromptProvider(BasePromptProvider): diff --git a/src/agentpool/prompts/conversion_manager.py b/src/wolfharness/prompts/conversion_manager.py similarity index 95% rename from src/agentpool/prompts/conversion_manager.py rename to src/wolfharness/prompts/conversion_manager.py index 678618b91..60f660052 100644 --- a/src/agentpool/prompts/conversion_manager.py +++ b/src/wolfharness/prompts/conversion_manager.py @@ -4,8 +4,8 @@ from concurrent.futures import ThreadPoolExecutor from typing import TYPE_CHECKING, Any -from agentpool.mime_utils import guess_type -from agentpool_config.converters import ConversionConfig +from wolfharness.mime_utils import guess_type +from wolfharness_config.converters import ConversionConfig if TYPE_CHECKING: @@ -89,7 +89,7 @@ async def convert_content(self, content: Any, mime_type: str | None = None) -> s if __name__ == "__main__": from docler_config.converter_configs import MarkItDownConfig - from agentpool_config.converters import ConversionConfig + from wolfharness_config.converters import ConversionConfig config = ConversionConfig(providers=[MarkItDownConfig()]) manager = ConversionManager(config) diff --git a/src/agentpool/prompts/convert.py b/src/wolfharness/prompts/convert.py similarity index 94% rename from src/agentpool/prompts/convert.py rename to src/wolfharness/prompts/convert.py index ec80c9aa5..a25c1d513 100644 --- a/src/agentpool/prompts/convert.py +++ b/src/wolfharness/prompts/convert.py @@ -10,7 +10,7 @@ from toprompt import to_prompt from upathtools import UPath, read_path, to_upath -from agentpool.common_types import PathReference +from wolfharness.common_types import PathReference if TYPE_CHECKING: @@ -18,7 +18,7 @@ from pydantic_ai import UserContent - from agentpool.common_types import PromptCompatible + from wolfharness.common_types import PromptCompatible async def convert_prompts(prompts: Sequence[PromptCompatible]) -> list[UserContent]: @@ -34,7 +34,7 @@ async def convert_prompts(prompts: Sequence[PromptCompatible]) -> list[UserConte for p in prompts: match p: case PathReference(path=path, fs=fs, mime_type=mime_type, display_name=display_name): - from agentpool.repomap import get_resource_context + from wolfharness.repomap import get_resource_context # Add display link if available if display_name: @@ -50,7 +50,7 @@ async def convert_prompts(prompts: Sequence[PromptCompatible]) -> list[UserConte result.append(path) case os.PathLike() | UPath(): - from agentpool.mime_utils import guess_type + from wolfharness.mime_utils import guess_type path_obj = to_upath(p) # ty: ignore[invalid-argument-type] mime_type = guess_type(str(path_obj)) diff --git a/src/agentpool/prompts/instructions.py b/src/wolfharness/prompts/instructions.py similarity index 98% rename from src/agentpool/prompts/instructions.py rename to src/wolfharness/prompts/instructions.py index adf2a0adb..4e32ee14c 100644 --- a/src/agentpool/prompts/instructions.py +++ b/src/wolfharness/prompts/instructions.py @@ -29,7 +29,7 @@ def instruction(ctx: RunContext[AgentContext[Any]]) -> str: from pydantic_ai import RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext # Import runtime_checkable for protocol instance checking diff --git a/src/agentpool/prompts/manager.py b/src/wolfharness/prompts/manager.py similarity index 95% rename from src/agentpool/prompts/manager.py rename to src/wolfharness/prompts/manager.py index 40e3263bd..9687fbaf9 100644 --- a/src/agentpool/prompts/manager.py +++ b/src/wolfharness/prompts/manager.py @@ -9,14 +9,14 @@ from pydantic_ai import UserPromptPart from slashed import Command, CommandContext -from agentpool.log import get_logger -from agentpool.prompts.builtin_provider import BuiltinPromptProvider +from wolfharness.log import get_logger +from wolfharness.prompts.builtin_provider import BuiltinPromptProvider if TYPE_CHECKING: - from agentpool.agents.staged_content import StagedContent - from agentpool.prompts.base import BasePromptProvider - from agentpool_config.system_prompts import PromptLibraryConfig, StaticPromptConfig + from wolfharness.agents.staged_content import StagedContent + from wolfharness.prompts.base import BasePromptProvider + from wolfharness_config.system_prompts import PromptLibraryConfig, StaticPromptConfig logger = get_logger(__name__) diff --git a/src/agentpool/prompts/parts/zed.md b/src/wolfharness/prompts/parts/zed.md similarity index 87% rename from src/agentpool/prompts/parts/zed.md rename to src/wolfharness/prompts/parts/zed.md index 4c5dbfe7e..b3f587262 100644 --- a/src/agentpool/prompts/parts/zed.md +++ b/src/wolfharness/prompts/parts/zed.md @@ -10,8 +10,8 @@ When referencing code locations in responses, use markdown links with `file://` Line range format is `#L:` (1-based, inclusive). Examples: -- [toolset.py](file:///home/phil65/dev/oss/agentpool/src/agentpool_toolsets/openapi/toolset.py) -- [OpenAPITools class](file:///home/phil65/dev/oss/agentpool/src/agentpool_toolsets/openapi/toolset.py#L19:80) +- [toolset.py](file:///home/phil65/dev/oss/wolfharness/src/wolfharness_toolsets/openapi/toolset.py) +- [OpenAPITools class](file:///home/phil65/dev/oss/wolfharness/src/wolfharness_toolsets/openapi/toolset.py#L19:80) Use these clickable references when pointing to specific code locations. For showing actual code content, still use fenced code blocks with the path-based syntax. diff --git a/src/agentpool/prompts/prompts.py b/src/wolfharness/prompts/prompts.py similarity index 98% rename from src/agentpool/prompts/prompts.py rename to src/wolfharness/prompts/prompts.py index 8ae96b8c4..4aaa4f82a 100644 --- a/src/agentpool/prompts/prompts.py +++ b/src/wolfharness/prompts/prompts.py @@ -14,10 +14,10 @@ from slashed import Command from upathtools import UPath, to_upath -from agentpool.log import get_logger -from agentpool.mcp_server import MCPClient -from agentpool.utils import importing -from agentpool.utils.inspection import execute +from wolfharness.log import get_logger +from wolfharness.mcp_server import MCPClient +from wolfharness.utils import importing +from wolfharness.utils.inspection import execute if TYPE_CHECKING: @@ -29,7 +29,7 @@ from pydantic_ai import ModelRequestPart from slashed import CommandContext - from agentpool.agents.staged_content import StagedContent + from wolfharness.agents.staged_content import StagedContent logger = get_logger(__name__) @@ -355,7 +355,7 @@ def from_callable( else: description = description_override or f"Prompt from {name}" - from agentpool.utils.inspection import get_fn_qualname + from wolfharness.utils.inspection import get_fn_qualname path = f"{fn.__module__}.{get_fn_qualname(fn)}" return cls( @@ -467,7 +467,7 @@ async def get_components( RuntimeError: If prompt fetch fails ValueError: If prompt contains unsupported message types """ - from agentpool.mcp_server.conversions import content_block_as_text + from wolfharness.mcp_server.conversions import content_block_as_text try: result = await self.client.get_prompt(self.name, arguments) diff --git a/src/agentpool/py.typed b/src/wolfharness/py.typed similarity index 100% rename from src/agentpool/py.typed rename to src/wolfharness/py.typed diff --git a/src/agentpool/repomap/__init__.py b/src/wolfharness/repomap/__init__.py similarity index 81% rename from src/agentpool/repomap/__init__.py rename to src/wolfharness/repomap/__init__.py index 91255481d..5e0846bc4 100644 --- a/src/agentpool/repomap/__init__.py +++ b/src/wolfharness/repomap/__init__.py @@ -34,21 +34,21 @@ from __future__ import annotations -from agentpool.repomap.context import ( +from wolfharness.repomap.context import ( generate_directory_context, generate_file_context, get_resource_context, ) -from agentpool.repomap.core import RepoMap -from agentpool.repomap.types import FileInfo, RepoMapResult, TokenCounter -from agentpool.repomap.languages import ( +from wolfharness.repomap.core import RepoMap +from wolfharness.repomap.types import FileInfo, RepoMapResult, TokenCounter +from wolfharness.repomap.languages import ( get_supported_languages, get_supported_languages_md, is_language_supported, ) -from agentpool.repomap.outline import generate_file_outline, get_file_map_from_content -from agentpool.repomap.tags import Tag, get_tags_from_content -from agentpool.repomap.utils import find_src_files, is_important, truncate_with_notice +from wolfharness.repomap.outline import generate_file_outline, get_file_map_from_content +from wolfharness.repomap.tags import Tag, get_tags_from_content +from wolfharness.repomap.utils import find_src_files, is_important, truncate_with_notice __all__ = [ "FileInfo", diff --git a/src/agentpool/repomap/context.py b/src/wolfharness/repomap/context.py similarity index 95% rename from src/agentpool/repomap/context.py rename to src/wolfharness/repomap/context.py index 8c4747665..85cb95069 100644 --- a/src/agentpool/repomap/context.py +++ b/src/wolfharness/repomap/context.py @@ -20,7 +20,7 @@ from typing import TYPE_CHECKING -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -89,9 +89,9 @@ async def generate_directory_context( """ from upathtools.filesystems import AsyncLocalFileSystem - from agentpool.repomap.core import RepoMap - from agentpool.repomap.languages import is_language_supported - from agentpool.repomap.utils import is_important + from wolfharness.repomap.core import RepoMap + from wolfharness.repomap.languages import is_language_supported + from wolfharness.repomap.utils import is_important logger.info("Generating directory context", path=str(path)) fs = fs or AsyncLocalFileSystem() @@ -172,8 +172,8 @@ async def generate_file_context(path: Path, fs: AsyncFileSystem | None = None) - Returns: File context string or None if generation fails """ - from agentpool.repomap.outline import generate_file_outline - from agentpool.repomap.utils import truncate_with_notice + from wolfharness.repomap.outline import generate_file_outline + from wolfharness.repomap.utils import truncate_with_notice logger.info("Generating file context", path=str(path)) try: diff --git a/src/agentpool/repomap/core.py b/src/wolfharness/repomap/core.py similarity index 98% rename from src/agentpool/repomap/core.py rename to src/wolfharness/repomap/core.py index 38689098e..d317eb915 100644 --- a/src/agentpool/repomap/core.py +++ b/src/wolfharness/repomap/core.py @@ -12,8 +12,8 @@ import re from typing import TYPE_CHECKING, Any, ClassVar, cast -from agentpool.repomap.tags import Tag, get_tags_from_content -from agentpool.repomap.utils import MIN_TOKEN_SAMPLE_SIZE, get_rel_path +from wolfharness.repomap.tags import Tag, get_tags_from_content +from wolfharness.repomap.utils import MIN_TOKEN_SAMPLE_SIZE, get_rel_path if TYPE_CHECKING: @@ -21,7 +21,7 @@ from fsspec import AbstractFileSystem - from agentpool.repomap.types import FileInfo, RepoMapResult, TokenCounter + from wolfharness.repomap.types import FileInfo, RepoMapResult, TokenCounter # Constants CACHE_VERSION = 3 @@ -37,7 +37,7 @@ class RepoMap: Uses async fsspec filesystem for non-blocking IO operations. """ - TAGS_CACHE_DIR: ClassVar[str] = f".agentpool.tags.cache.v{CACHE_VERSION}" + TAGS_CACHE_DIR: ClassVar[str] = f".wolfharness.tags.cache.v{CACHE_VERSION}" warned_files: ClassVar[set[str]] = set() def __init__( @@ -100,7 +100,7 @@ async def _cat_file(self, path: str) -> str | None: async def _info(self, path: str) -> FileInfo | None: """Get file info.""" - from agentpool.repomap.types import FileInfo + from wolfharness.repomap.types import FileInfo try: info = await self.fs._info(path) @@ -232,7 +232,7 @@ async def get_map_with_metadata( boost_files: Files to boost in ranking. boost_idents: Identifiers to boost in ranking. """ - from agentpool.repomap.types import RepoMapResult + from wolfharness.repomap.types import RepoMapResult if not files: return RepoMapResult( diff --git a/src/agentpool/repomap/languages.py b/src/wolfharness/repomap/languages.py similarity index 96% rename from src/agentpool/repomap/languages.py rename to src/wolfharness/repomap/languages.py index ee4e04a77..c30b0b89d 100644 --- a/src/agentpool/repomap/languages.py +++ b/src/wolfharness/repomap/languages.py @@ -18,7 +18,9 @@ def get_scm_fname(lang: str) -> Path | None: from importlib import resources fname = f"{lang}-tags.scm" - ref = resources.files("agentpool") / "repomap" / "queries" / "tree-sitter-language-pack" / fname + ref = ( + resources.files("wolfharness") / "repomap" / "queries" / "tree-sitter-language-pack" / fname + ) # Try to get the path if isinstance(ref, os.PathLike): diff --git a/src/agentpool/repomap/outline.py b/src/wolfharness/repomap/outline.py similarity index 97% rename from src/agentpool/repomap/outline.py rename to src/wolfharness/repomap/outline.py index 5ed4059fe..5eda6d151 100644 --- a/src/agentpool/repomap/outline.py +++ b/src/wolfharness/repomap/outline.py @@ -10,8 +10,8 @@ if TYPE_CHECKING: from fsspec.asyn import AsyncFileSystem -from agentpool.repomap.languages import is_language_supported -from agentpool.repomap.tags import get_tags_from_content +from wolfharness.repomap.languages import is_language_supported +from wolfharness.repomap.tags import get_tags_from_content async def generate_file_outline( diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/README.md b/src/wolfharness/repomap/queries/tree-sitter-language-pack/README.md similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/README.md rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/README.md diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/arduino-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/arduino-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/arduino-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/arduino-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/c-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/c-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/c-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/c-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/chatito-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/chatito-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/chatito-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/chatito-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/clojure-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/clojure-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/clojure-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/clojure-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/commonlisp-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/commonlisp-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/commonlisp-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/commonlisp-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/cpp-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/cpp-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/cpp-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/cpp-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/csharp-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/csharp-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/csharp-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/csharp-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/d-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/d-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/d-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/d-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/dart-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/dart-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/dart-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/dart-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/elisp-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/elisp-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/elisp-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/elisp-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/elixir-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/elixir-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/elixir-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/elixir-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/elm-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/elm-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/elm-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/elm-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/gleam-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/gleam-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/gleam-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/gleam-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/go-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/go-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/go-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/go-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/java-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/java-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/java-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/java-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/javascript-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/javascript-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/javascript-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/javascript-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/lua-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/lua-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/lua-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/lua-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/matlab-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/matlab-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/matlab-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/matlab-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/ocaml-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/ocaml-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/ocaml-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/ocaml-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/ocaml_interface-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/ocaml_interface-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/ocaml_interface-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/ocaml_interface-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/pony-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/pony-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/pony-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/pony-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/properties-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/properties-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/properties-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/properties-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/python-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/python-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/python-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/python-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/r-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/r-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/r-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/r-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/racket-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/racket-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/racket-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/racket-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/ruby-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/ruby-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/ruby-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/ruby-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/rust-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/rust-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/rust-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/rust-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/solidity-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/solidity-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/solidity-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/solidity-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/swift-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/swift-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/swift-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/swift-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-language-pack/udev-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-language-pack/udev-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-language-pack/udev-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-language-pack/udev-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/README.md b/src/wolfharness/repomap/queries/tree-sitter-languages/README.md similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/README.md rename to src/wolfharness/repomap/queries/tree-sitter-languages/README.md diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/c-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/c-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/c-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/c-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/c_sharp-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/c_sharp-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/c_sharp-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/c_sharp-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/cpp-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/cpp-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/cpp-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/cpp-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/dart-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/dart-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/dart-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/dart-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/elisp-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/elisp-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/elisp-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/elisp-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/elixir-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/elixir-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/elixir-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/elixir-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/elm-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/elm-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/elm-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/elm-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/fortran-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/fortran-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/fortran-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/fortran-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/go-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/go-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/go-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/go-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/haskell-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/haskell-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/haskell-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/haskell-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/hcl-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/hcl-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/hcl-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/hcl-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/java-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/java-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/java-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/java-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/javascript-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/javascript-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/javascript-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/javascript-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/julia-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/julia-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/julia-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/julia-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/kotlin-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/kotlin-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/kotlin-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/kotlin-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/matlab-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/matlab-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/matlab-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/matlab-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/ocaml-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/ocaml-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/ocaml-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/ocaml-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/ocaml_interface-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/ocaml_interface-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/ocaml_interface-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/ocaml_interface-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/php-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/php-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/php-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/php-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/python-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/python-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/python-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/python-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/ql-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/ql-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/ql-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/ql-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/ruby-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/ruby-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/ruby-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/ruby-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/rust-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/rust-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/rust-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/rust-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/scala-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/scala-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/scala-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/scala-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/typescript-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/typescript-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/typescript-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/typescript-tags.scm diff --git a/src/agentpool/repomap/queries/tree-sitter-languages/zig-tags.scm b/src/wolfharness/repomap/queries/tree-sitter-languages/zig-tags.scm similarity index 100% rename from src/agentpool/repomap/queries/tree-sitter-languages/zig-tags.scm rename to src/wolfharness/repomap/queries/tree-sitter-languages/zig-tags.scm diff --git a/src/agentpool/repomap/tags.py b/src/wolfharness/repomap/tags.py similarity index 98% rename from src/agentpool/repomap/tags.py rename to src/wolfharness/repomap/tags.py index 8930bdb69..7461ff342 100644 --- a/src/agentpool/repomap/tags.py +++ b/src/wolfharness/repomap/tags.py @@ -41,7 +41,7 @@ def get_tags_from_content(content: str, filename: str) -> list[Tag]: # noqa: PL from tree_sitter import Query, QueryCursor from tree_sitter_language_pack import SupportedLanguage - from agentpool.repomap.languages import get_scm_fname + from wolfharness.repomap.languages import get_scm_fname val = filename_to_lang(filename) if not val: diff --git a/src/agentpool/repomap/types.py b/src/wolfharness/repomap/types.py similarity index 100% rename from src/agentpool/repomap/types.py rename to src/wolfharness/repomap/types.py diff --git a/src/agentpool/repomap/utils.py b/src/wolfharness/repomap/utils.py similarity index 100% rename from src/agentpool/repomap/utils.py rename to src/wolfharness/repomap/utils.py diff --git a/src/wolfharness/running/__init__.py b/src/wolfharness/running/__init__.py new file mode 100644 index 000000000..d5f759fda --- /dev/null +++ b/src/wolfharness/running/__init__.py @@ -0,0 +1,20 @@ +"""Decorator auto-injection / run package.""" + +from __future__ import annotations + +from wolfharness.running.decorators import with_nodes +from wolfharness.running.discovery import NodeFunction, node_function +from wolfharness.running.executor import execute_single, execute_functions +from wolfharness.running.run_nodes import run_nodes_async, run_nodes +from wolfharness.running.injection import NodeInjectionError + +__all__ = [ + "NodeFunction", + "NodeInjectionError", + "execute_functions", + "execute_single", + "node_function", + "run_nodes", + "run_nodes_async", + "with_nodes", +] diff --git a/src/agentpool/running/decorators.py b/src/wolfharness/running/decorators.py similarity index 94% rename from src/agentpool/running/decorators.py rename to src/wolfharness/running/decorators.py index 66860eef6..9b6b0f769 100644 --- a/src/agentpool/running/decorators.py +++ b/src/wolfharness/running/decorators.py @@ -6,13 +6,13 @@ import inspect from typing import TYPE_CHECKING, overload -from agentpool.running.injection import inject_nodes +from wolfharness.running.injection import inject_nodes if TYPE_CHECKING: from collections.abc import Awaitable, Callable - from agentpool import AgentPool + from wolfharness import AgentPool @overload diff --git a/src/agentpool/running/discovery.py b/src/wolfharness/running/discovery.py similarity index 95% rename from src/agentpool/running/discovery.py rename to src/wolfharness/running/discovery.py index 5ce7b2ed7..8a84ab19b 100644 --- a/src/agentpool/running/discovery.py +++ b/src/wolfharness/running/discovery.py @@ -7,14 +7,14 @@ import inspect from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool.utils.inspection import get_fn_name +from wolfharness.log import get_logger +from wolfharness.utils.inspection import get_fn_name if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.common_types import AnyCallable + from wolfharness.common_types import AnyCallable logger = get_logger(__name__) diff --git a/src/agentpool/running/executor.py b/src/wolfharness/running/executor.py similarity index 97% rename from src/agentpool/running/executor.py rename to src/wolfharness/running/executor.py index de838ce20..6f6143cf2 100644 --- a/src/agentpool/running/executor.py +++ b/src/wolfharness/running/executor.py @@ -11,15 +11,15 @@ import anyio -from agentpool.log import get_logger -from agentpool.running import with_nodes +from wolfharness.log import get_logger +from wolfharness.running import with_nodes if TYPE_CHECKING: import os - from agentpool import AgentPool - from agentpool.running.discovery import NodeFunction + from wolfharness import AgentPool + from wolfharness.running.discovery import NodeFunction logger = get_logger(__name__) diff --git a/src/agentpool/running/injection.py b/src/wolfharness/running/injection.py similarity index 95% rename from src/agentpool/running/injection.py rename to src/wolfharness/running/injection.py index f7bc3c015..c9a836158 100644 --- a/src/agentpool/running/injection.py +++ b/src/wolfharness/running/injection.py @@ -6,14 +6,14 @@ import typing from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool.utils.inspection import get_fn_qualname +from wolfharness.log import get_logger +from wolfharness.utils.inspection import get_fn_qualname if TYPE_CHECKING: from collections.abc import Callable - from agentpool import AgentPool, MessageNode + from wolfharness import AgentPool, MessageNode logger = get_logger(__name__) @@ -21,7 +21,7 @@ def is_node_type(typ: Any) -> bool: """Check if a type is or inherits from MessageNode.""" - from agentpool import MessageNode + from wolfharness import MessageNode if typ is MessageNode: return True diff --git a/src/agentpool/running/py.typed b/src/wolfharness/running/py.typed similarity index 100% rename from src/agentpool/running/py.typed rename to src/wolfharness/running/py.typed diff --git a/src/agentpool/running/run_nodes.py b/src/wolfharness/running/run_nodes.py similarity index 93% rename from src/agentpool/running/run_nodes.py rename to src/wolfharness/running/run_nodes.py index 86226cc34..3734ce124 100644 --- a/src/agentpool/running/run_nodes.py +++ b/src/wolfharness/running/run_nodes.py @@ -5,13 +5,13 @@ import asyncio from typing import TYPE_CHECKING, Any -from agentpool.running.executor import discover_functions, execute_functions +from wolfharness.running.executor import discover_functions, execute_functions if TYPE_CHECKING: from upathtools import JoinablePathLike - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.manifest import AgentsManifest async def run_nodes_async( @@ -44,7 +44,7 @@ async def analyze(analyzer: Agent) -> str: print(results["analyze"]) ``` """ - from agentpool import AgentPool + from wolfharness import AgentPool # Find functions to run if module: diff --git a/src/agentpool/server.py b/src/wolfharness/server.py similarity index 97% rename from src/agentpool/server.py rename to src/wolfharness/server.py index 70ab21cb2..122c5a706 100644 --- a/src/agentpool/server.py +++ b/src/wolfharness/server.py @@ -5,13 +5,13 @@ import abc from typing import TYPE_CHECKING, Any, Self -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: from types import TracebackType - from agentpool import AgentPool + from wolfharness import AgentPool logger = get_logger(__name__) diff --git a/src/agentpool/sessions/__init__.py b/src/wolfharness/sessions/__init__.py similarity index 60% rename from src/agentpool/sessions/__init__.py rename to src/wolfharness/sessions/__init__.py index dacf804a4..d7d4f1f71 100644 --- a/src/agentpool/sessions/__init__.py +++ b/src/wolfharness/sessions/__init__.py @@ -1,12 +1,12 @@ """Session data models.""" -from agentpool.sessions.models import ProjectData, SessionData -from agentpool.sessions.state_mapper import ( +from wolfharness.sessions.models import ProjectData, SessionData +from wolfharness.sessions.state_mapper import ( InvariantResult, SessionStateMapper, VALID_SESSION_STATUSES, ) -from agentpool_storage.protocols import SessionPersistence +from wolfharness_storage.protocols import SessionPersistence __all__ = [ "VALID_SESSION_STATUSES", diff --git a/src/agentpool/sessions/models.py b/src/wolfharness/sessions/models.py similarity index 98% rename from src/agentpool/sessions/models.py rename to src/wolfharness/sessions/models.py index 18918cdcd..34e1f0e46 100644 --- a/src/agentpool/sessions/models.py +++ b/src/wolfharness/sessions/models.py @@ -8,13 +8,13 @@ from pydantic import Field from schemez import Schema -from agentpool.utils.time_utils import get_now +from wolfharness.utils.time_utils import get_now class ProjectData(Schema): """Persistable project/worktree state. - Represents a codebase/worktree that agentpool operates on. + Represents a codebase/worktree that wolfharness operates on. Sessions are associated with projects. """ diff --git a/src/agentpool/sessions/state_mapper.py b/src/wolfharness/sessions/state_mapper.py similarity index 98% rename from src/agentpool/sessions/state_mapper.py rename to src/wolfharness/sessions/state_mapper.py index 3c6f3ab39..a39400a3b 100644 --- a/src/agentpool/sessions/state_mapper.py +++ b/src/wolfharness/sessions/state_mapper.py @@ -42,7 +42,7 @@ if TYPE_CHECKING: - from agentpool.lifecycle.types import RunState + from wolfharness.lifecycle.types import RunState # Valid SessionData.status values (source of truth) @@ -109,7 +109,7 @@ def status_to_expected_run_state( ``RunState`` mapping exists (e.g., ``checkpointed`` with no ``RunHandle``, or ``closed`` with no ``RunHandle``). """ - from agentpool.lifecycle.types import RunState + from wolfharness.lifecycle.types import RunState if status == SESSION_STATUS_ACTIVE: # active maps to IDLE when idle, RUNNING when a turn is @@ -142,7 +142,7 @@ def run_state_to_expected_status(run_state: RunState) -> str: Returns: The expected ``SessionData.status`` string. """ - from agentpool.lifecycle.types import RunState + from wolfharness.lifecycle.types import RunState if run_state is RunState.IDLE: return SESSION_STATUS_ACTIVE diff --git a/src/agentpool/skills/AGENTS.md b/src/wolfharness/skills/AGENTS.md similarity index 97% rename from src/agentpool/skills/AGENTS.md rename to src/wolfharness/skills/AGENTS.md index 9cb6ca6a5..c31665c44 100644 --- a/src/agentpool/skills/AGENTS.md +++ b/src/wolfharness/skills/AGENTS.md @@ -13,7 +13,7 @@ | SkillCommand (slash commands) | `command.py` | | Skill URI resolver (`skill://`) | `uri_resolver.py` | | SkillsInstructionProvider (XML injection) | `instruction_provider.py` | -| Skill config models | `agentpool_config/skills.py` | +| Skill config models | `wolfharness_config/skills.py` | ## Conventions diff --git a/src/wolfharness/skills/__init__.py b/src/wolfharness/skills/__init__.py new file mode 100644 index 000000000..bbfad5da9 --- /dev/null +++ b/src/wolfharness/skills/__init__.py @@ -0,0 +1,6 @@ +"""Skills package for Claude Code Skills support.""" + +from wolfharness.skills.manager import SkillsManager +from wolfharness.skills.skill import Skill, to_prompt + +__all__ = ["Skill", "SkillsManager", "to_prompt"] diff --git a/src/agentpool/skills/command.py b/src/wolfharness/skills/command.py similarity index 97% rename from src/agentpool/skills/command.py rename to src/wolfharness/skills/command.py index 72380300f..963155fab 100644 --- a/src/agentpool/skills/command.py +++ b/src/wolfharness/skills/command.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill @dataclass(frozen=True) diff --git a/src/agentpool/skills/exceptions.py b/src/wolfharness/skills/exceptions.py similarity index 97% rename from src/agentpool/skills/exceptions.py rename to src/wolfharness/skills/exceptions.py index 6b8dd6c24..a8654358e 100644 --- a/src/agentpool/skills/exceptions.py +++ b/src/wolfharness/skills/exceptions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from agentpool.utils.baseregistry import AgentPoolError +from wolfharness.utils.baseregistry import AgentPoolError class SkillError(AgentPoolError): diff --git a/src/agentpool/skills/manager.py b/src/wolfharness/skills/manager.py similarity index 95% rename from src/agentpool/skills/manager.py rename to src/wolfharness/skills/manager.py index e56ea1440..c3d95cc5c 100644 --- a/src/agentpool/skills/manager.py +++ b/src/wolfharness/skills/manager.py @@ -6,9 +6,9 @@ from upathtools import JoinablePathLike, UPath, to_upath -from agentpool.log import get_logger -from agentpool.skills.registry import SkillsRegistry -from agentpool_config.skills import SkillsConfig # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.skills.registry import SkillsRegistry +from wolfharness_config.skills import SkillsConfig # noqa: TC001 if TYPE_CHECKING: @@ -16,7 +16,7 @@ from fsspec import AbstractFileSystem - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill logger = get_logger(__name__) @@ -124,7 +124,7 @@ async def discover_skills( config: Optional skills configuration. config_file_path: Optional path to configuration file for resolving relative paths. """ - from agentpool_config.skills import DEFAULT_SKILLS_PATHS + from wolfharness_config.skills import DEFAULT_SKILLS_PATHS base_path = config_file_path.parent if config_file_path is not None else UPath.cwd() logger.debug("Starting skills discovery", base_path=str(base_path)) diff --git a/src/agentpool/skills/registry.py b/src/wolfharness/skills/registry.py similarity index 97% rename from src/agentpool/skills/registry.py rename to src/wolfharness/skills/registry.py index d732d5a84..95f9a803e 100644 --- a/src/agentpool/skills/registry.py +++ b/src/wolfharness/skills/registry.py @@ -8,10 +8,10 @@ from upathtools import is_directory from upathtools.helpers import to_upath, upath_to_fs -from agentpool.log import get_logger -from agentpool.skills.skill import Skill -from agentpool.tools.exceptions import ToolError -from agentpool.utils.baseregistry import BaseRegistry +from wolfharness.log import get_logger +from wolfharness.skills.skill import Skill +from wolfharness.tools.exceptions import ToolError +from wolfharness.utils.baseregistry import BaseRegistry if TYPE_CHECKING: @@ -199,7 +199,7 @@ def __delitem__(self, key: str) -> None: import anyio from upathtools import UPath - from agentpool.log import configure_logging + from wolfharness.log import configure_logging configure_logging() diff --git a/src/agentpool/skills/skill.py b/src/wolfharness/skills/skill.py similarity index 99% rename from src/agentpool/skills/skill.py rename to src/wolfharness/skills/skill.py index 58b07f2ec..b67172e36 100644 --- a/src/agentpool/skills/skill.py +++ b/src/wolfharness/skills/skill.py @@ -13,7 +13,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from upathtools import UPath -from agentpool_config.skills import SkillMcpServerConfig, SkillToolConfig +from wolfharness_config.skills import SkillMcpServerConfig, SkillToolConfig # Type for skill paths - can be UPath (for local filesystem skills) diff --git a/src/agentpool/skills/skill_tool_manager.py b/src/wolfharness/skills/skill_tool_manager.py similarity index 96% rename from src/agentpool/skills/skill_tool_manager.py rename to src/wolfharness/skills/skill_tool_manager.py index 5ef7092f3..5905730e1 100644 --- a/src/agentpool/skills/skill_tool_manager.py +++ b/src/wolfharness/skills/skill_tool_manager.py @@ -6,11 +6,11 @@ import logging from typing import TYPE_CHECKING -from agentpool.tools.base import Tool +from wolfharness.tools.base import Tool if TYPE_CHECKING: - from agentpool_config.skills import SkillToolConfig + from wolfharness_config.skills import SkillToolConfig logger = logging.getLogger(__name__) diff --git a/src/agentpool/skills/uri_resolver.py b/src/wolfharness/skills/uri_resolver.py similarity index 97% rename from src/agentpool/skills/uri_resolver.py rename to src/wolfharness/skills/uri_resolver.py index 4bd42807a..9791a9fae 100644 --- a/src/agentpool/skills/uri_resolver.py +++ b/src/wolfharness/skills/uri_resolver.py @@ -19,16 +19,16 @@ import unicodedata from urllib.parse import unquote, urlparse -from agentpool.skills.exceptions import SecurityError, SkillNotFoundError +from wolfharness.skills.exceptions import SecurityError, SkillNotFoundError if TYPE_CHECKING: - from agentpool.capabilities.resource_protocols import ( + from wolfharness.capabilities.resource_protocols import ( SkillEntry, SkillResource, ) -from agentpool.skills.skill import Skill +from wolfharness.skills.skill import Skill logger = logging.getLogger(__name__) @@ -296,7 +296,7 @@ async def _find_skill_in_providers( Returns: The matching Skill or None if not found. """ - from agentpool.capabilities.resource_protocols import SkillResource + from wolfharness.capabilities.resource_protocols import SkillResource for provider in self._providers.values(): # Check for new SkillResource protocol first. @@ -398,7 +398,7 @@ async def resolve(self, uri: str) -> Skill: """ # Delegate to ExtensionRegistry when available. if self._extension_registry is not None: - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel result = await self._extension_registry.resolve_uri(uri, Scope(level=ScopeLevel.POOL)) if result is not None: diff --git a/src/agentpool/storage/__init__.py b/src/wolfharness/storage/__init__.py similarity index 71% rename from src/agentpool/storage/__init__.py rename to src/wolfharness/storage/__init__.py index 01841d777..702148e10 100644 --- a/src/agentpool/storage/__init__.py +++ b/src/wolfharness/storage/__init__.py @@ -1,7 +1,7 @@ """Storage package.""" -from agentpool.storage.manager import StorageManager -from agentpool.storage.serialization import ( +from wolfharness.storage.manager import StorageManager +from wolfharness.storage.serialization import ( deserialize_messages, deserialize_parts, serialize_messages, diff --git a/src/agentpool/storage/manager.py b/src/wolfharness/storage/manager.py similarity index 98% rename from src/agentpool/storage/manager.py rename to src/wolfharness/storage/manager.py index 23b66b1e9..ba22a9de2 100644 --- a/src/agentpool/storage/manager.py +++ b/src/wolfharness/storage/manager.py @@ -12,13 +12,13 @@ import anyio from pydantic import BaseModel, TypeAdapter -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.sessions.models import PendingDeferredCall -from agentpool.storage.serialization import deserialize_messages -from agentpool.utils.identifiers import generate_session_id -from agentpool_config.session import SessionQuery -from agentpool_config.storage import StorageConfig +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.sessions.models import PendingDeferredCall +from wolfharness.storage.serialization import deserialize_messages +from wolfharness.utils.identifiers import generate_session_id +from wolfharness_config.session import SessionQuery +from wolfharness_config.storage import StorageConfig if TYPE_CHECKING: @@ -28,10 +28,10 @@ from pydantic_ai.messages import ModelMessage - from agentpool.common_types import JsonValue - from agentpool.sessions.models import ProjectData, SessionData - from agentpool_config.storage import BaseStorageProviderConfig - from agentpool_storage.base import StorageProvider + from wolfharness.common_types import JsonValue + from wolfharness.sessions.models import ProjectData, SessionData + from wolfharness_config.storage import BaseStorageProviderConfig + from wolfharness_storage.base import StorageProvider logger = get_logger(__name__) @@ -749,8 +749,8 @@ async def _generate_title_core( Returns: SessionMetadata with title, emoji, and icon, or None if generation fails. """ - from agentpool import Agent - from agentpool.utils.model_helpers import infer_model + from wolfharness import Agent + from wolfharness.utils.model_helpers import infer_model logger.info("_generate_title_core called", session_id=session_id) if not self.config.title_generation_model: diff --git a/src/agentpool/storage/serialization.py b/src/wolfharness/storage/serialization.py similarity index 98% rename from src/agentpool/storage/serialization.py rename to src/wolfharness/storage/serialization.py index 290774062..d2da20dc8 100644 --- a/src/agentpool/storage/serialization.py +++ b/src/wolfharness/storage/serialization.py @@ -8,8 +8,8 @@ from pydantic_ai import ModelMessage, ModelRequestPart, ModelResponsePart from pydantic_ai.messages import UserContent -from agentpool.log import get_logger -from agentpool.sessions.models import PendingDeferredCall +from wolfharness.log import get_logger +from wolfharness.sessions.models import PendingDeferredCall if TYPE_CHECKING: diff --git a/src/wolfharness/talk/__init__.py b/src/wolfharness/talk/__init__.py new file mode 100644 index 000000000..878cdc12c --- /dev/null +++ b/src/wolfharness/talk/__init__.py @@ -0,0 +1,13 @@ +"""Talk classes.""" + +from wolfharness.talk.stats import TalkStats, AggregatedTalkStats +from wolfharness.talk.talk import Talk, TeamTalk +from wolfharness.talk.registry import ConnectionRegistry + +__all__ = [ + "AggregatedTalkStats", + "ConnectionRegistry", + "Talk", + "TalkStats", + "TeamTalk", +] diff --git a/src/agentpool/talk/graph_edges.py b/src/wolfharness/talk/graph_edges.py similarity index 96% rename from src/agentpool/talk/graph_edges.py rename to src/wolfharness/talk/graph_edges.py index 27bd57da0..109b3fe1b 100644 --- a/src/agentpool/talk/graph_edges.py +++ b/src/wolfharness/talk/graph_edges.py @@ -1,7 +1,7 @@ """Translate Talk configuration into GraphBuilder edges and Path transforms. This module provides :class:`TalkEdgeTranslator`, which converts AgentPool -:class:`~agentpool.talk.Talk` instances into ``pydantic-graph`` +:class:`~wolfharness.talk.Talk` instances into ``pydantic-graph`` :class:`~pydantic_graph.paths.EdgePath` objects. Key mappings: @@ -25,7 +25,7 @@ from pydantic_graph.paths import TransformFunction from pydantic_graph.step import StepContext # noqa: TC002 -from agentpool.utils.inspection import is_async_callable +from wolfharness.utils.inspection import is_async_callable if TYPE_CHECKING: @@ -36,8 +36,8 @@ from pydantic_graph.paths import EdgePath from pydantic_graph.step import Step - from agentpool.messaging import MessageNode - from agentpool.talk import Talk + from wolfharness.messaging import MessageNode + from wolfharness.talk import Talk # --------------------------------------------------------------------------- @@ -94,7 +94,7 @@ def _make_sync_matches( """ def _matches(inputs: Any) -> bool: - from agentpool.talk.registry import EventContext + from wolfharness.talk.registry import EventContext ctx = EventContext( message=inputs, @@ -116,7 +116,7 @@ def _matches(inputs: Any) -> bool: @dataclass class TalkEdgeTranslator: - """Translates :class:`~agentpool.talk.Talk` connections into graph edges. + """Translates :class:`~wolfharness.talk.Talk` connections into graph edges. Attributes: builder: The :class:`GraphBuilder` receiving translated edges. @@ -244,7 +244,7 @@ def _build_transform_step(self, talk: Talk[Any]) -> Step: assert transform_fn is not None async def _transform_step(ctx: StepContext) -> Any: - from agentpool.utils.inspection import execute + from wolfharness.utils.inspection import execute return await execute(transform_fn, ctx.inputs) @@ -359,8 +359,8 @@ def _build_async_condition_step( """Create a :class:`Step` that evaluates an async condition.""" async def _eval_step(ctx: StepContext) -> _ConditionResult: - from agentpool.talk.registry import EventContext - from agentpool.utils.inspection import execute + from wolfharness.talk.registry import EventContext + from wolfharness.utils.inspection import execute # Evaluate against the first target (stop/exit are "any target") event_ctx: EventContext[Any] = EventContext( @@ -427,8 +427,8 @@ def _build_async_filter_step( """Create a :class:`Step` that evaluates an async filter condition.""" async def _eval_filter(ctx: StepContext) -> _ConditionResult: - from agentpool.talk.registry import EventContext - from agentpool.utils.inspection import execute + from wolfharness.talk.registry import EventContext + from wolfharness.utils.inspection import execute event_ctx: EventContext[Any] = EventContext( message=ctx.inputs, diff --git a/src/agentpool/talk/registry.py b/src/wolfharness/talk/registry.py similarity index 91% rename from src/agentpool/talk/registry.py rename to src/wolfharness/talk/registry.py index 23144906e..20199422c 100644 --- a/src/agentpool/talk/registry.py +++ b/src/wolfharness/talk/registry.py @@ -7,16 +7,16 @@ from anyenv.signals import Signal -from agentpool.log import get_logger -from agentpool.talk.talk import Talk -from agentpool.utils.baseregistry import AgentPoolError, BaseRegistry +from wolfharness.log import get_logger +from wolfharness.talk.talk import Talk +from wolfharness.utils.baseregistry import AgentPoolError, BaseRegistry if TYPE_CHECKING: - from agentpool import MessageNode - from agentpool.messaging import ChatMessage - from agentpool.talk.stats import TalkStats - from agentpool_config.conditions import ConnectionCondition + from wolfharness import MessageNode + from wolfharness.messaging import ChatMessage + from wolfharness.talk.stats import TalkStats + from wolfharness_config.conditions import ConnectionCondition logger = get_logger(__name__) diff --git a/src/agentpool/talk/stats.py b/src/wolfharness/talk/stats.py similarity index 96% rename from src/agentpool/talk/stats.py rename to src/wolfharness/talk/stats.py index 9915abe54..882c0c2bb 100644 --- a/src/agentpool/talk/stats.py +++ b/src/wolfharness/talk/stats.py @@ -6,15 +6,15 @@ from functools import cached_property from typing import TYPE_CHECKING, Any -from agentpool.utils.time_utils import get_now +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: from collections.abc import MutableSequence, Sequence from datetime import datetime - from agentpool.messaging.messages import ChatMessage, FormatStyle - from agentpool.tools import ToolCallInfo + from wolfharness.messaging.messages import ChatMessage, FormatStyle + from wolfharness.tools import ToolCallInfo @dataclass(frozen=True, kw_only=True) diff --git a/src/agentpool/talk/talk.py b/src/wolfharness/talk/talk.py similarity index 95% rename from src/agentpool/talk/talk.py rename to src/wolfharness/talk/talk.py index ae7674842..91e372b7b 100644 --- a/src/agentpool/talk/talk.py +++ b/src/wolfharness/talk/talk.py @@ -10,11 +10,11 @@ from anyenv.signals import Signal -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.talk.stats import AggregatedTalkStats, TalkStats -from agentpool.utils.inspection import execute -from agentpool.utils.time_utils import get_now +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.talk.stats import AggregatedTalkStats, TalkStats +from wolfharness.utils.inspection import execute +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: @@ -23,17 +23,17 @@ from evented.event_data import EventData - from agentpool.common_types import ( + from wolfharness.common_types import ( AnyFilterFn, AnyTransformFn, ProcessorCallback, PromptCompatible, QueueStrategy, ) - from agentpool.messaging import MessageNode - from agentpool.messaging.events import ConnectionEventData - from agentpool_config.events import ConnectionEventType - from agentpool_config.forward_targets import ConnectionType + from wolfharness.messaging import MessageNode + from wolfharness.messaging.events import ConnectionEventData + from wolfharness_config.events import ConnectionEventType + from wolfharness_config.forward_targets import ConnectionType logger = get_logger(__name__) @@ -153,8 +153,8 @@ def __rshift__( connection >> other_agent # Connect to single agent connection >> (agent2 & agent3) # Connect to group """ - from agentpool import Agent, MessageNode - from agentpool.talk import TeamTalk + from wolfharness import Agent, MessageNode + from wolfharness.talk import TeamTalk match other: case Callable(): # ty: ignore[invalid-match-pattern] @@ -180,7 +180,7 @@ async def _evaluate_condition( default_return: bool = False, ) -> bool: """Evaluate a condition with flexible parameter handling.""" - from agentpool.talk.registry import EventContext + from wolfharness.talk.registry import EventContext if not condition: return default_return @@ -201,7 +201,7 @@ def on_event( callback: Callable[[ConnectionEventData[TTransmittedData]], Awaitable[None] | None], ) -> Self: """Register callback for connection events.""" - from agentpool.messaging.events import ConnectionEventData + from wolfharness.messaging.events import ConnectionEventData async def wrapped_callback(event: EventData) -> None: if isinstance(event, ConnectionEventData) and event.event_type == event_type: @@ -215,7 +215,7 @@ async def _emit_connection_event( event_type: ConnectionEventType, message: ChatMessage[TTransmittedData] | None, ) -> None: - from agentpool.messaging.events import ConnectionEventData + from wolfharness.messaging.events import ConnectionEventData event = ConnectionEventData[Any]( connection=self, @@ -302,8 +302,8 @@ async def _process_for_target( prompt: PromptCompatible | None = None, ) -> ChatMessage[Any] | None: """Process message for a single target.""" - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation.base_team import BaseTeam match self.connection_type: case "run": @@ -482,8 +482,8 @@ def __rshift__( connection >> other_agent # Connect to single agent connection >> (agent2 & agent3) # Connect to group """ - from agentpool import Agent, MessageNode - from agentpool.talk import TeamTalk + from wolfharness import Agent, MessageNode + from wolfharness.talk import TeamTalk match other: case Callable(): # ty: ignore[invalid-match-pattern] diff --git a/src/agentpool/tasks/__init__.py b/src/wolfharness/tasks/__init__.py similarity index 74% rename from src/agentpool/tasks/__init__.py rename to src/wolfharness/tasks/__init__.py index 9a84b4606..71a9685af 100644 --- a/src/agentpool/tasks/__init__.py +++ b/src/wolfharness/tasks/__init__.py @@ -1,6 +1,6 @@ """Task management.""" -from agentpool.tasks.exceptions import ( +from wolfharness.tasks.exceptions import ( JobError, ToolSkippedError, RunAbortedError, @@ -8,7 +8,7 @@ JobRegistrationError, ) -from agentpool.tasks.registry import TaskRegistry +from wolfharness.tasks.registry import TaskRegistry __all__ = [ "ChainAbortedError", diff --git a/src/agentpool/tasks/exceptions.py b/src/wolfharness/tasks/exceptions.py similarity index 88% rename from src/agentpool/tasks/exceptions.py rename to src/wolfharness/tasks/exceptions.py index 6ce600066..8a543bc8c 100644 --- a/src/agentpool/tasks/exceptions.py +++ b/src/wolfharness/tasks/exceptions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from agentpool.utils.baseregistry import AgentPoolError +from wolfharness.utils.baseregistry import AgentPoolError class JobError(AgentPoolError): diff --git a/src/agentpool/tasks/registry.py b/src/wolfharness/tasks/registry.py similarity index 79% rename from src/agentpool/tasks/registry.py rename to src/wolfharness/tasks/registry.py index 9ccfdf2d4..e531525c7 100644 --- a/src/agentpool/tasks/registry.py +++ b/src/wolfharness/tasks/registry.py @@ -4,9 +4,9 @@ from typing import Any -from agentpool.tasks.exceptions import JobRegistrationError -from agentpool.utils.baseregistry import BaseRegistry -from agentpool_config.task import Job +from wolfharness.tasks.exceptions import JobRegistrationError +from wolfharness.utils.baseregistry import BaseRegistry +from wolfharness_config.task import Job class TaskRegistry(BaseRegistry[str, Job[Any, Any]]): @@ -17,7 +17,7 @@ def _error_class(self) -> type[JobRegistrationError]: return JobRegistrationError def _validate_item(self, item: Any) -> Job[Any, Any]: - from agentpool_config.task import Job + from wolfharness_config.task import Job if not isinstance(item, Job): raise self._error_class(f"Expected Job, got {type(item)}") diff --git a/src/agentpool/testing.py b/src/wolfharness/testing.py similarity index 98% rename from src/agentpool/testing.py rename to src/wolfharness/testing.py index aaafc50c2..3293e83c4 100644 --- a/src/agentpool/testing.py +++ b/src/wolfharness/testing.py @@ -1,7 +1,7 @@ """Testing utilities for end-to-end ACP testing and CI integration. This module provides: -- A lightweight test harness for running end-to-end tests against the agentpool +- A lightweight test harness for running end-to-end tests against the wolfharness ACP server using ACPAgent as the client - GitHub CI integration for programmatically triggering and monitoring workflow runs @@ -39,8 +39,8 @@ async def test_commit_in_ci(): from evented_config import EventConfig - from agentpool.agents.acp_agent import ACPAgent - from agentpool.common_types import AnyEventHandlerType + from wolfharness.agents.acp_agent import ACPAgent + from wolfharness.common_types import AnyEventHandlerType @asynccontextmanager @@ -56,9 +56,9 @@ async def acp_test_session( event_configs: Sequence[EventConfig] | None = None, event_handlers: Sequence[AnyEventHandlerType] | None = None, ) -> AsyncIterator[ACPAgent[Any]]: - """Create an end-to-end ACP test session using agentpool as server. + """Create an end-to-end ACP test session using wolfharness as server. - This context manager starts an ACPAgent connected to a agentpool serve-acp + This context manager starts an ACPAgent connected to a wolfharness serve-acp subprocess, enabling full round-trip testing of the ACP protocol. Args: @@ -85,10 +85,10 @@ async def test_echo(): assert "Hello" in result.content ``` """ - from agentpool.agents.acp_agent import ACPAgent + from wolfharness.agents.acp_agent import ACPAgent # Build command line arguments - args = ["run", "agentpool", "serve-acp"] + args = ["run", "wolfharness", "serve-acp"] if config is not None: args.extend(["--config", str(config)]) diff --git a/src/agentpool/text_templates/__init__.py b/src/wolfharness/text_templates/__init__.py similarity index 93% rename from src/agentpool/text_templates/__init__.py rename to src/wolfharness/text_templates/__init__.py index d2beaa9fa..6b7069951 100644 --- a/src/agentpool/text_templates/__init__.py +++ b/src/wolfharness/text_templates/__init__.py @@ -6,7 +6,7 @@ from functools import cache from typing import Final, Literal -_RESOURCES = importlib.resources.files("agentpool.text_templates") +_RESOURCES = importlib.resources.files("wolfharness.text_templates") DEFAULT_SYSTEM_PROMPT: Final[str] = str(_RESOURCES / "system_prompt.jinja") """Path to the default system prompt template.""" diff --git a/src/agentpool/text_templates/system_prompt.jinja b/src/wolfharness/text_templates/system_prompt.jinja similarity index 100% rename from src/agentpool/text_templates/system_prompt.jinja rename to src/wolfharness/text_templates/system_prompt.jinja diff --git a/src/agentpool/text_templates/tool_call_default.jinja b/src/wolfharness/text_templates/tool_call_default.jinja similarity index 100% rename from src/agentpool/text_templates/tool_call_default.jinja rename to src/wolfharness/text_templates/tool_call_default.jinja diff --git a/src/agentpool/text_templates/tool_call_markdown.jinja b/src/wolfharness/text_templates/tool_call_markdown.jinja similarity index 100% rename from src/agentpool/text_templates/tool_call_markdown.jinja rename to src/wolfharness/text_templates/tool_call_markdown.jinja diff --git a/src/agentpool/text_templates/tool_call_simple.jinja b/src/wolfharness/text_templates/tool_call_simple.jinja similarity index 100% rename from src/agentpool/text_templates/tool_call_simple.jinja rename to src/wolfharness/text_templates/tool_call_simple.jinja diff --git a/src/agentpool/tool_impls/__init__.py b/src/wolfharness/tool_impls/__init__.py similarity index 100% rename from src/agentpool/tool_impls/__init__.py rename to src/wolfharness/tool_impls/__init__.py diff --git a/src/agentpool/tool_impls/agent_cli/__init__.py b/src/wolfharness/tool_impls/agent_cli/__init__.py similarity index 89% rename from src/agentpool/tool_impls/agent_cli/__init__.py rename to src/wolfharness/tool_impls/agent_cli/__init__.py index bd67797b8..7acdbfef1 100644 --- a/src/agentpool/tool_impls/agent_cli/__init__.py +++ b/src/wolfharness/tool_impls/agent_cli/__init__.py @@ -4,8 +4,8 @@ from typing import Literal -from agentpool.tool_impls.agent_cli.tool import AgentCliTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.agent_cli.tool import AgentCliTool +from wolfharness_config.tools import ToolHints __all__ = ["AgentCliTool", "create_agent_cli_tool"] diff --git a/src/agentpool/tool_impls/agent_cli/tool.py b/src/wolfharness/tool_impls/agent_cli/tool.py similarity index 96% rename from src/agentpool/tool_impls/agent_cli/tool.py rename to src/wolfharness/tool_impls/agent_cli/tool.py index c29880b32..866e0a1a5 100644 --- a/src/agentpool/tool_impls/agent_cli/tool.py +++ b/src/wolfharness/tool_impls/agent_cli/tool.py @@ -6,8 +6,8 @@ from io import StringIO from typing import TYPE_CHECKING -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.tools.base import Tool +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.tools.base import Tool if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/bash/__init__.py b/src/wolfharness/tool_impls/bash/__init__.py similarity index 94% rename from src/agentpool/tool_impls/bash/__init__.py rename to src/wolfharness/tool_impls/bash/__init__.py index c0781cae1..9356caa69 100644 --- a/src/agentpool/tool_impls/bash/__init__.py +++ b/src/wolfharness/tool_impls/bash/__init__.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Literal -from agentpool.tool_impls.bash.tool import BashTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.bash.tool import BashTool +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/bash/helpers.py b/src/wolfharness/tool_impls/bash/helpers.py similarity index 100% rename from src/agentpool/tool_impls/bash/helpers.py rename to src/wolfharness/tool_impls/bash/helpers.py diff --git a/src/agentpool/tool_impls/bash/tool.py b/src/wolfharness/tool_impls/bash/tool.py similarity index 97% rename from src/agentpool/tool_impls/bash/tool.py rename to src/wolfharness/tool_impls/bash/tool.py index 6fdb97e4b..5af9184ec 100644 --- a/src/agentpool/tool_impls/bash/tool.py +++ b/src/wolfharness/tool_impls/bash/tool.py @@ -14,10 +14,10 @@ ProcessStartedEvent, ) -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.tool_impls.bash.helpers import format_output, truncate_output -from agentpool.tools.base import Tool, ToolResult +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.tool_impls.bash.helpers import format_output, truncate_output +from wolfharness.tools.base import Tool, ToolResult if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/delete_path/__init__.py b/src/wolfharness/tool_impls/delete_path/__init__.py similarity index 93% rename from src/agentpool/tool_impls/delete_path/__init__.py rename to src/wolfharness/tool_impls/delete_path/__init__.py index 97eb9e03f..2f71feee4 100644 --- a/src/agentpool/tool_impls/delete_path/__init__.py +++ b/src/wolfharness/tool_impls/delete_path/__init__.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Literal -from agentpool.tool_impls.delete_path.tool import DeletePathTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.delete_path.tool import DeletePathTool +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/delete_path/tool.py b/src/wolfharness/tool_impls/delete_path/tool.py similarity index 96% rename from src/agentpool/tool_impls/delete_path/tool.py rename to src/wolfharness/tool_impls/delete_path/tool.py index bb9f85d39..a4f0dd70c 100644 --- a/src/agentpool/tool_impls/delete_path/tool.py +++ b/src/wolfharness/tool_impls/delete_path/tool.py @@ -6,9 +6,9 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.tools.base import Tool +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.tools.base import Tool if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/download_file/__init__.py b/src/wolfharness/tool_impls/download_file/__init__.py similarity index 94% rename from src/agentpool/tool_impls/download_file/__init__.py rename to src/wolfharness/tool_impls/download_file/__init__.py index b337772ae..194c4c1e6 100644 --- a/src/agentpool/tool_impls/download_file/__init__.py +++ b/src/wolfharness/tool_impls/download_file/__init__.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Literal -from agentpool.tool_impls.download_file.tool import DownloadFileTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.download_file.tool import DownloadFileTool +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/download_file/tool.py b/src/wolfharness/tool_impls/download_file/tool.py similarity index 97% rename from src/agentpool/tool_impls/download_file/tool.py rename to src/wolfharness/tool_impls/download_file/tool.py index 708142500..246dd96a3 100644 --- a/src/agentpool/tool_impls/download_file/tool.py +++ b/src/wolfharness/tool_impls/download_file/tool.py @@ -10,9 +10,9 @@ import anyio -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.tools.base import Tool +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.tools.base import Tool if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/execute_code/__init__.py b/src/wolfharness/tool_impls/execute_code/__init__.py similarity index 92% rename from src/agentpool/tool_impls/execute_code/__init__.py rename to src/wolfharness/tool_impls/execute_code/__init__.py index 810720d9a..e12ae58ca 100644 --- a/src/agentpool/tool_impls/execute_code/__init__.py +++ b/src/wolfharness/tool_impls/execute_code/__init__.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Literal -from agentpool.tool_impls.execute_code.tool import ExecuteCodeTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.execute_code.tool import ExecuteCodeTool +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/execute_code/tool.py b/src/wolfharness/tool_impls/execute_code/tool.py similarity index 97% rename from src/agentpool/tool_impls/execute_code/tool.py rename to src/wolfharness/tool_impls/execute_code/tool.py index 03cc222e7..09f0936eb 100644 --- a/src/agentpool/tool_impls/execute_code/tool.py +++ b/src/wolfharness/tool_impls/execute_code/tool.py @@ -15,9 +15,9 @@ ProcessStartedEvent, ) -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.tools.base import Tool +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.tools.base import Tool if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/grep/__init__.py b/src/wolfharness/tool_impls/grep/__init__.py similarity index 95% rename from src/agentpool/tool_impls/grep/__init__.py rename to src/wolfharness/tool_impls/grep/__init__.py index 25ec48242..7b7a23381 100644 --- a/src/agentpool/tool_impls/grep/__init__.py +++ b/src/wolfharness/tool_impls/grep/__init__.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Literal -from agentpool.tool_impls.grep.tool import GrepTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.grep.tool import GrepTool +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/grep/tool.py b/src/wolfharness/tool_impls/grep/tool.py similarity index 94% rename from src/agentpool/tool_impls/grep/tool.py rename to src/wolfharness/tool_impls/grep/tool.py index 38dd659da..9527bfb84 100644 --- a/src/agentpool/tool_impls/grep/tool.py +++ b/src/wolfharness/tool_impls/grep/tool.py @@ -6,9 +6,9 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.tools.base import Tool, ToolResult +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.tools.base import Tool, ToolResult if TYPE_CHECKING: @@ -17,7 +17,7 @@ from exxec import ExecutionEnvironment from fsspec.asyn import AsyncFileSystem - from agentpool_toolsets.fsspec_toolset.grep import GrepBackend + from wolfharness_toolsets.fsspec_toolset.grep import GrepBackend logger = get_logger(__name__) @@ -100,7 +100,7 @@ async def _grep( Returns: Grep results as formatted text """ - from agentpool_toolsets.fsspec_toolset.grep import ( + from wolfharness_toolsets.fsspec_toolset.grep import ( DEFAULT_EXCLUDE_PATTERNS, detect_grep_backend, grep_with_fsspec, @@ -172,7 +172,7 @@ async def _grep( output += "\n\n[Results truncated]" # Emit formatted content for UI display - from agentpool.agents.events import TextContentItem + from wolfharness.agents.events import TextContentItem await ctx.events.tool_call_progress( title=f"Found {match_count} matches", diff --git a/src/agentpool/tool_impls/list_directory/__init__.py b/src/wolfharness/tool_impls/list_directory/__init__.py similarity index 94% rename from src/agentpool/tool_impls/list_directory/__init__.py rename to src/wolfharness/tool_impls/list_directory/__init__.py index 8374a1bf0..c38201710 100644 --- a/src/agentpool/tool_impls/list_directory/__init__.py +++ b/src/wolfharness/tool_impls/list_directory/__init__.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Literal -from agentpool.tool_impls.list_directory.tool import ListDirectoryTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.list_directory.tool import ListDirectoryTool +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/list_directory/tool.py b/src/wolfharness/tool_impls/list_directory/tool.py similarity index 95% rename from src/agentpool/tool_impls/list_directory/tool.py rename to src/wolfharness/tool_impls/list_directory/tool.py index c28c5f502..d3c7f0a5d 100644 --- a/src/agentpool/tool_impls/list_directory/tool.py +++ b/src/wolfharness/tool_impls/list_directory/tool.py @@ -10,9 +10,9 @@ from upathtools import is_directory -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.tools.base import Tool, ToolResult +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.tools.base import Tool, ToolResult if TYPE_CHECKING: @@ -94,7 +94,7 @@ async def _list_directory( Returns: Markdown-formatted directory listing """ - from agentpool_toolsets.fsspec_toolset.helpers import format_directory_listing + from wolfharness_toolsets.fsspec_toolset.helpers import format_directory_listing path = self._resolve_path(path, ctx) msg = f"Listing directory: {path}" @@ -164,7 +164,7 @@ async def _list_directory( await ctx.events.file_operation("list", path=path, success=True) result = format_directory_listing(path, dirs, files, pattern) # Emit formatted content for UI display - from agentpool.agents.events import TextContentItem + from wolfharness.agents.events import TextContentItem await ctx.events.tool_call_progress( title=f"Listed: {path}", diff --git a/src/agentpool/tool_impls/question/__init__.py b/src/wolfharness/tool_impls/question/__init__.py similarity index 90% rename from src/agentpool/tool_impls/question/__init__.py rename to src/wolfharness/tool_impls/question/__init__.py index 012dd292a..e33a8b76e 100644 --- a/src/agentpool/tool_impls/question/__init__.py +++ b/src/wolfharness/tool_impls/question/__init__.py @@ -4,8 +4,8 @@ from typing import Literal -from agentpool.tool_impls.question.tool import QuestionTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.question.tool import QuestionTool +from wolfharness_config.tools import ToolHints __all__ = ["QuestionTool", "create_question_tool"] diff --git a/src/agentpool/tool_impls/question/tool.py b/src/wolfharness/tool_impls/question/tool.py similarity index 95% rename from src/agentpool/tool_impls/question/tool.py rename to src/wolfharness/tool_impls/question/tool.py index 3b697b3bf..e078f579f 100644 --- a/src/agentpool/tool_impls/question/tool.py +++ b/src/wolfharness/tool_impls/question/tool.py @@ -7,9 +7,9 @@ from pydantic_ai import ModelRetry -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.tasks.exceptions import RunAbortedError -from agentpool.tools.base import Tool, ToolResult +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.tasks.exceptions import RunAbortedError +from wolfharness.tools.base import Tool, ToolResult if TYPE_CHECKING: diff --git a/src/agentpool/tool_impls/read/__init__.py b/src/wolfharness/tool_impls/read/__init__.py similarity index 92% rename from src/agentpool/tool_impls/read/__init__.py rename to src/wolfharness/tool_impls/read/__init__.py index 928f188e5..5d06a561c 100644 --- a/src/agentpool/tool_impls/read/__init__.py +++ b/src/wolfharness/tool_impls/read/__init__.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Literal -from agentpool.tool_impls.read.tool import ReadTool -from agentpool_config.tools import ToolHints +from wolfharness.tool_impls.read.tool import ReadTool +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: from exxec import ExecutionEnvironment - from agentpool.prompts.conversion_manager import ConversionManager + from wolfharness.prompts.conversion_manager import ConversionManager __all__ = ["ReadTool", "create_read_tool"] @@ -82,7 +82,7 @@ def create_read_tool( ) # With converter for automatic markdown conversion - from agentpool.prompts.conversion_manager import ConversionManager + from wolfharness.prompts.conversion_manager import ConversionManager read = create_read_tool( converter=ConversionManager(), ) diff --git a/src/agentpool/tool_impls/read/tool.py b/src/wolfharness/tool_impls/read/tool.py similarity index 94% rename from src/agentpool/tool_impls/read/tool.py rename to src/wolfharness/tool_impls/read/tool.py index 66cd2de38..9af980606 100644 --- a/src/agentpool/tool_impls/read/tool.py +++ b/src/wolfharness/tool_impls/read/tool.py @@ -8,10 +8,10 @@ from pydantic_ai import BinaryContent -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.mime_utils import guess_type, is_binary_content, is_binary_mime -from agentpool.tools.base import Tool, ToolResult +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.mime_utils import guess_type, is_binary_content, is_binary_mime +from wolfharness.tools.base import Tool, ToolResult if TYPE_CHECKING: @@ -20,7 +20,7 @@ from exxec import ExecutionEnvironment from fsspec.asyn import AsyncFileSystem - from agentpool.prompts.conversion_manager import ConversionManager + from wolfharness.prompts.conversion_manager import ConversionManager logger = get_logger(__name__) @@ -103,7 +103,7 @@ async def _get_file_map( Returns: Structure map or None if language not supported """ - from agentpool.repomap import generate_file_outline + from wolfharness.repomap import generate_file_outline # Use centralized outline generation fs = self._get_fs(ctx) @@ -132,10 +132,10 @@ async def _read( # noqa: PLR0911, PLR0915 Text content for text files, BinaryContent for binary files (with optional dimension note as list when image was resized), or error string """ - from agentpool.agents.events import FileContentItem, LocationContentItem - from agentpool.repomap import truncate_with_notice - from agentpool_toolsets.fsspec_toolset.helpers import truncate_lines - from agentpool_toolsets.fsspec_toolset.image_utils import resize_image_if_needed + from wolfharness.agents.events import FileContentItem, LocationContentItem + from wolfharness.repomap import truncate_with_notice + from wolfharness_toolsets.fsspec_toolset.helpers import truncate_lines + from wolfharness_toolsets.fsspec_toolset.image_utils import resize_image_if_needed path = self._resolve_path(path, ctx) msg = f"Reading file: {path}" diff --git a/src/agentpool/tools/__init__.py b/src/wolfharness/tools/__init__.py similarity index 70% rename from src/agentpool/tools/__init__.py rename to src/wolfharness/tools/__init__.py index 8e0fe3fb7..6079f2ae3 100644 --- a/src/agentpool/tools/__init__.py +++ b/src/wolfharness/tools/__init__.py @@ -9,10 +9,10 @@ from pydantic_ai.exceptions import ApprovalRequired, CallDeferred -from agentpool.tools.base import FunctionTool, Tool -from agentpool.tools.exceptions import ToolError -from agentpool.tools.tool_call_info import ToolCallInfo -from agentpool.skills.registry import SkillsRegistry +from wolfharness.tools.base import FunctionTool, Tool +from wolfharness.tools.exceptions import ToolError +from wolfharness.tools.tool_call_info import ToolCallInfo +from wolfharness.skills.registry import SkillsRegistry __all__ = [ "ApprovalRequired", diff --git a/src/agentpool/tools/base.py b/src/wolfharness/tools/base.py similarity index 98% rename from src/agentpool/tools/base.py rename to src/wolfharness/tools/base.py index 2a0a39c1e..a6b823e66 100644 --- a/src/agentpool/tools/base.py +++ b/src/wolfharness/tools/base.py @@ -13,15 +13,15 @@ from pydantic_ai.tools import Tool as PydanticAiTool import schemez -from agentpool.log import get_logger -from agentpool.tools.exceptions import ToolError -from agentpool.utils.inspection import ( +from wolfharness.log import get_logger +from wolfharness.tools.exceptions import ToolError +from wolfharness.utils.inspection import ( dataclasses_no_defaults_repr, execute, get_fn_name, get_fn_qualname, ) -from agentpool_config.tools import ToolHints +from wolfharness_config.tools import ToolHints if TYPE_CHECKING: @@ -29,7 +29,7 @@ from pydantic_ai import RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext if TYPE_CHECKING: @@ -40,7 +40,7 @@ from pydantic_ai.tools import ToolDefinition from schemez import FunctionSchema, Property - from agentpool.common_types import ToolSource + from wolfharness.common_types import ToolSource # Tool state filter type ToolState = Literal["all", "enabled", "disabled"] @@ -59,7 +59,7 @@ "other", ] -TERMINAL_TOOL_METADATA_KEY = "agentpool_terminal" +TERMINAL_TOOL_METADATA_KEY = "wolfharness_terminal" _TERMINAL_TOOL_TRUE_VALUES = frozenset({"1", "true", "yes", "on"}) @@ -406,7 +406,7 @@ def apply_schema_override(base_schema: dict[str, Any]) -> dict[str, Any]: # Fallback: use schemez to generate schema from pydantic_ai import RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext # Use schema_override description if provided, otherwise use self.description desc = ( @@ -496,7 +496,7 @@ def schema_obj(self) -> FunctionSchema: """Get the OpenAI function schema for the tool.""" from pydantic_ai import RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext return schemez.create_schema( self.get_callable(), @@ -858,7 +858,7 @@ def from_callable( """Create a FunctionTool from a callable or import path string.""" if isinstance(fn, str): import_path = fn - from agentpool.utils import importing + from wolfharness.utils import importing callable_obj = importing.import_callable(fn) name = getattr(callable_obj, "__name__", "unknown") diff --git a/src/agentpool/tools/exceptions.py b/src/wolfharness/tools/exceptions.py similarity index 69% rename from src/agentpool/tools/exceptions.py rename to src/wolfharness/tools/exceptions.py index d46100c54..35d62428f 100644 --- a/src/agentpool/tools/exceptions.py +++ b/src/wolfharness/tools/exceptions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from agentpool.utils.baseregistry import AgentPoolError +from wolfharness.utils.baseregistry import AgentPoolError class ToolError(AgentPoolError): diff --git a/src/agentpool/tools/tool_call_info.py b/src/wolfharness/tools/tool_call_info.py similarity index 93% rename from src/agentpool/tools/tool_call_info.py rename to src/wolfharness/tools/tool_call_info.py index c78c1aca1..2e7e853fc 100644 --- a/src/agentpool/tools/tool_call_info.py +++ b/src/wolfharness/tools/tool_call_info.py @@ -9,12 +9,12 @@ from pydantic import Field from schemez import Schema -from agentpool.text_templates import get_tool_call_template -from agentpool.utils.time_utils import get_now +from wolfharness.text_templates import get_tool_call_template +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: - from agentpool.text_templates import FormatStyle + from wolfharness.text_templates import FormatStyle class ToolCallInfo(Schema): diff --git a/src/agentpool/tools/tool_wrapping.py b/src/wolfharness/tools/tool_wrapping.py similarity index 96% rename from src/agentpool/tools/tool_wrapping.py rename to src/wolfharness/tools/tool_wrapping.py index 16ccd7be5..6f50aacb8 100644 --- a/src/agentpool/tools/tool_wrapping.py +++ b/src/wolfharness/tools/tool_wrapping.py @@ -22,8 +22,8 @@ if TYPE_CHECKING: - from agentpool.agents.context import AgentContext - from agentpool.tools.base import Tool + from wolfharness.agents.context import AgentContext + from wolfharness.tools.base import Tool def wrap_tool_for_pydantic_ai(tool: Tool[Any]) -> Any: @@ -34,7 +34,7 @@ def wrap_tool_for_pydantic_ai(tool: Tool[Any]) -> Any: accepts ``RunContext`` (which carries ``AgentContext`` in ``deps``) and injects the ``AgentContext`` into the original tool call. """ - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext original_fn = tool.get_callable() sig = inspect.signature(original_fn) diff --git a/src/agentpool/ui/__init__.py b/src/wolfharness/ui/__init__.py similarity index 51% rename from src/agentpool/ui/__init__.py rename to src/wolfharness/ui/__init__.py index c40770ec5..77cf2e549 100644 --- a/src/agentpool/ui/__init__.py +++ b/src/wolfharness/ui/__init__.py @@ -1,2 +1,2 @@ -# src/agentpool/interfaces/__init__.py +# src/wolfharness/interfaces/__init__.py """Interface definitions for AgentPool.""" diff --git a/src/agentpool/ui/base.py b/src/wolfharness/ui/base.py similarity index 96% rename from src/agentpool/ui/base.py rename to src/wolfharness/ui/base.py index 360741ac7..11f405e24 100644 --- a/src/agentpool/ui/base.py +++ b/src/wolfharness/ui/base.py @@ -12,8 +12,8 @@ from mcp import types from pydantic import BaseModel - from agentpool.agents.context import AgentContext, ConfirmationResult - from agentpool.messaging.context import NodeContext + from wolfharness.agents.context import AgentContext, ConfirmationResult + from wolfharness.messaging.context import NodeContext class InputProvider(ABC): diff --git a/src/agentpool/ui/elicitation.py b/src/wolfharness/ui/elicitation.py similarity index 99% rename from src/agentpool/ui/elicitation.py rename to src/wolfharness/ui/elicitation.py index 0deceb522..456c1fc07 100644 --- a/src/agentpool/ui/elicitation.py +++ b/src/wolfharness/ui/elicitation.py @@ -1,6 +1,6 @@ """Elicitation types for user interaction requests. -These are agentpool's internal representation of elicitation requests, +These are wolfharness's internal representation of elicitation requests, independent of MCP or any other protocol. Adapters convert to/from protocol-specific formats. @@ -16,7 +16,7 @@ if TYPE_CHECKING: - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext # ============================================================================= diff --git a/src/agentpool/ui/mock_provider.py b/src/wolfharness/ui/mock_provider.py similarity index 92% rename from src/agentpool/ui/mock_provider.py rename to src/wolfharness/ui/mock_provider.py index aba76f4f4..9cb6e9e25 100644 --- a/src/agentpool/ui/mock_provider.py +++ b/src/wolfharness/ui/mock_provider.py @@ -5,14 +5,14 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Literal -from agentpool.ui.base import InputProvider +from wolfharness.ui.base import InputProvider if TYPE_CHECKING: from mcp import types - from agentpool.agents.context import AgentContext, ConfirmationResult - from agentpool.messaging.context import NodeContext + from wolfharness.agents.context import AgentContext, ConfirmationResult + from wolfharness.messaging.context import NodeContext InputMethod = Literal["get_input", "get_tool_confirmation", "get_elicitation"] diff --git a/src/agentpool/ui/stdlib_provider.py b/src/wolfharness/ui/stdlib_provider.py similarity index 94% rename from src/agentpool/ui/stdlib_provider.py rename to src/wolfharness/ui/stdlib_provider.py index 85413c771..e9da7efab 100644 --- a/src/agentpool/ui/stdlib_provider.py +++ b/src/wolfharness/ui/stdlib_provider.py @@ -9,16 +9,16 @@ import anyenv from mcp import types -from agentpool.log import get_logger -from agentpool.tools.exceptions import ToolError -from agentpool.ui.base import InputProvider +from wolfharness.log import get_logger +from wolfharness.tools.exceptions import ToolError +from wolfharness.ui.base import InputProvider if TYPE_CHECKING: from pydantic import BaseModel - from agentpool.agents.context import AgentContext, ConfirmationResult - from agentpool.messaging.context import NodeContext + from wolfharness.agents.context import AgentContext, ConfirmationResult + from wolfharness.messaging.context import NodeContext logger = get_logger(__name__) diff --git a/src/agentpool/utils/__init__.py b/src/wolfharness/utils/__init__.py similarity index 88% rename from src/agentpool/utils/__init__.py rename to src/wolfharness/utils/__init__.py index 7b63ffd29..abbf7399d 100644 --- a/src/agentpool/utils/__init__.py +++ b/src/wolfharness/utils/__init__.py @@ -15,15 +15,15 @@ def setup_env(env: jinja2.Environment) -> None: Args: env: The jinjarope environment to extend """ - from agentpool.agents.native_agent import Agent - from agentpool.functional import ( + from wolfharness.agents.native_agent import Agent + from wolfharness.functional import ( run_agent, run_agent_sync, get_structured, get_structured_multiple, pick_one, ) - from agentpool.jinja_filters import ( + from wolfharness.jinja_filters import ( pydantic_playground, pydantic_playground_iframe, pydantic_playground_link, diff --git a/src/agentpool/utils/baseregistry.py b/src/wolfharness/utils/baseregistry.py similarity index 100% rename from src/agentpool/utils/baseregistry.py rename to src/wolfharness/utils/baseregistry.py diff --git a/src/agentpool/utils/context_wrapping.py b/src/wolfharness/utils/context_wrapping.py similarity index 94% rename from src/agentpool/utils/context_wrapping.py rename to src/wolfharness/utils/context_wrapping.py index cb52a9a9a..6c5683a5e 100644 --- a/src/agentpool/utils/context_wrapping.py +++ b/src/wolfharness/utils/context_wrapping.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -16,9 +16,9 @@ from pydantic_ai import RunContext - from agentpool.prompts.instructions import InstructionFunc + from wolfharness.prompts.instructions import InstructionFunc -from agentpool.utils.inspection import ( +from wolfharness.utils.inspection import ( execute, get_argument_key, get_fn_name, @@ -88,7 +88,7 @@ async def from_run_context(ctx: RunContext) -> str: """ from pydantic_ai import RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext # Detect which contexts function expects agent_ctx_key = get_argument_key(fn, AgentContext) diff --git a/src/agentpool/utils/count_tokens.py b/src/wolfharness/utils/count_tokens.py similarity index 100% rename from src/agentpool/utils/count_tokens.py rename to src/wolfharness/utils/count_tokens.py diff --git a/src/agentpool/utils/dag.py b/src/wolfharness/utils/dag.py similarity index 100% rename from src/agentpool/utils/dag.py rename to src/wolfharness/utils/dag.py diff --git a/src/agentpool/utils/diffs.py b/src/wolfharness/utils/diffs.py similarity index 100% rename from src/agentpool/utils/diffs.py rename to src/wolfharness/utils/diffs.py diff --git a/src/agentpool/utils/file_watcher.py b/src/wolfharness/utils/file_watcher.py similarity index 99% rename from src/agentpool/utils/file_watcher.py rename to src/wolfharness/utils/file_watcher.py index b726150f5..63d9b40d4 100644 --- a/src/agentpool/utils/file_watcher.py +++ b/src/wolfharness/utils/file_watcher.py @@ -10,7 +10,7 @@ import anyenv -from agentpool import log +from wolfharness import log if TYPE_CHECKING: diff --git a/src/agentpool/utils/identifiers.py b/src/wolfharness/utils/identifiers.py similarity index 99% rename from src/agentpool/utils/identifiers.py rename to src/wolfharness/utils/identifiers.py index eff091074..6119efa1b 100644 --- a/src/agentpool/utils/identifiers.py +++ b/src/wolfharness/utils/identifiers.py @@ -13,7 +13,7 @@ import secrets from typing import Literal -from agentpool.utils.time_utils import now_ms +from wolfharness.utils.time_utils import now_ms PrefixType = Literal["session", "message", "permission", "user", "part", "pty", "call"] diff --git a/src/agentpool/utils/importing.py b/src/wolfharness/utils/importing.py similarity index 100% rename from src/agentpool/utils/importing.py rename to src/wolfharness/utils/importing.py diff --git a/src/agentpool/utils/inspection.py b/src/wolfharness/utils/inspection.py similarity index 98% rename from src/agentpool/utils/inspection.py rename to src/wolfharness/utils/inspection.py index ad78bec7c..fa86c5294 100644 --- a/src/agentpool/utils/inspection.py +++ b/src/wolfharness/utils/inspection.py @@ -24,10 +24,10 @@ if TYPE_CHECKING: from collections.abc import Awaitable, Callable - from agentpool.agents import AgentContext + from wolfharness.agents import AgentContext -PACKAGE_NAME = "agentpool" +PACKAGE_NAME = "wolfharness" def dataclasses_no_defaults_repr(self: Any) -> str: @@ -285,7 +285,7 @@ def call_with_context[T]( - Functions expecting AgentContext - Functions expecting context data """ - from agentpool.agents import AgentContext + from wolfharness.agents import AgentContext if inspect.ismethod(func): if get_argument_key(func, AgentContext): diff --git a/src/agentpool/utils/model_capabilities.py b/src/wolfharness/utils/model_capabilities.py similarity index 100% rename from src/agentpool/utils/model_capabilities.py rename to src/wolfharness/utils/model_capabilities.py diff --git a/src/agentpool/utils/model_helpers.py b/src/wolfharness/utils/model_helpers.py similarity index 98% rename from src/agentpool/utils/model_helpers.py rename to src/wolfharness/utils/model_helpers.py index b9f6eb0ec..1200c6ca0 100644 --- a/src/agentpool/utils/model_helpers.py +++ b/src/wolfharness/utils/model_helpers.py @@ -108,7 +108,7 @@ def _infer_single_model(model: str | Model) -> Model: # noqa: PLR0911 if model.startswith("anthropic-max:"): from pydantic_ai.models.anthropic import AnthropicModel - from agentpool.auth.anthropic_auth import AnthropicMaxProvider + from wolfharness.auth.anthropic_auth import AnthropicMaxProvider provider = AnthropicMaxProvider() model_name = model.removeprefix("anthropic-max:") @@ -130,7 +130,7 @@ def _infer_single_model(model: str | Model) -> Model: # noqa: PLR0911 return OpenAIChatModel(model_name=model_name, provider=prov) if model.startswith("import:"): - from agentpool.utils.importing import import_callable + from wolfharness.utils.importing import import_callable imported = import_callable(model.removeprefix("import:")) return imported() if isinstance(imported, type) else imported # type: ignore[return-value] diff --git a/src/agentpool/utils/network.py b/src/wolfharness/utils/network.py similarity index 100% rename from src/agentpool/utils/network.py rename to src/wolfharness/utils/network.py diff --git a/src/agentpool/utils/parse_time.py b/src/wolfharness/utils/parse_time.py similarity index 100% rename from src/agentpool/utils/parse_time.py rename to src/wolfharness/utils/parse_time.py diff --git a/src/agentpool/utils/pydantic_ai_helpers.py b/src/wolfharness/utils/pydantic_ai_helpers.py similarity index 99% rename from src/agentpool/utils/pydantic_ai_helpers.py rename to src/wolfharness/utils/pydantic_ai_helpers.py index fe0e635e6..f881cbbd3 100644 --- a/src/agentpool/utils/pydantic_ai_helpers.py +++ b/src/wolfharness/utils/pydantic_ai_helpers.py @@ -8,7 +8,7 @@ from pydantic_ai import AudioUrl, BinaryContent, DocumentUrl, ImageUrl, VideoUrl from pydantic_ai.messages import BaseToolCallPart -from agentpool.common_types import PathReference +from wolfharness.common_types import PathReference if TYPE_CHECKING: diff --git a/src/agentpool/utils/result_utils.py b/src/wolfharness/utils/result_utils.py similarity index 95% rename from src/agentpool/utils/result_utils.py rename to src/wolfharness/utils/result_utils.py index 6265d2026..c8f96bc1e 100644 --- a/src/agentpool/utils/result_utils.py +++ b/src/wolfharness/utils/result_utils.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: - from agentpool_config.output_types import StructuredResponseConfig + from wolfharness_config.output_types import StructuredResponseConfig def to_type( diff --git a/src/agentpool/utils/signatures.py b/src/wolfharness/utils/signatures.py similarity index 99% rename from src/agentpool/utils/signatures.py rename to src/wolfharness/utils/signatures.py index caca00cc7..704e3ab7a 100644 --- a/src/agentpool/utils/signatures.py +++ b/src/wolfharness/utils/signatures.py @@ -8,7 +8,7 @@ from pydantic._internal import _typing_extra -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -289,7 +289,7 @@ async def wrapper(*args: Any, **kwargs: Any) -> Any: wrapper.__doc__ = getattr(original_callable, "__doc__", None) wrapper.__module__ = getattr(original_callable, "__module__", None) # type: ignore[assignment] # ty: ignore[invalid-assignment] wrapper.__wrapped__ = original_callable # type: ignore[attr-defined] - wrapper.__agentpool_wrapped__ = original_callable # type: ignore[attr-defined] + wrapper.__wolfharness_wrapped__ = original_callable # type: ignore[attr-defined] # Create modified signature without context parameters try: diff --git a/src/agentpool/utils/streams.py b/src/wolfharness/utils/streams.py similarity index 99% rename from src/agentpool/utils/streams.py rename to src/wolfharness/utils/streams.py index 86e9e6af4..d6f3f9bb9 100644 --- a/src/agentpool/utils/streams.py +++ b/src/wolfharness/utils/streams.py @@ -41,7 +41,7 @@ def to_unified_diff(self) -> str: Returns: Unified diff string """ - from agentpool.utils.diffs import compute_unified_diff + from wolfharness.utils.diffs import compute_unified_diff return compute_unified_diff( self.old_content or "", diff --git a/src/agentpool/utils/subprocess_utils.py b/src/wolfharness/utils/subprocess_utils.py similarity index 99% rename from src/agentpool/utils/subprocess_utils.py rename to src/wolfharness/utils/subprocess_utils.py index 187fd6008..6fef5598f 100644 --- a/src/agentpool/utils/subprocess_utils.py +++ b/src/wolfharness/utils/subprocess_utils.py @@ -10,7 +10,7 @@ import anyio -from agentpool import log +from wolfharness import log if TYPE_CHECKING: diff --git a/src/agentpool/utils/task_group.py b/src/wolfharness/utils/task_group.py similarity index 99% rename from src/agentpool/utils/task_group.py rename to src/wolfharness/utils/task_group.py index f9641656d..cf9869e17 100644 --- a/src/agentpool/utils/task_group.py +++ b/src/wolfharness/utils/task_group.py @@ -7,7 +7,7 @@ import anyio -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool/utils/tasks.py b/src/wolfharness/utils/tasks.py similarity index 75% rename from src/agentpool/utils/tasks.py rename to src/wolfharness/utils/tasks.py index 0ac5fbc3f..1b64dbeea 100644 --- a/src/agentpool/utils/tasks.py +++ b/src/wolfharness/utils/tasks.py @@ -2,7 +2,7 @@ This module previously provided ``TaskManager`` and ``PrioritizedTask``. All call sites have been migrated to either ``ManagedTaskGroup`` -(see :mod:`agentpool.utils.task_group`) or direct ``asyncio`` task +(see :mod:`wolfharness.utils.task_group`) or direct ``asyncio`` task tracking via ``MessageNode._pending_tasks`` / ``MessageNode.spawn_task()``. """ @@ -14,7 +14,7 @@ class TaskManager: """Stub — TaskManager has been removed. - Use :class:`agentpool.utils.task_group.ManagedTaskGroup` for structured + Use :class:`wolfharness.utils.task_group.ManagedTaskGroup` for structured concurrency, or ``MessageNode.spawn_task()`` / ``MessageNode._pending_tasks`` for direct asyncio task tracking. """ @@ -22,6 +22,6 @@ class TaskManager: def __init__(self, *args: Any, **kwargs: Any) -> None: msg = ( "TaskManager is removed. Use ManagedTaskGroup " - "(agentpool.utils.task_group) or MessageNode.spawn_task() instead." + "(wolfharness.utils.task_group) or MessageNode.spawn_task() instead." ) raise RuntimeError(msg) diff --git a/src/agentpool/utils/thread_helpers.py b/src/wolfharness/utils/thread_helpers.py similarity index 100% rename from src/agentpool/utils/thread_helpers.py rename to src/wolfharness/utils/thread_helpers.py diff --git a/src/agentpool/utils/time_utils.py b/src/wolfharness/utils/time_utils.py similarity index 100% rename from src/agentpool/utils/time_utils.py rename to src/wolfharness/utils/time_utils.py diff --git a/src/agentpool/utils/todos.py b/src/wolfharness/utils/todos.py similarity index 100% rename from src/agentpool/utils/todos.py rename to src/wolfharness/utils/todos.py diff --git a/src/agentpool/utils/token_breakdown.py b/src/wolfharness/utils/token_breakdown.py similarity index 99% rename from src/agentpool/utils/token_breakdown.py rename to src/wolfharness/utils/token_breakdown.py index f20d6efeb..64d636ec8 100644 --- a/src/agentpool/utils/token_breakdown.py +++ b/src/wolfharness/utils/token_breakdown.py @@ -26,7 +26,7 @@ from pydantic_ai.models import Model from pydantic_ai.settings import ModelSettings - from agentpool.messaging.messages import TokenCost + from wolfharness.messaging.messages import TokenCost @dataclass @@ -129,7 +129,7 @@ async def calculate_usage_from_parts( Returns: Tuple of (RequestUsage, TokenCost or None) """ - from agentpool.messaging.messages import TokenCost + from wolfharness.messaging.messages import TokenCost model_for_count = model_name or "gpt-4" diff --git a/src/agentpool/utils/tool_schema.py b/src/wolfharness/utils/tool_schema.py similarity index 100% rename from src/agentpool/utils/tool_schema.py rename to src/wolfharness/utils/tool_schema.py diff --git a/src/agentpool/utils/usage.py b/src/wolfharness/utils/usage.py similarity index 100% rename from src/agentpool/utils/usage.py rename to src/wolfharness/utils/usage.py diff --git a/src/agentpool/utils/worktree.py b/src/wolfharness/utils/worktree.py similarity index 99% rename from src/agentpool/utils/worktree.py rename to src/wolfharness/utils/worktree.py index a5ab69c58..f2f4a8c51 100644 --- a/src/agentpool/utils/worktree.py +++ b/src/wolfharness/utils/worktree.py @@ -7,7 +7,7 @@ import random import shutil -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) diff --git a/src/agentpool/vfs_registry.py b/src/wolfharness/vfs_registry.py similarity index 98% rename from src/agentpool/vfs_registry.py rename to src/wolfharness/vfs_registry.py index 3ee8cbde7..643d55db7 100644 --- a/src/agentpool/vfs_registry.py +++ b/src/wolfharness/vfs_registry.py @@ -4,8 +4,8 @@ from upathtools import AsyncUPath, list_files, read_folder, read_path -from agentpool.log import get_logger -from agentpool.utils.baseregistry import AgentPoolError +from wolfharness.log import get_logger +from wolfharness.utils.baseregistry import AgentPoolError if TYPE_CHECKING: @@ -15,7 +15,7 @@ from upathtools import AsyncUPath, UPath from upathtools.filesystems import UnionFileSystem - from agentpool.models.manifest import ResourceConfig + from wolfharness.models.manifest import ResourceConfig logger = get_logger(__name__) diff --git a/src/wolfharness_bot/__init__.py b/src/wolfharness_bot/__init__.py new file mode 100644 index 000000000..85764d6c7 --- /dev/null +++ b/src/wolfharness_bot/__init__.py @@ -0,0 +1,3 @@ +"""wolfharness_bot - Chat channel integrations for wolfharness.""" + +from __future__ import annotations diff --git a/src/agentpool_bot/bus.py b/src/wolfharness_bot/bus.py similarity index 99% rename from src/agentpool_bot/bus.py rename to src/wolfharness_bot/bus.py index 3289da1a0..2d8ccf234 100644 --- a/src/agentpool_bot/bus.py +++ b/src/wolfharness_bot/bus.py @@ -7,7 +7,7 @@ from datetime import datetime from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool_bot/channels/__init__.py b/src/wolfharness_bot/channels/__init__.py similarity index 54% rename from src/agentpool_bot/channels/__init__.py rename to src/wolfharness_bot/channels/__init__.py index 500a79b5a..5c741f3b1 100644 --- a/src/agentpool_bot/channels/__init__.py +++ b/src/wolfharness_bot/channels/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from agentpool_bot.channels.base import BaseChannel -from agentpool_bot.channels.manager import ChannelManager +from wolfharness_bot.channels.base import BaseChannel +from wolfharness_bot.channels.manager import ChannelManager __all__ = ["BaseChannel", "ChannelManager"] diff --git a/src/agentpool_bot/channels/base.py b/src/wolfharness_bot/channels/base.py similarity index 94% rename from src/agentpool_bot/channels/base.py rename to src/wolfharness_bot/channels/base.py index aa1fe0e21..86759d8e9 100644 --- a/src/agentpool_bot/channels/base.py +++ b/src/wolfharness_bot/channels/base.py @@ -5,12 +5,12 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool_bot.bus import InboundMessage +from wolfharness.log import get_logger +from wolfharness_bot.bus import InboundMessage if TYPE_CHECKING: - from agentpool_bot.bus import MessageBus, OutboundMessage + from wolfharness_bot.bus import MessageBus, OutboundMessage logger = get_logger(__name__) @@ -20,7 +20,7 @@ class BaseChannel(ABC): """Abstract base class for chat channel implementations. Each channel (Telegram, Discord, etc.) should implement this interface - to integrate with the agentpool message bus. + to integrate with the wolfharness message bus. """ name: str = "base" diff --git a/src/agentpool_bot/channels/discord.py b/src/wolfharness_bot/channels/discord.py similarity index 95% rename from src/agentpool_bot/channels/discord.py rename to src/wolfharness_bot/channels/discord.py index a3eef3e46..efe88a968 100644 --- a/src/agentpool_bot/channels/discord.py +++ b/src/wolfharness_bot/channels/discord.py @@ -11,13 +11,13 @@ import httpx import websockets -from agentpool.log import get_logger -from agentpool_bot.channels.base import BaseChannel +from wolfharness.log import get_logger +from wolfharness_bot.channels.base import BaseChannel if TYPE_CHECKING: - from agentpool_bot.bus import MessageBus, OutboundMessage - from agentpool_bot.config import DiscordConfig + from wolfharness_bot.bus import MessageBus, OutboundMessage + from wolfharness_bot.config import DiscordConfig logger = get_logger(__name__) @@ -173,9 +173,9 @@ async def _identify(self) -> None: "token": self.config.token, "intents": self.config.intents, "properties": { - "os": "agentpool", - "browser": "agentpool", - "device": "agentpool", + "os": "wolfharness", + "browser": "wolfharness", + "device": "wolfharness", }, }, } @@ -216,7 +216,7 @@ async def _handle_message_create(self, payload: dict[str, Any]) -> None: content_parts = [content] if content else [] media_paths: list[str] = [] - media_dir = Path.home() / ".agentpool" / "media" + media_dir = Path.home() / ".wolfharness" / "media" media_dir.mkdir(parents=True, exist_ok=True) for attachment in payload.get("attachments") or []: url = attachment.get("url") diff --git a/src/agentpool_bot/channels/email.py b/src/wolfharness_bot/channels/email.py similarity index 97% rename from src/agentpool_bot/channels/email.py rename to src/wolfharness_bot/channels/email.py index d33ddf507..60917fd52 100644 --- a/src/agentpool_bot/channels/email.py +++ b/src/wolfharness_bot/channels/email.py @@ -17,15 +17,15 @@ import ssl from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool_bot.channels.base import BaseChannel +from wolfharness.log import get_logger +from wolfharness_bot.channels.base import BaseChannel if TYPE_CHECKING: from datetime import date - from agentpool_bot.bus import MessageBus, OutboundMessage - from agentpool_bot.config import EmailConfig + from wolfharness_bot.bus import MessageBus, OutboundMessage + from wolfharness_bot.config import EmailConfig @dataclass(frozen=True, slots=True) @@ -155,7 +155,7 @@ async def send(self, msg: OutboundMessage) -> None: logger.warning("Email channel missing recipient address") return - base_subject = self._last_subject_by_chat.get(to_addr, "agentpool reply") + base_subject = self._last_subject_by_chat.get(to_addr, "wolfharness reply") subject = self._reply_subject(base_subject) if isinstance(msg.metadata.get("subject"), str) and ( override := msg.metadata["subject"].strip() @@ -405,7 +405,7 @@ def _html_to_text(raw_html: str) -> str: return html.unescape(text) def _reply_subject(self, base_subject: str) -> str: - subject = (base_subject or "").strip() or "agentpool reply" + subject = (base_subject or "").strip() or "wolfharness reply" prefix = self.config.subject_prefix or "Re: " if subject.lower().startswith("re:"): return subject diff --git a/src/agentpool_bot/channels/manager.py b/src/wolfharness_bot/channels/manager.py similarity index 95% rename from src/agentpool_bot/channels/manager.py rename to src/wolfharness_bot/channels/manager.py index 52425a751..9de4d990a 100644 --- a/src/agentpool_bot/channels/manager.py +++ b/src/wolfharness_bot/channels/manager.py @@ -6,13 +6,13 @@ import contextlib from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool_bot.bus import MessageBus - from agentpool_bot.channels.base import BaseChannel - from agentpool_bot.config import ChannelsConfig + from wolfharness_bot.bus import MessageBus + from wolfharness_bot.channels.base import BaseChannel + from wolfharness_bot.config import ChannelsConfig logger = get_logger(__name__) diff --git a/src/agentpool_bot/channels/slack.py b/src/wolfharness_bot/channels/slack.py similarity index 96% rename from src/agentpool_bot/channels/slack.py rename to src/wolfharness_bot/channels/slack.py index fd02a2eba..d3fcaabae 100644 --- a/src/agentpool_bot/channels/slack.py +++ b/src/wolfharness_bot/channels/slack.py @@ -11,16 +11,16 @@ from slack_sdk.web.async_client import AsyncWebClient from slackify_markdown import slackify_markdown -from agentpool.log import get_logger -from agentpool.utils.time_utils import get_now -from agentpool_bot.channels.base import BaseChannel +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import get_now +from wolfharness_bot.channels.base import BaseChannel if TYPE_CHECKING: from slack_sdk.socket_mode.request import SocketModeRequest - from agentpool_bot.bus import MessageBus, OutboundMessage - from agentpool_bot.config import SlackConfig + from wolfharness_bot.bus import MessageBus, OutboundMessage + from wolfharness_bot.config import SlackConfig logger = get_logger(__name__) diff --git a/src/agentpool_bot/channels/telegram.py b/src/wolfharness_bot/channels/telegram.py similarity index 97% rename from src/agentpool_bot/channels/telegram.py rename to src/wolfharness_bot/channels/telegram.py index fc7b181f2..f83c89cc8 100644 --- a/src/agentpool_bot/channels/telegram.py +++ b/src/wolfharness_bot/channels/telegram.py @@ -11,8 +11,8 @@ from telegram.ext import Application, CommandHandler, MessageHandler, filters from telegram.request import HTTPXRequest -from agentpool.log import get_logger -from agentpool_bot.channels.base import BaseChannel +from wolfharness.log import get_logger +from wolfharness_bot.channels.base import BaseChannel if TYPE_CHECKING: @@ -20,8 +20,8 @@ from telegram._files._basemedium import _BaseMedium from telegram.ext import ContextTypes - from agentpool_bot.bus import MessageBus, OutboundMessage - from agentpool_bot.config import TelegramConfig + from wolfharness_bot.bus import MessageBus, OutboundMessage + from wolfharness_bot.config import TelegramConfig logger = get_logger(__name__) @@ -283,7 +283,7 @@ async def _on_start(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> user = update.effective_user await update.message.reply_text( - f"Hi {user.first_name}! I'm an agentpool bot.\n\n" + f"Hi {user.first_name}! I'm an wolfharness bot.\n\n" "Send me a message and I'll respond!\n" "Type /help to see available commands." ) @@ -351,7 +351,7 @@ async def _on_message( # noqa: PLR0915 getattr(media_file, "mime_type", None), ) - media_dir = Path.home() / ".agentpool" / "media" + media_dir = Path.home() / ".wolfharness" / "media" media_dir.mkdir(parents=True, exist_ok=True) file_path = media_dir / f"{media_file.file_id[:16]}{ext}" @@ -360,7 +360,7 @@ async def _on_message( # noqa: PLR0915 media_paths.append(str(file_path)) if media_type in ("voice", "audio"): - from agentpool_bot.transcription import GroqTranscriptionProvider + from wolfharness_bot.transcription import GroqTranscriptionProvider transcriber = GroqTranscriptionProvider(api_key=self.groq_api_key) transcription = await transcriber.transcribe(file_path) diff --git a/src/agentpool_bot/channels/whatsapp.py b/src/wolfharness_bot/channels/whatsapp.py similarity index 95% rename from src/agentpool_bot/channels/whatsapp.py rename to src/wolfharness_bot/channels/whatsapp.py index 188855201..836c5a4ea 100644 --- a/src/agentpool_bot/channels/whatsapp.py +++ b/src/wolfharness_bot/channels/whatsapp.py @@ -7,15 +7,15 @@ import anyenv -from agentpool.log import get_logger -from agentpool_bot.channels.base import BaseChannel +from wolfharness.log import get_logger +from wolfharness_bot.channels.base import BaseChannel if TYPE_CHECKING: from websockets import ClientConnection - from agentpool_bot.bus import MessageBus, OutboundMessage - from agentpool_bot.config import WhatsAppConfig + from wolfharness_bot.bus import MessageBus, OutboundMessage + from wolfharness_bot.config import WhatsAppConfig logger = get_logger(__name__) diff --git a/src/agentpool_bot/config.py b/src/wolfharness_bot/config.py similarity index 86% rename from src/agentpool_bot/config.py rename to src/wolfharness_bot/config.py index 1b31ba898..3b7d7500f 100644 --- a/src/agentpool_bot/config.py +++ b/src/wolfharness_bot/config.py @@ -1,4 +1,4 @@ -"""Configuration models for agentpool_bot channels.""" +"""Configuration models for wolfharness_bot channels.""" from __future__ import annotations @@ -9,12 +9,12 @@ if TYPE_CHECKING: - from agentpool_bot.bus import MessageBus - from agentpool_bot.channels.discord import DiscordChannel - from agentpool_bot.channels.email import EmailChannel - from agentpool_bot.channels.slack import SlackChannel - from agentpool_bot.channels.telegram import TelegramChannel - from agentpool_bot.channels.whatsapp import WhatsAppChannel + from wolfharness_bot.bus import MessageBus + from wolfharness_bot.channels.discord import DiscordChannel + from wolfharness_bot.channels.email import EmailChannel + from wolfharness_bot.channels.slack import SlackChannel + from wolfharness_bot.channels.telegram import TelegramChannel + from wolfharness_bot.channels.whatsapp import WhatsAppChannel class BaseChannelConfig(BaseModel): @@ -34,7 +34,7 @@ class WhatsAppConfig(BaseChannelConfig): allow_from: list[str] = Field(default_factory=list) def get_provider(self, bus: MessageBus) -> WhatsAppChannel: - from agentpool_bot.channels.whatsapp import WhatsAppChannel + from wolfharness_bot.channels.whatsapp import WhatsAppChannel return WhatsAppChannel(self, bus) @@ -49,7 +49,7 @@ class TelegramConfig(BaseChannelConfig): proxy: str | None = None def get_provider(self, bus: MessageBus) -> TelegramChannel: - from agentpool_bot.channels.telegram import TelegramChannel + from wolfharness_bot.channels.telegram import TelegramChannel return TelegramChannel(self, bus) @@ -65,7 +65,7 @@ class DiscordConfig(BaseChannelConfig): intents: int = 37377 # GUILDS + GUILD_MESSAGES + DIRECT_MESSAGES + MESSAGE_CONTENT def get_provider(self, bus: MessageBus) -> DiscordChannel: - from agentpool_bot.channels.discord import DiscordChannel + from wolfharness_bot.channels.discord import DiscordChannel return DiscordChannel(self, bus) @@ -121,7 +121,7 @@ def _check_credentials_when_enabled(self) -> EmailConfig: allow_from: list[str] = Field(default_factory=list) def get_provider(self, bus: MessageBus) -> EmailChannel: - from agentpool_bot.channels.email import EmailChannel + from wolfharness_bot.channels.email import EmailChannel return EmailChannel(self, bus) @@ -151,7 +151,7 @@ class SlackConfig(BaseChannelConfig): dm: SlackDMConfig = Field(default_factory=SlackDMConfig) def get_provider(self, bus: MessageBus) -> SlackChannel: - from agentpool_bot.channels.slack import SlackChannel + from wolfharness_bot.channels.slack import SlackChannel return SlackChannel(self, bus) diff --git a/src/agentpool_bot/cron/__init__.py b/src/wolfharness_bot/cron/__init__.py similarity index 76% rename from src/agentpool_bot/cron/__init__.py rename to src/wolfharness_bot/cron/__init__.py index 727452437..569999a0c 100644 --- a/src/agentpool_bot/cron/__init__.py +++ b/src/wolfharness_bot/cron/__init__.py @@ -2,8 +2,8 @@ from __future__ import annotations -from agentpool_bot.cron.service import CronService -from agentpool_bot.cron.cron_types import ( +from wolfharness_bot.cron.service import CronService +from wolfharness_bot.cron.cron_types import ( CronJob, CronJobState, CronPayload, diff --git a/src/agentpool_bot/cron/cron_types.py b/src/wolfharness_bot/cron/cron_types.py similarity index 100% rename from src/agentpool_bot/cron/cron_types.py rename to src/wolfharness_bot/cron/cron_types.py diff --git a/src/agentpool_bot/cron/service.py b/src/wolfharness_bot/cron/service.py similarity index 98% rename from src/agentpool_bot/cron/service.py rename to src/wolfharness_bot/cron/service.py index 7a98af2ec..4d6237b0b 100644 --- a/src/agentpool_bot/cron/service.py +++ b/src/wolfharness_bot/cron/service.py @@ -7,16 +7,16 @@ from typing import TYPE_CHECKING import uuid -from agentpool.log import get_logger -from agentpool.utils.time_utils import now_ms -from agentpool_bot.cron.cron_types import CronJob, CronJobState, CronPayload, CronStore +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import now_ms +from wolfharness_bot.cron.cron_types import CronJob, CronJobState, CronPayload, CronStore if TYPE_CHECKING: from collections.abc import Awaitable, Callable from pathlib import Path - from agentpool_bot.cron.cron_types import CronSchedule + from wolfharness_bot.cron.cron_types import CronSchedule logger = get_logger(__name__) diff --git a/src/agentpool_bot/heartbeat.py b/src/wolfharness_bot/heartbeat.py similarity index 98% rename from src/agentpool_bot/heartbeat.py rename to src/wolfharness_bot/heartbeat.py index e56f1708e..0bd9d4a15 100644 --- a/src/agentpool_bot/heartbeat.py +++ b/src/wolfharness_bot/heartbeat.py @@ -2,7 +2,7 @@ The service reads ``HEARTBEAT.md`` from the workspace directory at a configurable interval. When the file contains actionable content the -``on_heartbeat`` callback is invoked (typically wired to an agentpool +``on_heartbeat`` callback is invoked (typically wired to an wolfharness agent) so the agent can process whatever tasks are listed. """ @@ -11,7 +11,7 @@ import asyncio from typing import TYPE_CHECKING -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool_bot/message_tools.py b/src/wolfharness_bot/message_tools.py similarity index 93% rename from src/agentpool_bot/message_tools.py rename to src/wolfharness_bot/message_tools.py index d7ffd78e6..892665edc 100644 --- a/src/agentpool_bot/message_tools.py +++ b/src/wolfharness_bot/message_tools.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool.tools.base import Tool - from agentpool_bot.bus import MessageBus + from wolfharness.tools.base import Tool + from wolfharness_bot.bus import MessageBus logger = get_logger(__name__) @@ -84,7 +84,7 @@ async def send_message( Returns: Confirmation string, or an error message. """ - from agentpool_bot.bus import OutboundMessage + from wolfharness_bot.bus import OutboundMessage resolved_channel = channel or self.default_channel resolved_chat_id = chat_id or self.default_chat_id diff --git a/src/agentpool_bot/transcription.py b/src/wolfharness_bot/transcription.py similarity index 98% rename from src/agentpool_bot/transcription.py rename to src/wolfharness_bot/transcription.py index 59a949e34..24b5923d9 100644 --- a/src/agentpool_bot/transcription.py +++ b/src/wolfharness_bot/transcription.py @@ -7,7 +7,7 @@ import httpx -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) diff --git a/src/agentpool_cli/__init__.py b/src/wolfharness_cli/__init__.py similarity index 90% rename from src/agentpool_cli/__init__.py rename to src/wolfharness_cli/__init__.py index 71ffb0e98..ddaab3360 100644 --- a/src/agentpool_cli/__init__.py +++ b/src/wolfharness_cli/__init__.py @@ -1,8 +1,8 @@ -"""CLI commands for agentpool.""" +"""CLI commands for wolfharness.""" from __future__ import annotations -from agentpool_cli.store import ConfigStore +from wolfharness_cli.store import ConfigStore agent_store = ConfigStore("agents.json") diff --git a/src/agentpool_cli/__main__.py b/src/wolfharness_cli/__main__.py similarity index 68% rename from src/agentpool_cli/__main__.py rename to src/wolfharness_cli/__main__.py index 13372ef32..cb49f1c88 100644 --- a/src/agentpool_cli/__main__.py +++ b/src/wolfharness_cli/__main__.py @@ -6,22 +6,22 @@ import typer as t -from agentpool_cli import log -from agentpool_cli.agent import add_agent_file, list_agents, list_configs, set_active_file -from agentpool_cli.cli_types import LogLevel # noqa: TC001 -from agentpool_cli.config_info import config_cli -from agentpool_cli.history import history_cli -from agentpool_cli.run import run_command -from agentpool_cli.serve_acp import acp_command -from agentpool_cli.serve_agui import agui_command -from agentpool_cli.serve_api import api_command -from agentpool_cli.serve_mcp import serve_command -from agentpool_cli.serve_opencode import opencode_command -from agentpool_cli.serve_vercel import vercel_command -from agentpool_cli.store import ConfigStore -from agentpool_cli.task import task_command -from agentpool_cli.ui import ui_app -from agentpool_cli.watch import watch_command +from wolfharness_cli import log +from wolfharness_cli.agent import add_agent_file, list_agents, list_configs, set_active_file +from wolfharness_cli.cli_types import LogLevel # noqa: TC001 +from wolfharness_cli.config_info import config_cli +from wolfharness_cli.history import history_cli +from wolfharness_cli.run import run_command +from wolfharness_cli.serve_acp import acp_command +from wolfharness_cli.serve_agui import agui_command +from wolfharness_cli.serve_api import api_command +from wolfharness_cli.serve_mcp import serve_command +from wolfharness_cli.serve_opencode import opencode_command +from wolfharness_cli.serve_vercel import vercel_command +from wolfharness_cli.store import ConfigStore +from wolfharness_cli.task import task_command +from wolfharness_cli.ui import ui_app +from wolfharness_cli.watch import watch_command agent_store = ConfigStore("agents.json") @@ -39,8 +39,8 @@ def get_command_help(base_help: str) -> str: def version_callback(value: bool) -> None: """Print version and exit.""" if value: - version = metadata.version("agentpool") - t.echo(f"agentpool version {version}") + version = metadata.version("wolfharness") + t.echo(f"wolfharness version {version}") raise t.Exit diff --git a/src/agentpool_cli/agent.py b/src/wolfharness_cli/agent.py similarity index 92% rename from src/agentpool_cli/agent.py rename to src/wolfharness_cli/agent.py index 1e3c8c710..9b55832a5 100644 --- a/src/agentpool_cli/agent.py +++ b/src/wolfharness_cli/agent.py @@ -7,8 +7,8 @@ import typer as t -from agentpool_cli import agent_store, resolve_agent_config -from agentpool_cli.common import verbose_opt +from wolfharness_cli import agent_store, resolve_agent_config +from wolfharness_cli.common import verbose_opt agent_cli = t.Typer(help="Agent management commands", no_args_is_help=True) @@ -33,18 +33,18 @@ def init_agent_config( from pathlib import Path if interactive: - from agentpool.utils.inspection import validate_import + from wolfharness.utils.inspection import validate_import validate_import("promptantic", "chat") from promptantic import ModelGenerator - from agentpool import AgentsManifest + from wolfharness import AgentsManifest generator = ModelGenerator() manifest = generator.populate(AgentsManifest) manifest.save(output) else: - from agentpool import config_resources + from wolfharness import config_resources shutil.copy2(config_resources.AGENTS_TEMPLATE, output) @@ -54,8 +54,8 @@ def init_agent_config( print(f"\nCreated and activated agent configuration {config_name!r}: {output}") print("\nTry these commands:") - print(" agentpool list") - print(" agentpool chat simple_agent") + print(" wolfharness list") + print(" wolfharness chat simple_agent") @agent_cli.command("add") @@ -97,7 +97,7 @@ def list_configs() -> None: active_name = active.name if active else None if not configs: - t.echo("No configurations stored. Use 'agentpool add' to add one.") + t.echo("No configurations stored. Use 'wolfharness add' to add one.") return table = Table(title="Stored Configurations") diff --git a/src/agentpool_cli/cli_types.py b/src/wolfharness_cli/cli_types.py similarity index 100% rename from src/agentpool_cli/cli_types.py rename to src/wolfharness_cli/cli_types.py diff --git a/src/agentpool_cli/common.py b/src/wolfharness_cli/common.py similarity index 95% rename from src/agentpool_cli/common.py rename to src/wolfharness_cli/common.py index 02463e9c4..7c30fd94a 100644 --- a/src/agentpool_cli/common.py +++ b/src/wolfharness_cli/common.py @@ -26,9 +26,9 @@ OUTPUT_FORMAT_CMDS = "-o", "--output-format" VERBOSE_CMDS = "-v", "--verbose" -LOG_DIR = Path(platformdirs.user_log_dir("agentpool", "agentpool")) +LOG_DIR = Path(platformdirs.user_log_dir("wolfharness", "wolfharness")) TIMESTAMP = datetime.now().strftime("%Y%m%d_%H%M%S_%f") -LOG_FILE = LOG_DIR / f"agentpool_{TIMESTAMP}.log" +LOG_FILE = LOG_DIR / f"wolfharness_{TIMESTAMP}.log" MAX_LOG_SIZE = 10 * 1024 * 1024 # (10MB) @@ -50,7 +50,7 @@ def setup_logging( format_string: Optional custom format string log_to_file: Whether to log to file in addition to stdout """ - logger = logging.getLogger("agentpool") + logger = logging.getLogger("wolfharness") logger.setLevel(logging.DEBUG) format_string = format_string or "%(asctime)s - %(name)s - %(levelname)s - %(message)s" formatter = logging.Formatter(format_string) @@ -91,7 +91,7 @@ def setup_logging( def complete_config_names() -> list[str]: """Complete stored config names.""" - from agentpool_cli import agent_store + from wolfharness_cli import agent_store return [name for name, _ in agent_store.list_configs()] diff --git a/src/agentpool_cli/config_info.py b/src/wolfharness_cli/config_info.py similarity index 92% rename from src/agentpool_cli/config_info.py rename to src/wolfharness_cli/config_info.py index 774bcf315..e415ef92e 100644 --- a/src/agentpool_cli/config_info.py +++ b/src/wolfharness_cli/config_info.py @@ -6,7 +6,7 @@ import typer as t -from agentpool_cli import log +from wolfharness_cli import log logger = log.get_logger(__name__) @@ -26,7 +26,7 @@ def show_config( ] = "text", ) -> None: """Show configuration resolution status.""" - from agentpool_config.resolution import get_config_search_paths, resolve_config + from wolfharness_config.resolution import get_config_search_paths, resolve_config search_paths = get_config_search_paths() @@ -82,7 +82,7 @@ def show_paths() -> None: import os from pathlib import Path - from agentpool_config.resolution import ( + from wolfharness_config.resolution import ( CONFIG_FILE_NAMES, ENV_CONFIG_PATH, find_project_config, @@ -123,12 +123,12 @@ def init_config( """Initialize a new configuration file.""" from pathlib import Path - from agentpool_config.resolution import ensure_global_config_dir + from wolfharness_config.resolution import ensure_global_config_dir if location == "global": - target = ensure_global_config_dir() / "agentpool.yml" + target = ensure_global_config_dir() / "wolfharness.yml" elif location == "project": - target = Path.cwd() / "agentpool.yml" + target = Path.cwd() / "wolfharness.yml" else: target = Path(location) diff --git a/src/agentpool_cli/history.py b/src/wolfharness_cli/history.py similarity index 84% rename from src/agentpool_cli/history.py rename to src/wolfharness_cli/history.py index 274f85c6c..405be7e31 100644 --- a/src/agentpool_cli/history.py +++ b/src/wolfharness_cli/history.py @@ -7,13 +7,13 @@ import typer as t -from agentpool_cli import log, resolve_agent_config -from agentpool_cli.cli_types import GroupBy -from agentpool_cli.common import OutputFormat, format_output, output_format_opt +from wolfharness_cli import log, resolve_agent_config +from wolfharness_cli.cli_types import GroupBy +from wolfharness_cli.common import OutputFormat, format_output, output_format_opt if TYPE_CHECKING: - from agentpool_storage.base import StorageProvider + from wolfharness_storage.base import StorageProvider logger = log.get_logger(__name__) @@ -38,8 +38,8 @@ def get_history_provider(config_path: str) -> StorageProvider: Returns: Storage provider configured for history operations """ - from agentpool import AgentsManifest - from agentpool.storage import StorageManager + from wolfharness import AgentsManifest + from wolfharness.storage import StorageManager manifest = AgentsManifest.from_file(config_path) storage = StorageManager(manifest.storage) @@ -68,22 +68,22 @@ def show_history( Examples: # Show last 5 conversations - agentpool history show -n 5 + wolfharness history show -n 5 # Show conversations from last 24 hours - agentpool history show --period 24h + wolfharness history show --period 24h # Show conversations since specific date - agentpool history show --since 2024-01-01 + wolfharness history show --since 2024-01-01 # Search for specific content - agentpool history show --query "database schema" + wolfharness history show --query "database schema" # Show GPT-5 conversations with token usage - agentpool history show --model gpt-5 --tokens + wolfharness history show --model gpt-5 --tokens # Compact view of recent conversations - agentpool history show --period 1d --compact + wolfharness history show --period 1d --compact """ import anyio @@ -127,19 +127,19 @@ def show_stats( Examples: # Show stats for all agents - agentpool history stats + wolfharness history stats # Show daily stats for specific agent - agentpool history stats myagent --group-by day + wolfharness history stats myagent --group-by day # Show model usage for last week - agentpool history stats --period 1w --group-by model + wolfharness history stats --period 1w --group-by model """ import anyio - from agentpool.utils.parse_time import parse_time_period - from agentpool_storage.formatters import format_stats - from agentpool_storage.models import StatsFilters + from wolfharness.utils.parse_time import parse_time_period + from wolfharness_storage.formatters import format_stats + from wolfharness_storage.models import StatsFilters try: # Resolve config and get provider @@ -178,16 +178,16 @@ def reset_history( Examples: # Clear all history (with confirmation) - agentpool history reset + wolfharness history reset # Clear without confirmation - agentpool history reset --confirm + wolfharness history reset --confirm # Clear history for specific agent - agentpool history reset --agent myagent + wolfharness history reset --agent myagent # Drop and recreate tables (for schema changes) - agentpool history reset --hard --confirm + wolfharness history reset --hard --confirm """ import anyio diff --git a/src/agentpool_cli/log.py b/src/wolfharness_cli/log.py similarity index 93% rename from src/agentpool_cli/log.py rename to src/wolfharness_cli/log.py index 64d453cd1..cbd0a88dc 100644 --- a/src/agentpool_cli/log.py +++ b/src/wolfharness_cli/log.py @@ -1,7 +1,7 @@ """Lightweight logging configuration for CLI startup. -This is a simplified copy of agentpool.log to avoid importing the full -agentpool package (which pulls in pydantic-ai, llmling-models, etc.) +This is a simplified copy of wolfharness.log to avoid importing the full +wolfharness package (which pulls in pydantic-ai, llmling-models, etc.) just for CLI help text. """ diff --git a/src/agentpool_cli/py.typed b/src/wolfharness_cli/py.typed similarity index 100% rename from src/agentpool_cli/py.typed rename to src/wolfharness_cli/py.typed diff --git a/src/agentpool_cli/run.py b/src/wolfharness_cli/run.py similarity index 92% rename from src/agentpool_cli/run.py rename to src/wolfharness_cli/run.py index 7251ad128..7db94709e 100644 --- a/src/agentpool_cli/run.py +++ b/src/wolfharness_cli/run.py @@ -11,10 +11,10 @@ from pydantic_ai import TextPartDelta import typer as t -from agentpool.agents.events import PartDeltaEvent, StreamCompleteEvent -from agentpool_cli import resolve_agent_config -from agentpool_cli.cli_types import DetailLevel # noqa: TC001 -from agentpool_cli.common import verbose_opt +from wolfharness.agents.events import PartDeltaEvent, StreamCompleteEvent +from wolfharness_cli import resolve_agent_config +from wolfharness_cli.cli_types import DetailLevel # noqa: TC001 +from wolfharness_cli.common import verbose_opt def run_command( @@ -43,7 +43,7 @@ def run_command( raise t.BadParameter(error_msg) from e async def run() -> None: - from agentpool import AgentPool + from wolfharness import AgentPool async with AgentPool(config_path) as pool: sp = pool.session_pool diff --git a/src/agentpool_cli/serve_acp.py b/src/wolfharness_cli/serve_acp.py similarity index 91% rename from src/agentpool_cli/serve_acp.py rename to src/wolfharness_cli/serve_acp.py index e6ecf14be..111e2b8de 100644 --- a/src/agentpool_cli/serve_acp.py +++ b/src/wolfharness_cli/serve_acp.py @@ -5,9 +5,9 @@ integration with file system access, permission handling, and terminal support. Configuration is resolved from multiple layers (in precedence order): -1. Global config (~/.config/agentpool/agentpool.yml) +1. Global config (~/.config/wolfharness/wolfharness.yml) 2. Custom config (AGENTPOOL_CONFIG env var) -3. Project config (agentpool.yml in project/git root) +3. Project config (wolfharness.yml in project/git root) 4. Explicit CLI argument (highest precedence) 5. Built-in fallback (only if no agents defined elsewhere) """ @@ -23,7 +23,7 @@ from platformdirs import user_log_path import typer as t -from agentpool_cli import log +from wolfharness_cli import log if TYPE_CHECKING: @@ -174,9 +174,9 @@ def acp_command( # noqa: PLR0915 Configuration Layers (merged in order, later overrides earlier): - 1. Global config - ~/.config/agentpool/agentpool.yml for user preferences + 1. Global config - ~/.config/wolfharness/wolfharness.yml for user preferences 2. Custom config - AGENTPOOL_CONFIG env var for CI/deployment overrides - 3. Project config - agentpool.yml in project/git root for project-specific settings + 3. Project config - wolfharness.yml in project/git root for project-specific settings 4. Explicit config - CLI argument (highest precedence) 5. Built-in fallback - Only used if no agents defined in any layer @@ -187,21 +187,21 @@ def acp_command( # noqa: PLR0915 Transport Options: # Stdio transport (default, for IDE integration) - agentpool serve-acp agents.yml + wolfharness serve-acp agents.yml # Streamable HTTP transport (recommended for Toad, web clients) - agentpool serve-acp agents.yml --transport streamable-http --port 8080 + wolfharness serve-acp agents.yml --transport streamable-http --port 8080 # Legacy WebSocket transport (deprecated) - agentpool serve-acp agents.yml --transport websocket --ws-port 8765 + wolfharness serve-acp agents.yml --transport websocket --ws-port 8765 """ from acp import ACPWebSocketTransport, StdioTransport, WebSocketTransport - from agentpool import log - from agentpool.config_resources import ACP_ASSISTANT - from agentpool.models.manifest import AgentsManifest - from agentpool_config.context import ConfigContextManager - from agentpool_config.resolution import resolve_config - from agentpool_server.acp_server import ACPServer + from wolfharness import log + from wolfharness.config_resources import ACP_ASSISTANT + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.context import ConfigContextManager + from wolfharness_config.resolution import resolve_config + from wolfharness_server.acp_server import ACPServer # Build transport config if transport == "streamable-http": @@ -229,7 +229,7 @@ def acp_command( # noqa: PLR0915 transport_config = StdioTransport() # Always log to file with rollover - log_dir = user_log_path("agentpool", appauthor=False) + log_dir = user_log_path("wolfharness", appauthor=False) log_dir.mkdir(parents=True, exist_ok=True) log_file = log_dir / "acp.log" log.configure_logging(force=True, log_file=str(log_file)) @@ -293,7 +293,7 @@ def update_with_path(nodes: dict[str, Any]) -> dict[str, Any]: # TODO: Consider adding to specific agent's MCP manager instead of pool-level # for better isolation (currently all agents in pool share these servers) if mcp_config: - from agentpool_config.mcp_server import parse_mcp_servers_json + from wolfharness_config.mcp_server import parse_mcp_servers_json try: mcp_data = anyenv.load_json(mcp_config) diff --git a/src/agentpool_cli/serve_agui.py b/src/wolfharness_cli/serve_agui.py similarity index 89% rename from src/agentpool_cli/serve_agui.py rename to src/wolfharness_cli/serve_agui.py index 7b07a2e4d..5818c4af1 100644 --- a/src/agentpool_cli/serve_agui.py +++ b/src/wolfharness_cli/serve_agui.py @@ -7,8 +7,8 @@ import typer as t -from agentpool_cli import resolve_agent_config -from agentpool_cli.log import get_logger +from wolfharness_cli import resolve_agent_config +from wolfharness_cli.log import get_logger logger = get_logger(__name__) @@ -32,9 +32,9 @@ def agui_command( """ import anyio - from agentpool import AgentPool, AgentsManifest - from agentpool_config.context import ConfigContextManager - from agentpool_server.agui_server import AGUIServer + from wolfharness import AgentPool, AgentsManifest + from wolfharness_config.context import ConfigContextManager + from wolfharness_server.agui_server import AGUIServer logger.info("Server PID", pid=os.getpid()) diff --git a/src/agentpool_cli/serve_api.py b/src/wolfharness_cli/serve_api.py similarity index 91% rename from src/agentpool_cli/serve_api.py rename to src/wolfharness_cli/serve_api.py index e69fd457c..d0380df36 100644 --- a/src/agentpool_cli/serve_api.py +++ b/src/wolfharness_cli/serve_api.py @@ -8,8 +8,8 @@ import typer as t -from agentpool_cli import resolve_agent_config -from agentpool_cli.log import get_logger +from wolfharness_cli import resolve_agent_config +from wolfharness_cli.log import get_logger logger = get_logger(__name__) @@ -33,9 +33,9 @@ def api_command( """ import uvicorn - from agentpool import AgentPool, AgentsManifest - from agentpool_config.context import ConfigContextManager - from agentpool_server.openai_api_server.server import OpenAIAPIServer + from wolfharness import AgentPool, AgentsManifest + from wolfharness_config.context import ConfigContextManager + from wolfharness_server.openai_api_server.server import OpenAIAPIServer logger.info("Server PID", pid=os.getpid()) diff --git a/src/agentpool_cli/serve_mcp.py b/src/wolfharness_cli/serve_mcp.py similarity index 85% rename from src/agentpool_cli/serve_mcp.py rename to src/wolfharness_cli/serve_mcp.py index 14b9c4cac..903c35098 100644 --- a/src/agentpool_cli/serve_mcp.py +++ b/src/wolfharness_cli/serve_mcp.py @@ -9,14 +9,14 @@ import typer as t -from agentpool_cli import log +from wolfharness_cli import log -# Duplicated from agentpool_config.pool_server to avoid heavy imports at CLI startup +# Duplicated from wolfharness_config.pool_server to avoid heavy imports at CLI startup TransportType = Literal["stdio", "sse", "streamable-http"] if TYPE_CHECKING: - from agentpool import ChatMessage + from wolfharness import ChatMessage logger = log.get_logger(__name__) @@ -45,10 +45,10 @@ def on_message(message: ChatMessage[Any]) -> None: async def run_server() -> None: - from agentpool import AgentPool, AgentsManifest - from agentpool_config.context import ConfigContextManager - from agentpool_config.pool_server import MCPPoolServerConfig - from agentpool_server.mcp_server.server import MCPServer + from wolfharness import AgentPool, AgentsManifest + from wolfharness_config.context import ConfigContextManager + from wolfharness_config.pool_server import MCPPoolServerConfig + from wolfharness_server.mcp_server.server import MCPServer logger.info("Server PID", pid=os.getpid()) # Load manifest and create pool (without server config) @@ -67,14 +67,14 @@ async def run_server() -> None: async with pool, server: _consumer_task: asyncio.Task[None] | None = None if show_messages: - from agentpool.agents.events import StreamCompleteEvent + from wolfharness.agents.events import StreamCompleteEvent _event_bus = pool.session_pool.event_bus if pool.session_pool is not None else None if _event_bus is not None: async def _consume_stream_complete() -> None: """Subscribe to EventBus and print completed messages.""" - from agentpool.orchestrator.core import drain_and_merge + from wolfharness.orchestrator.core import drain_and_merge stream: Any = None try: diff --git a/src/agentpool_cli/serve_opencode.py b/src/wolfharness_cli/serve_opencode.py similarity index 86% rename from src/agentpool_cli/serve_opencode.py rename to src/wolfharness_cli/serve_opencode.py index 610e4b41c..7281a0209 100644 --- a/src/agentpool_cli/serve_opencode.py +++ b/src/wolfharness_cli/serve_opencode.py @@ -4,9 +4,9 @@ allowing OpenCode TUI and SDK clients to interact with AgentPool agents. Configuration is resolved from multiple layers (in precedence order): -1. Global config (~/.config/agentpool/agentpool.yml) +1. Global config (~/.config/wolfharness/wolfharness.yml) 2. Custom config (AGENTPOOL_CONFIG env var) -3. Project config (agentpool.yml in project/git root) +3. Project config (wolfharness.yml in project/git root) 4. Explicit CLI argument (highest precedence) 5. Built-in fallback (only if no agents defined elsewhere) """ @@ -19,8 +19,8 @@ from platformdirs import user_log_path import typer as t -from agentpool_cli import log -from agentpool_cli.cli_types import LogLevel # noqa: TC001 +from wolfharness_cli import log +from wolfharness_cli.cli_types import LogLevel # noqa: TC001 logger = log.get_logger(__name__) @@ -48,12 +48,12 @@ def _configure_observability_and_logging( manifest: Any, host: str, port: int, resolved: Any, log_level: str = "info" ) -> None: """Initialize observability and configure file logging.""" - from agentpool import log as ap_log - from agentpool.observability import registry + from wolfharness import log as ap_log + from wolfharness.observability import registry registry.configure_observability(manifest.observability) - log_dir = user_log_path("agentpool", appauthor=False) + log_dir = user_log_path("wolfharness", appauthor=False) log_dir.mkdir(parents=True, exist_ok=True) log_file = log_dir / "opencode.log" ap_log.configure_logging(level=log_level.upper(), force=True, log_file=str(log_file)) @@ -104,9 +104,9 @@ def opencode_command( Configuration Layers (merged in order, later overrides earlier): - 1. Global config - ~/.config/agentpool/agentpool.yml for user preferences + 1. Global config - ~/.config/wolfharness/wolfharness.yml for user preferences 2. Custom config - AGENTPOOL_CONFIG env var for CI/deployment overrides - 3. Project config - agentpool.yml in project/git root for project-specific settings + 3. Project config - wolfharness.yml in project/git root for project-specific settings 4. Explicit config - CLI argument (highest precedence) 5. Built-in fallback - Only used if no agents defined in any layer @@ -115,12 +115,12 @@ def opencode_command( the pool's default agent is used (set via 'default_agent' in config, or falls back to the first agent). """ - from agentpool import AgentPool - from agentpool.config_resources import ACP_ASSISTANT - from agentpool.models.manifest import AgentsManifest - from agentpool_config.context import ConfigContextManager - from agentpool_config.resolution import resolve_config - from agentpool_server.opencode_server.server import OpenCodeServer + from wolfharness import AgentPool + from wolfharness.config_resources import ACP_ASSISTANT + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.context import ConfigContextManager + from wolfharness_config.resolution import resolve_config + from wolfharness_server.opencode_server.server import OpenCodeServer # Resolve configuration from all layers FIRST (before logging) # fallback_config is only used if no agents defined elsewhere diff --git a/src/agentpool_cli/serve_vercel.py b/src/wolfharness_cli/serve_vercel.py similarity index 95% rename from src/agentpool_cli/serve_vercel.py rename to src/wolfharness_cli/serve_vercel.py index faa9a608e..1d70dbf74 100644 --- a/src/agentpool_cli/serve_vercel.py +++ b/src/wolfharness_cli/serve_vercel.py @@ -9,13 +9,13 @@ import anyenv import typer as t -from agentpool_cli import log, resolve_agent_config +from wolfharness_cli import log, resolve_agent_config if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import ChatMessage + from wolfharness import ChatMessage logger = log.get_logger(__name__) @@ -53,9 +53,9 @@ def vercel_command( # noqa: PLR0915 from starlette.responses import JSONResponse, Response, StreamingResponse import uvicorn - from agentpool import AgentPool, AgentsManifest - from agentpool.agents.events import StreamCompleteEvent, UserMessageInsertedEvent - from agentpool_config.context import ConfigContextManager + from wolfharness import AgentPool, AgentsManifest + from wolfharness.agents.events import StreamCompleteEvent, UserMessageInsertedEvent + from wolfharness_config.context import ConfigContextManager logger.info("Server PID", pid=os.getpid()) @@ -135,7 +135,7 @@ async def chat(request: Request) -> Response: # noqa: PLR0915 # Determine which agent to use and create a per-request session # Vercel protocol is stateless — new session per HTTP request effective_agent_name = agent_name or pool.main_agent_name - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id session_id = generate_session_id() session_pool = pool.session_pool diff --git a/src/agentpool_cli/store.py b/src/wolfharness_cli/store.py similarity index 97% rename from src/agentpool_cli/store.py rename to src/wolfharness_cli/store.py index bcc2fbec9..ab64cc3c4 100644 --- a/src/agentpool_cli/store.py +++ b/src/wolfharness_cli/store.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, TypedDict -from agentpool_cli.log import get_logger +from wolfharness_cli.log import get_logger if TYPE_CHECKING: @@ -48,7 +48,7 @@ def config_dir(self) -> UPath: import platformdirs from upathtools import to_upath - return to_upath(platformdirs.user_config_dir("agentpool")) + return to_upath(platformdirs.user_config_dir("wolfharness")) @property def config_file(self) -> UPath: diff --git a/src/agentpool_cli/task.py b/src/wolfharness_cli/task.py similarity index 87% rename from src/agentpool_cli/task.py rename to src/wolfharness_cli/task.py index c6fb3b446..04237d774 100644 --- a/src/agentpool_cli/task.py +++ b/src/wolfharness_cli/task.py @@ -9,20 +9,20 @@ import typer as t -from agentpool.agents.events import StreamCompleteEvent -from agentpool_cli import log, resolve_agent_config +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness_cli import log, resolve_agent_config if TYPE_CHECKING: - from agentpool import AgentsManifest + from wolfharness import AgentsManifest TASK_HELP = """ Execute a defined task with the specified agent. Example: - agentpool task docs write_api_docs --prompt "Include code examples" - agentpool task docs write_api_docs --log-level DEBUG + wolfharness task docs write_api_docs --prompt "Include code examples" + wolfharness task docs write_api_docs --log-level DEBUG """ @@ -34,7 +34,7 @@ async def execute_job( prompt: str | None = None, ) -> str: """Execute task with agent.""" - from agentpool import AgentPool + from wolfharness import AgentPool async with AgentPool(config) as pool: sp = pool.session_pool @@ -84,8 +84,8 @@ def task_command( prompt: Annotated[str | None, t.Option("--prompt", "-p", help="Additional prompt")] = None, ) -> None: """Execute a task with the specified agent.""" - from agentpool import AgentsManifest - from agentpool_config.context import ConfigContextManager + from wolfharness import AgentsManifest + from wolfharness_config.context import ConfigContextManager logger = log.get_logger(__name__) try: diff --git a/src/agentpool_cli/ui.py b/src/wolfharness_cli/ui.py similarity index 93% rename from src/agentpool_cli/ui.py rename to src/wolfharness_cli/ui.py index 32fa7468b..2e056fc62 100644 --- a/src/agentpool_cli/ui.py +++ b/src/wolfharness_cli/ui.py @@ -14,7 +14,7 @@ import typer as t -from agentpool_cli import log +from wolfharness_cli import log logger = log.get_logger(__name__) @@ -60,17 +60,17 @@ def opencode_ui_command( Examples: # Start server + TUI - agentpool ui opencode + wolfharness ui opencode # Use specific config and agent - agentpool ui opencode agents.yml --agent myagent + wolfharness ui opencode agents.yml --agent myagent # Custom port - agentpool ui opencode --port 8080 + wolfharness ui opencode --port 8080 # Attach to existing server (no server startup) - agentpool ui opencode --attach - agentpool ui opencode --attach --port 8080 + wolfharness ui opencode --attach + wolfharness ui opencode --attach --port 8080 """ url = f"http://{host}:{port}" # Attach-only mode: just launch TUI @@ -82,7 +82,7 @@ def opencode_ui_command( logger.warning("OpenCode TUI exited with non-zero status", code=result.returncode) return # Build server command - server_cmd = ["agentpool", "serve-opencode", "--host", host, "--port", str(port)] + server_cmd = ["wolfharness", "serve-opencode", "--host", host, "--port", str(port)] if config: server_cmd.append(config) if agent: @@ -148,21 +148,21 @@ def toad_ui_command( ) -> None: """Launch Toad TUI for ACP agents. - By default uses stdio transport where Toad spawns the agentpool server. + By default uses stdio transport where Toad spawns the wolfharness server. With --websocket, starts a WebSocket ACP server in the background first. Examples: # Direct stdio (Toad spawns server) - agentpool ui toad + wolfharness ui toad # Use specific config - agentpool ui toad agents.yml + wolfharness ui toad agents.yml # WebSocket transport - agentpool ui toad --websocket + wolfharness ui toad --websocket # WebSocket with custom port - agentpool ui toad --websocket --port 9000 + wolfharness ui toad --websocket --port 9000 """ if websocket: _run_toad_websocket(config, port) @@ -172,14 +172,14 @@ def toad_ui_command( def _run_toad_stdio(config: str | None) -> None: """Run Toad with stdio transport (Toad spawns server).""" - # Build agentpool command that Toad will spawn - agentpool_cmd = "agentpool serve-acp" + # Build wolfharness command that Toad will spawn + wolfharness_cmd = "wolfharness serve-acp" if config: - agentpool_cmd += f" {config}" + wolfharness_cmd += f" {config}" os.system("clear" if os.name != "nt" else "cls") - # Run toad with agentpool as subprocess + # Run toad with wolfharness as subprocess result = subprocess.run( - ["uvx", "--from", "batrachian-toad@latest", "toad", "acp", agentpool_cmd], + ["uvx", "--from", "batrachian-toad@latest", "toad", "acp", wolfharness_cmd], check=False, ) if result.returncode not in {0, 130}: # 130 = Ctrl+C @@ -192,7 +192,7 @@ def _run_toad_websocket(config: str | None, port: int) -> None: # Build server command server_cmd = [ - "agentpool", + "wolfharness", "serve-acp", "--transport", "streamable-http", @@ -274,20 +274,20 @@ def opencode_desktop_command( # noqa: PLR0915 Examples: # Start server, configure desktop app, and launch it - agentpool ui desktop + wolfharness ui desktop # Use specific config and agent - agentpool ui desktop agents.yml --agent myagent + wolfharness ui desktop agents.yml --agent myagent # Custom port - agentpool ui desktop --port 8080 + wolfharness ui desktop --port 8080 # Configure desktop to attach to existing server - agentpool ui desktop --attach - agentpool ui desktop --attach --port 8080 + wolfharness ui desktop --attach + wolfharness ui desktop --attach --port 8080 # After using --attach, reset to default (spawn local server) - agentpool ui desktop --attach --port 0 # port 0 clears the setting + wolfharness ui desktop --attach --port 0 # port 0 clears the setting """ url = f"http://{host}:{port}" # Determine config path based on platform @@ -388,7 +388,7 @@ def opencode_desktop_command( # noqa: PLR0915 # Default mode: Start server + launch desktop app # Build server command - server_cmd = ["agentpool", "serve-opencode", "--host", host, "--port", str(port)] + server_cmd = ["wolfharness", "serve-opencode", "--host", host, "--port", str(port)] if config: server_cmd.append(config) if agent: diff --git a/src/agentpool_cli/utils.py b/src/wolfharness_cli/utils.py similarity index 100% rename from src/agentpool_cli/utils.py rename to src/wolfharness_cli/utils.py diff --git a/src/agentpool_cli/watch.py b/src/wolfharness_cli/watch.py similarity index 89% rename from src/agentpool_cli/watch.py rename to src/wolfharness_cli/watch.py index e1f65a398..d5e8f1e6b 100644 --- a/src/agentpool_cli/watch.py +++ b/src/wolfharness_cli/watch.py @@ -7,7 +7,7 @@ import typer as t -from agentpool_cli import log +from wolfharness_cli import log logger = log.get_logger(__name__) @@ -31,8 +31,8 @@ def watch_command( """Run agents in event-watching mode.""" async def run_watch() -> None: - from agentpool import AgentPool, AgentsManifest - from agentpool_config.context import ConfigContextManager + from wolfharness import AgentPool, AgentsManifest + from wolfharness_config.context import ConfigContextManager with ConfigContextManager(config): manifest = AgentsManifest.from_file(config) diff --git a/src/agentpool_commands/__init__.py b/src/wolfharness_commands/__init__.py similarity index 89% rename from src/agentpool_commands/__init__.py rename to src/wolfharness_commands/__init__.py index de56505b3..3dcdf86a6 100644 --- a/src/agentpool_commands/__init__.py +++ b/src/wolfharness_commands/__init__.py @@ -2,35 +2,35 @@ from __future__ import annotations -from agentpool_commands.agents import ( +from wolfharness_commands.agents import ( CreateAgentCommand, CreateTeamCommand, ListAgentsCommand, ShowAgentCommand, # SwitchAgentCommand, ) -from agentpool_commands.connections import ( +from wolfharness_commands.connections import ( ConnectCommand, DisconnectCommand, ListConnectionsCommand, DisconnectAllCommand, ) -from agentpool_commands.history import SearchHistoryCommand -from agentpool_commands.mcp import ( +from wolfharness_commands.history import SearchHistoryCommand +from wolfharness_commands.mcp import ( AddMCPServerCommand, AddRemoteMCPServerCommand, ListMCPServersCommand, ) -from agentpool_commands.models import SetModelCommand -from agentpool_commands.prompts import ListPromptsCommand, ShowPromptCommand -from agentpool_commands.resources import ( +from wolfharness_commands.models import SetModelCommand +from wolfharness_commands.prompts import ListPromptsCommand, ShowPromptCommand +from wolfharness_commands.resources import ( ListResourcesCommand, ShowResourceCommand, AddResourceCommand, ) -from agentpool_commands.session import ClearCommand, ResetCommand -from agentpool_commands.read import ReadCommand -from agentpool_commands.tools import ( +from wolfharness_commands.session import ClearCommand, ResetCommand +from wolfharness_commands.read import ReadCommand +from wolfharness_commands.tools import ( DisableToolCommand, EnableToolCommand, ListToolsCommand, @@ -38,18 +38,18 @@ RegisterToolCommand, ShowToolCommand, ) -from agentpool_commands.workers import AddWorkerCommand, RemoveWorkerCommand, ListWorkersCommand -from agentpool_commands.utils import ( +from wolfharness_commands.workers import AddWorkerCommand, RemoveWorkerCommand, ListWorkersCommand +from wolfharness_commands.utils import ( CopyClipboardCommand, EditAgentFileCommand, GetLogsCommand, ShareHistoryCommand, ) -from agentpool_commands.pool import ListPoolsCommand, SpawnCommand +from wolfharness_commands.pool import ListPoolsCommand, SpawnCommand # CompactCommand is only for Native Agent (has its own history) # Other agents (ClaudeCode, ACP, AGUI) don't control their own history -from agentpool_commands.pool import CompactCommand # noqa: F401 +from wolfharness_commands.pool import CompactCommand # noqa: F401 from typing import TYPE_CHECKING, Any if TYPE_CHECKING: diff --git a/src/agentpool_commands/agents.py b/src/wolfharness_commands/agents.py similarity index 94% rename from src/agentpool_commands/agents.py rename to src/wolfharness_commands/agents.py index ebc0b95a1..518f8e95a 100644 --- a/src/agentpool_commands/agents.py +++ b/src/wolfharness_commands/agents.py @@ -7,15 +7,15 @@ from slashed import CommandContext, CommandError from slashed.completers import CallbackCompleter -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_commands.base import NodeCommand -from agentpool_commands.completers import get_available_agents, get_available_nodes -from agentpool_commands.markdown_utils import format_table +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_commands.base import NodeCommand +from wolfharness_commands.completers import get_available_agents, get_available_nodes +from wolfharness_commands.markdown_utils import format_table if TYPE_CHECKING: - from agentpool.delegation import BaseTeam - from agentpool.messaging.messagenode import MessageNode + from wolfharness.delegation import BaseTeam + from wolfharness.messaging.messagenode import MessageNode class CreateAgentCommand(NodeCommand): @@ -242,7 +242,7 @@ async def execute_command( pool.agent_configs[name].get_agent(pool=pool) for name in node_names ] # Create the team - from agentpool.delegation.base_team import BaseTeam as TeamClass + from wolfharness.delegation.base_team import BaseTeam as TeamClass team: BaseTeam[Any, Any] = TeamClass( node_instances, diff --git a/src/wolfharness_commands/base.py b/src/wolfharness_commands/base.py new file mode 100644 index 000000000..79a25d72e --- /dev/null +++ b/src/wolfharness_commands/base.py @@ -0,0 +1,12 @@ +"""Base command class with node-type filtering support. + +Re-exports from ``wolfharness.commands.base`` for backward compatibility. +New code should import from ``wolfharness.commands.base`` directly. +""" + +from __future__ import annotations + +from wolfharness.commands.base import AgentCommand, NodeCommand + + +__all__ = ["AgentCommand", "NodeCommand"] diff --git a/src/agentpool_commands/commands.py b/src/wolfharness_commands/commands.py similarity index 83% rename from src/agentpool_commands/commands.py rename to src/wolfharness_commands/commands.py index 6cd02ee76..02b63b908 100644 --- a/src/agentpool_commands/commands.py +++ b/src/wolfharness_commands/commands.py @@ -4,10 +4,10 @@ from slashed import CommandContext # noqa: TC002 -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_commands.base import NodeCommand -from agentpool_commands.markdown_utils import format_table +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_commands.base import NodeCommand +from wolfharness_commands.markdown_utils import format_table logger = get_logger(__name__) diff --git a/src/agentpool_commands/completers.py b/src/wolfharness_commands/completers.py similarity index 94% rename from src/agentpool_commands/completers.py rename to src/wolfharness_commands/completers.py index 4a47b5e5b..338254b86 100644 --- a/src/agentpool_commands/completers.py +++ b/src/wolfharness_commands/completers.py @@ -6,8 +6,8 @@ from slashed import CompletionItem, CompletionProvider -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.messaging.context import NodeContext # noqa: TC001 +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.messaging.context import NodeContext # noqa: TC001 if TYPE_CHECKING: @@ -15,7 +15,7 @@ from slashed import CompletionContext - from agentpool.agents.base_agent import AgentTypeLiteral + from wolfharness.agents.base_agent import AgentTypeLiteral def get_available_agents( diff --git a/src/agentpool_commands/connections.py b/src/wolfharness_commands/connections.py similarity index 95% rename from src/agentpool_commands/connections.py rename to src/wolfharness_commands/connections.py index d0ca5b005..af9cb5020 100644 --- a/src/agentpool_commands/connections.py +++ b/src/wolfharness_commands/connections.py @@ -8,11 +8,11 @@ from slashed import CommandContext, CommandError from slashed.completers import CallbackCompleter -from agentpool.log import get_logger -from agentpool.messaging import MessageNode -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_commands.base import NodeCommand -from agentpool_commands.completers import get_available_nodes +from wolfharness.log import get_logger +from wolfharness.messaging import MessageNode +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_commands.base import NodeCommand +from wolfharness_commands.completers import get_available_nodes logger = get_logger(__name__) diff --git a/src/agentpool_commands/history.py b/src/wolfharness_commands/history.py similarity index 89% rename from src/agentpool_commands/history.py rename to src/wolfharness_commands/history.py index 1e76f9406..0d8c54606 100644 --- a/src/agentpool_commands/history.py +++ b/src/wolfharness_commands/history.py @@ -4,8 +4,8 @@ from slashed import CommandContext, CommandError -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_commands.base import NodeCommand +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_commands.base import NodeCommand class SearchHistoryCommand(NodeCommand): @@ -46,7 +46,7 @@ async def execute_command( raise CommandError("No storage manager available for history search") try: - from agentpool_storage.formatters import format_output + from wolfharness_storage.formatters import format_output provider = ctx.context.agent.storage.get_history_provider() results = await provider.get_filtered_conversations( diff --git a/src/agentpool_commands/markdown_utils.py b/src/wolfharness_commands/markdown_utils.py similarity index 100% rename from src/agentpool_commands/markdown_utils.py rename to src/wolfharness_commands/markdown_utils.py diff --git a/src/agentpool_commands/mcp.py b/src/wolfharness_commands/mcp.py similarity index 89% rename from src/agentpool_commands/mcp.py rename to src/wolfharness_commands/mcp.py index 223e5afe5..7f3f17931 100644 --- a/src/agentpool_commands/mcp.py +++ b/src/wolfharness_commands/mcp.py @@ -7,13 +7,13 @@ from pydantic import HttpUrl from slashed import CommandContext, CommandError -from agentpool_commands.base import NodeCommand +from wolfharness_commands.base import NodeCommand if TYPE_CHECKING: - from agentpool.agents.context import AgentContext - from agentpool.messaging import MessageNode - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness.agents.context import AgentContext + from wolfharness.messaging import MessageNode + from wolfharness_config.mcp_server import MCPServerConfig class AddMCPServerCommand(NodeCommand): @@ -35,7 +35,7 @@ class AddMCPServerCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -57,7 +57,7 @@ async def execute_command( args: Pipe-separated command arguments env: Pipe-separated environment variables (KEY=value format) """ - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness_config.mcp_server import StdioMCPServerConfig try: # Parse arguments @@ -96,7 +96,7 @@ class AddRemoteMCPServerCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -116,7 +116,7 @@ async def execute_command( url: Server URL endpoint transport: HTTP transport type (sse or streamable-http) """ - from agentpool_config.mcp_server import SSEMCPServerConfig, StreamableHTTPMCPServerConfig + from wolfharness_config.mcp_server import SSEMCPServerConfig, StreamableHTTPMCPServerConfig try: config: MCPServerConfig @@ -143,7 +143,7 @@ class ListMCPServersCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -153,7 +153,7 @@ async def execute_command(self, ctx: CommandContext[AgentContext]) -> None: Args: ctx: Command context """ - from agentpool_commands.markdown_utils import format_table + from wolfharness_commands.markdown_utils import format_table servers = ctx.context.agent.mcp.providers if not servers: diff --git a/src/agentpool_commands/models.py b/src/wolfharness_commands/models.py similarity index 90% rename from src/agentpool_commands/models.py rename to src/wolfharness_commands/models.py index eee6b6a02..2ab3bf5a7 100644 --- a/src/agentpool_commands/models.py +++ b/src/wolfharness_commands/models.py @@ -7,13 +7,13 @@ from slashed import CommandContext # noqa: TC002 from slashed.completers import CallbackCompleter -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool_commands.base import NodeCommand -from agentpool_commands.completers import get_model_names +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness_commands.base import NodeCommand +from wolfharness_commands.completers import get_model_names if TYPE_CHECKING: - from agentpool.messaging import MessageNode + from wolfharness.messaging import MessageNode class ListModelsCommand(NodeCommand): @@ -64,7 +64,7 @@ async def execute_command( @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import ACPAgent, Agent + from wolfharness import ACPAgent, Agent return isinstance(node, Agent | ACPAgent) @@ -110,6 +110,6 @@ def get_completer(self) -> CallbackCompleter: @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import ACPAgent, Agent + from wolfharness import ACPAgent, Agent return isinstance(node, Agent | ACPAgent) diff --git a/src/agentpool_commands/pool.py b/src/wolfharness_commands/pool.py similarity index 94% rename from src/agentpool_commands/pool.py rename to src/wolfharness_commands/pool.py index 4e023e6f4..12f797195 100644 --- a/src/agentpool_commands/pool.py +++ b/src/wolfharness_commands/pool.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Any -from agentpool.agents.base_agent import BaseAgent -from agentpool_commands.base import NodeCommand +from wolfharness.agents.base_agent import BaseAgent +from wolfharness_commands.base import NodeCommand if TYPE_CHECKING: from slashed import CommandContext - from agentpool.messaging.context import NodeContext + from wolfharness.messaging.context import NodeContext class ListPoolsCommand(NodeCommand): @@ -30,7 +30,7 @@ async def execute_command(self, ctx: CommandContext[NodeContext[Any]]) -> None: Args: ctx: Command context with node context """ - from agentpool_cli import agent_store + from wolfharness_cli import agent_store pool = ctx.context.pool if pool is None: @@ -57,7 +57,7 @@ async def execute_command(self, ctx: CommandContext[NodeContext[Any]]) -> None: if not stored_configs: output_lines.append("*No stored configurations*") output_lines.append("") - output_lines.append("Use `agentpool add ` to add configurations.") + output_lines.append("Use `wolfharness add ` to add configurations.") else: # Build markdown table output_lines.append("| Name | Path |") @@ -111,7 +111,7 @@ async def execute_command( ctx: Command context with node context preset: Optional preset name (minimal, balanced, summarizing) """ - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent # Get agent from context agent = ctx.context.node @@ -128,7 +128,7 @@ async def execute_command( try: # Get compaction pipeline - from agentpool.messaging.compaction import ( + from wolfharness.messaging.compaction import ( balanced_context, minimal_context, summarizing_context, @@ -163,7 +163,7 @@ async def execute_command( await ctx.output.print("🔄 **Compacting conversation history...**") # Apply the pipeline using shared helper - from agentpool.messaging.compaction import compact_conversation + from wolfharness.messaging.compaction import compact_conversation original_count, compacted_count = await compact_conversation( pipeline, agent.conversation diff --git a/src/agentpool_commands/prompts.py b/src/wolfharness_commands/prompts.py similarity index 93% rename from src/agentpool_commands/prompts.py rename to src/wolfharness_commands/prompts.py index 8c3e3d692..346d93efb 100644 --- a/src/agentpool_commands/prompts.py +++ b/src/wolfharness_commands/prompts.py @@ -4,9 +4,9 @@ from slashed import CommandContext, CommandError -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_commands.base import NodeCommand -from agentpool_commands.completers import PromptCompleter +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_commands.base import NodeCommand +from wolfharness_commands.completers import PromptCompleter class ShowPromptCommand(NodeCommand): diff --git a/src/agentpool_commands/py.typed b/src/wolfharness_commands/py.typed similarity index 100% rename from src/agentpool_commands/py.typed rename to src/wolfharness_commands/py.typed diff --git a/src/agentpool_commands/read.py b/src/wolfharness_commands/read.py similarity index 89% rename from src/agentpool_commands/read.py rename to src/wolfharness_commands/read.py index 6cba70449..9cadcbaa4 100644 --- a/src/agentpool_commands/read.py +++ b/src/wolfharness_commands/read.py @@ -7,13 +7,13 @@ from slashed import CommandContext, CommandError from slashed.completers import PathCompleter -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool_commands.base import NodeCommand +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness_commands.base import NodeCommand if TYPE_CHECKING: - from agentpool.messaging import MessageNode + from wolfharness.messaging import MessageNode logger = get_logger(__name__) @@ -68,7 +68,7 @@ async def execute_command( @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: """Only available for Agent nodes.""" - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) diff --git a/src/agentpool_commands/resources.py b/src/wolfharness_commands/resources.py similarity index 96% rename from src/agentpool_commands/resources.py rename to src/wolfharness_commands/resources.py index 19c216d1d..2e715b468 100644 --- a/src/agentpool_commands/resources.py +++ b/src/wolfharness_commands/resources.py @@ -4,11 +4,11 @@ from slashed import CommandContext, CommandError -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_commands.base import AgentCommand, NodeCommand -from agentpool_commands.markdown_utils import format_table +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_commands.base import AgentCommand, NodeCommand +from wolfharness_commands.markdown_utils import format_table logger = get_logger(__name__) diff --git a/src/agentpool_commands/session.py b/src/wolfharness_commands/session.py similarity index 90% rename from src/agentpool_commands/session.py rename to src/wolfharness_commands/session.py index 8186d0380..63b99be20 100644 --- a/src/agentpool_commands/session.py +++ b/src/wolfharness_commands/session.py @@ -4,8 +4,8 @@ from slashed import CommandContext # noqa: TC002 -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool_commands.base import AgentCommand +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness_commands.base import AgentCommand class ClearCommand(AgentCommand): diff --git a/src/agentpool_commands/text_sharing/__init__.py b/src/wolfharness_commands/text_sharing/__init__.py similarity index 85% rename from src/agentpool_commands/text_sharing/__init__.py rename to src/wolfharness_commands/text_sharing/__init__.py index 36d45413e..c309a067d 100644 --- a/src/agentpool_commands/text_sharing/__init__.py +++ b/src/wolfharness_commands/text_sharing/__init__.py @@ -4,12 +4,12 @@ from typing import Literal, assert_never, overload -from agentpool_commands.text_sharing.base import ShareResult, TextSharer, Visibility -from agentpool_commands.text_sharing.github_gist import GistSharer -from agentpool_commands.text_sharing.opencode import OpenCodeSharer -from agentpool_commands.text_sharing.paste_rs import PasteRsSharer -from agentpool_commands.text_sharing.pastebin import PastebinSharer -from agentpool_commands.text_sharing.shittycodingagent import ShittyCodingAgentSharer +from wolfharness_commands.text_sharing.base import ShareResult, TextSharer, Visibility +from wolfharness_commands.text_sharing.github_gist import GistSharer +from wolfharness_commands.text_sharing.opencode import OpenCodeSharer +from wolfharness_commands.text_sharing.paste_rs import PasteRsSharer +from wolfharness_commands.text_sharing.pastebin import PastebinSharer +from wolfharness_commands.text_sharing.shittycodingagent import ShittyCodingAgentSharer TextSharerStr = Literal["gist", "pastebin", "paste_rs", "opencode", "shittycodingagent"] diff --git a/src/agentpool_commands/text_sharing/base.py b/src/wolfharness_commands/text_sharing/base.py similarity index 98% rename from src/agentpool_commands/text_sharing/base.py rename to src/wolfharness_commands/text_sharing/base.py index c2a0fb1d9..cb76b54cb 100644 --- a/src/agentpool_commands/text_sharing/base.py +++ b/src/wolfharness_commands/text_sharing/base.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory Visibility = Literal["public", "unlisted", "private"] diff --git a/src/agentpool_commands/text_sharing/github_gist.py b/src/wolfharness_commands/text_sharing/github_gist.py similarity index 94% rename from src/agentpool_commands/text_sharing/github_gist.py rename to src/wolfharness_commands/text_sharing/github_gist.py index cefe52e33..762cb3d71 100644 --- a/src/agentpool_commands/text_sharing/github_gist.py +++ b/src/wolfharness_commands/text_sharing/github_gist.py @@ -5,11 +5,11 @@ import os from typing import TYPE_CHECKING, Any -from agentpool_commands.text_sharing.base import ShareResult, TextSharer +from wolfharness_commands.text_sharing.base import ShareResult, TextSharer if TYPE_CHECKING: - from agentpool_commands.text_sharing.base import Visibility + from wolfharness_commands.text_sharing.base import Visibility class GistSharer(TextSharer): diff --git a/src/agentpool_commands/text_sharing/opencode.py b/src/wolfharness_commands/text_sharing/opencode.py similarity index 97% rename from src/agentpool_commands/text_sharing/opencode.py rename to src/wolfharness_commands/text_sharing/opencode.py index 23f3d96e9..a0c2b0725 100644 --- a/src/agentpool_commands/text_sharing/opencode.py +++ b/src/wolfharness_commands/text_sharing/opencode.py @@ -29,15 +29,15 @@ import httpx -from agentpool.utils.time_utils import now_ms -from agentpool_commands.text_sharing.base import ShareResult, TextSharer +from wolfharness.utils.time_utils import now_ms +from wolfharness_commands.text_sharing.base import ShareResult, TextSharer if TYPE_CHECKING: from anyenv.text_sharing.base import Visibility - from agentpool.messaging.message_history import MessageHistory - from agentpool.messaging.messages import ChatMessage + from wolfharness.messaging.message_history import MessageHistory + from wolfharness.messaging.messages import ChatMessage class OpenCodeSharer(TextSharer): @@ -56,7 +56,7 @@ class OpenCodeSharer(TextSharer): Share a conversation: ```python - # Use share_conversation() with a Conversation object from agentpool + # Use share_conversation() with a Conversation object from wolfharness async with OpenCodeSharer() as sharer: result = await sharer.share_conversation( conversation, @@ -257,7 +257,7 @@ async def _share_messages( resp.raise_for_status() # Sync each message and its parts for chat_msg in messages: - from agentpool_server.opencode_server.converters import ( + from wolfharness_server.opencode_server.converters import ( chat_message_to_opencode, ) @@ -268,7 +268,7 @@ async def _share_messages( working_dir="/tmp", agent_name=chat_msg.name or "default", model_id=chat_msg.model_name or "unknown", - provider_id="agentpool", + provider_id="wolfharness", ) # Serialize to dicts with camelCase and no None values diff --git a/src/agentpool_commands/text_sharing/paste_rs.py b/src/wolfharness_commands/text_sharing/paste_rs.py similarity index 91% rename from src/agentpool_commands/text_sharing/paste_rs.py rename to src/wolfharness_commands/text_sharing/paste_rs.py index 8ed67ba3a..8a0477713 100644 --- a/src/agentpool_commands/text_sharing/paste_rs.py +++ b/src/wolfharness_commands/text_sharing/paste_rs.py @@ -4,11 +4,11 @@ from typing import TYPE_CHECKING -from agentpool_commands.text_sharing.base import ShareResult, TextSharer +from wolfharness_commands.text_sharing.base import ShareResult, TextSharer if TYPE_CHECKING: - from agentpool_commands.text_sharing.base import Visibility + from wolfharness_commands.text_sharing.base import Visibility class PasteRsSharer(TextSharer): diff --git a/src/agentpool_commands/text_sharing/pastebin.py b/src/wolfharness_commands/text_sharing/pastebin.py similarity index 96% rename from src/agentpool_commands/text_sharing/pastebin.py rename to src/wolfharness_commands/text_sharing/pastebin.py index 71d5ec858..dc2e686db 100644 --- a/src/agentpool_commands/text_sharing/pastebin.py +++ b/src/wolfharness_commands/text_sharing/pastebin.py @@ -5,11 +5,11 @@ import os from typing import TYPE_CHECKING, Any -from agentpool_commands.text_sharing.base import ShareResult, TextSharer +from wolfharness_commands.text_sharing.base import ShareResult, TextSharer if TYPE_CHECKING: - from agentpool_commands.text_sharing.base import Visibility + from wolfharness_commands.text_sharing.base import Visibility # Pastebin expiry values diff --git a/src/agentpool_commands/text_sharing/shittycodingagent.py b/src/wolfharness_commands/text_sharing/shittycodingagent.py similarity index 95% rename from src/agentpool_commands/text_sharing/shittycodingagent.py rename to src/wolfharness_commands/text_sharing/shittycodingagent.py index 2486cb85b..5edf43cb2 100644 --- a/src/agentpool_commands/text_sharing/shittycodingagent.py +++ b/src/wolfharness_commands/text_sharing/shittycodingagent.py @@ -8,11 +8,11 @@ import os from typing import TYPE_CHECKING, Any -from agentpool_commands.text_sharing.base import ShareResult, TextSharer +from wolfharness_commands.text_sharing.base import ShareResult, TextSharer if TYPE_CHECKING: - from agentpool_commands.text_sharing.base import Visibility + from wolfharness_commands.text_sharing.base import Visibility PREVIEW_BASE_URL = "https://shittycodingagent.ai/session" diff --git a/src/agentpool_commands/tools.py b/src/wolfharness_commands/tools.py similarity index 94% rename from src/agentpool_commands/tools.py rename to src/wolfharness_commands/tools.py index 34addae00..da2755c03 100644 --- a/src/agentpool_commands/tools.py +++ b/src/wolfharness_commands/tools.py @@ -7,15 +7,15 @@ from slashed import CommandContext, CommandError, CompletionContext from slashed.completers import CallbackCompleter -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.utils.importing import import_callable -from agentpool_commands.base import NodeCommand -from agentpool_commands.markdown_utils import format_table +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.utils.importing import import_callable +from wolfharness_commands.base import NodeCommand +from wolfharness_commands.markdown_utils import format_table if TYPE_CHECKING: - from agentpool.messaging import MessageNode + from wolfharness.messaging import MessageNode logger = get_logger(__name__) @@ -33,7 +33,7 @@ class ListToolsCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -82,7 +82,7 @@ class ShowToolCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -138,7 +138,7 @@ class EnableToolCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -177,7 +177,7 @@ class DisableToolCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -217,7 +217,7 @@ class RegisterToolCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -280,7 +280,7 @@ class RegisterCodeToolCommand(NodeCommand): @classmethod def supports_node(cls, node: MessageNode[Any, Any]) -> bool: - from agentpool import Agent + from wolfharness import Agent return isinstance(node, Agent) @@ -300,7 +300,7 @@ async def execute_command( name: Optional custom name for the tool description: Optional custom description """ - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool try: tool_obj = Tool.from_code(code, name=name, description=description) diff --git a/src/agentpool_commands/utils.py b/src/wolfharness_commands/utils.py similarity index 95% rename from src/agentpool_commands/utils.py rename to src/wolfharness_commands/utils.py index 698d7f286..59b1ea267 100644 --- a/src/agentpool_commands/utils.py +++ b/src/wolfharness_commands/utils.py @@ -8,12 +8,12 @@ from slashed import CommandContext, CommandError -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_commands.base import NodeCommand +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_commands.base import NodeCommand if TYPE_CHECKING: - from agentpool_commands.text_sharing import TextSharerStr, Visibility + from wolfharness_commands.text_sharing import TextSharerStr, Visibility LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] @@ -148,7 +148,7 @@ async def execute_command( syntax: Syntax highlighting (e.g., "python", "markdown") visibility: Visibility level """ - from agentpool_commands.text_sharing import get_sharer + from wolfharness_commands.text_sharing import get_sharer conversation = ctx.context.agent.conversation @@ -218,7 +218,7 @@ async def execute_command( logger_filter: Only show logs from loggers containing this string limit: Maximum number of log entries to return """ - from agentpool_toolsets.builtin.debug import get_memory_handler + from wolfharness_toolsets.builtin.debug import get_memory_handler # Convert limit to int (slashed passes args as strings) limit_int = int(limit) if isinstance(limit, str) else limit diff --git a/src/agentpool_commands/workers.py b/src/wolfharness_commands/workers.py similarity index 93% rename from src/agentpool_commands/workers.py rename to src/wolfharness_commands/workers.py index 8c7f24528..d336db01e 100644 --- a/src/agentpool_commands/workers.py +++ b/src/wolfharness_commands/workers.py @@ -7,12 +7,12 @@ from slashed import CommandContext, CommandError from slashed.completers import CallbackCompleter -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.log import get_logger -from agentpool.messaging.messagenode import MessageNode # noqa: TC001 -from agentpool_commands.base import AgentCommand -from agentpool_commands.completers import get_available_agents -from agentpool_commands.markdown_utils import format_table +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.log import get_logger +from wolfharness.messaging.messagenode import MessageNode # noqa: TC001 +from wolfharness_commands.base import AgentCommand +from wolfharness_commands.completers import get_available_agents +from wolfharness_commands.markdown_utils import format_table logger = get_logger(__name__) diff --git a/src/agentpool_config/__init__.py b/src/wolfharness_config/__init__.py similarity index 71% rename from src/agentpool_config/__init__.py rename to src/wolfharness_config/__init__.py index fc490b167..1f32e60be 100644 --- a/src/agentpool_config/__init__.py +++ b/src/wolfharness_config/__init__.py @@ -6,31 +6,31 @@ from typing import Annotated from pydantic import Field -from agentpool_config.tools import ImportToolConfig, BaseToolConfig -from agentpool_config.agentpool_tools import AgentpoolToolConfig -from agentpool_config.builtin_tools import BuiltinToolConfig +from wolfharness_config.tools import ImportToolConfig, BaseToolConfig +from wolfharness_config.wolfharness_tools import AgentpoolToolConfig +from wolfharness_config.builtin_tools import BuiltinToolConfig -from agentpool_config.capabilities import CapabilityConfig -from agentpool_config.forward_targets import ForwardingTarget -from agentpool_config.session import SessionQuery -from agentpool_config.session_pool import ACPConfig, OpenCodeConfig, SessionPoolConfig -from agentpool_config.teams import TeamConfig, TeamMemberConfig -from agentpool_config.durable import CheckpointConfig, DeferredToolConfig -from agentpool_config.mcp_server import ( +from wolfharness_config.capabilities import CapabilityConfig +from wolfharness_config.forward_targets import ForwardingTarget +from wolfharness_config.session import SessionQuery +from wolfharness_config.session_pool import ACPConfig, OpenCodeConfig, SessionPoolConfig +from wolfharness_config.teams import TeamConfig, TeamMemberConfig +from wolfharness_config.durable import CheckpointConfig, DeferredToolConfig +from wolfharness_config.mcp_server import ( BaseMCPServerConfig, StdioMCPServerConfig, StreamableHTTPMCPServerConfig, MCPServerConfig, SSEMCPServerConfig, ) -from agentpool_config.event_handlers import ( +from wolfharness_config.event_handlers import ( BaseEventHandlerConfig, StdoutEventHandlerConfig, CallbackEventHandlerConfig, EventHandlerConfig, resolve_handler_configs, ) -from agentpool_config.hooks import ( +from wolfharness_config.hooks import ( BaseHookConfig, CallableHookConfig, CommandHookConfig, @@ -38,23 +38,23 @@ HooksConfig, PromptHookConfig, ) -from agentpool_config.graph_config import ( +from wolfharness_config.graph_config import ( GraphConfig, GraphEdgeConfig, GraphJoinConfig, GraphStepConfig, ) -from agentpool_config.graph_translation import ( +from wolfharness_config.graph_translation import ( build_steps_from_agents, translate_config_to_graph, translate_connections_to_edges, translate_team_to_graph, translate_teams_to_graphs, ) -from agentpool_config.toolsets import ToolsetConfig -from agentpool_config.skills import SkillsConfig, DEFAULT_SKILLS_PATHS -from agentpool_config.skill_commands import SkillSlashConfig, SkillCommandConfig -from agentpool_config.resolution import ( +from wolfharness_config.toolsets import ToolsetConfig +from wolfharness_config.skills import SkillsConfig, DEFAULT_SKILLS_PATHS +from wolfharness_config.skill_commands import SkillSlashConfig, SkillCommandConfig +from wolfharness_config.resolution import ( ConfigLayer, ConfigSource, ResolvedConfig, diff --git a/src/agentpool_config/builtin_tools.py b/src/wolfharness_config/builtin_tools.py similarity index 99% rename from src/agentpool_config/builtin_tools.py rename to src/wolfharness_config/builtin_tools.py index f731deba3..930734733 100644 --- a/src/agentpool_config/builtin_tools.py +++ b/src/wolfharness_config/builtin_tools.py @@ -16,7 +16,7 @@ ) from pydantic_ai.native_tools import ImageAspectRatio, WebSearchUserLocation # noqa: TC002 -from agentpool_config.tools import BaseToolConfig +from wolfharness_config.tools import BaseToolConfig if TYPE_CHECKING: diff --git a/src/agentpool_config/capabilities.py b/src/wolfharness_config/capabilities.py similarity index 94% rename from src/agentpool_config/capabilities.py rename to src/wolfharness_config/capabilities.py index cc23bfaae..f82ca43e4 100644 --- a/src/agentpool_config/capabilities.py +++ b/src/wolfharness_config/capabilities.py @@ -8,7 +8,7 @@ 1. **Built-in short names** (``loop_detection``, ``token_budget``, etc.) validated against typed config models. 2. **Entry-point names** (e.g. ``mermaid_lint``) resolved via the - ``agentpool.capabilities`` entry-point group using + ``wolfharness.capabilities`` entry-point group using :class:`EntryPointCapabilityConfig`. 3. **Python import paths** (e.g. ``pydantic_ai.capabilities.Instrumentation``) resolved via :class:`GenericCapabilityConfig` using ``__import__``. @@ -33,14 +33,16 @@ }) IMPORT_MAP: dict[str, str] = { - "loop_detection": "agentpool.capabilities.loop_detection.LoopDetectionCapability", - "token_budget": "agentpool.capabilities.token_budget.TokenBudgetCapability", - "tool_output_budget": ("agentpool.capabilities.tool_output_budget.ToolOutputBudgetCapability"), - "dcp": "agentpool.capabilities.dcp.DynamicContextPruningCapability", - "skill_activation": "agentpool.capabilities.skill_manager_cap:SkillManagerCap", - "memory": "agentpool.capabilities.memory.MemoryCapability", - "modality_filter": "agentpool.capabilities.modality_filter.ModalityFilterCapability", - "viking": "agentpool.capabilities.viking.VikingCapability", + "loop_detection": "wolfharness.capabilities.loop_detection.LoopDetectionCapability", + "token_budget": "wolfharness.capabilities.token_budget.TokenBudgetCapability", + "tool_output_budget": ( + "wolfharness.capabilities.tool_output_budget.ToolOutputBudgetCapability" + ), + "dcp": "wolfharness.capabilities.dcp.DynamicContextPruningCapability", + "skill_activation": "wolfharness.capabilities.skill_manager_cap:SkillManagerCap", + "memory": "wolfharness.capabilities.memory.MemoryCapability", + "modality_filter": "wolfharness.capabilities.modality_filter.ModalityFilterCapability", + "viking": "wolfharness.capabilities.viking.VikingCapability", } @@ -227,7 +229,7 @@ class VikingCapabilityConfig(BaseModel): auto_ingest_sanitize: bool = True """Strip ```` and ```` XML blocks from messages before ingestion to prevent feedback loops.""" - auto_ingest_source_type: str = "agentpool" + auto_ingest_source_type: str = "wolfharness" """Source type metadata for ingested sessions.""" auto_ingest_keep_recent_turns: int = 0 """Number of recent turns to retain in the session after commit. @@ -305,14 +307,14 @@ class EntryPointCapabilityConfig(BaseModel): """Configuration for a capability loaded via entry-point name. Used when ``type`` matches a name registered in the - ``agentpool.capabilities`` entry-point group (e.g. ``"mermaid_lint"``). + ``wolfharness.capabilities`` entry-point group (e.g. ``"mermaid_lint"``). The entry-point registry maps short names to capability classes, enabling external packages to register capabilities without requiring users to know the full Python import path. """ type: str - """Entry-point name registered under ``agentpool.capabilities``.""" + """Entry-point name registered under ``wolfharness.capabilities``.""" args: dict[str, Any] = Field(default_factory=dict) """Arguments to pass to the capability constructor.""" @@ -321,10 +323,10 @@ def build(self) -> Any: """Resolve the entry-point name and instantiate the capability. Uses :mod:`importlib.metadata` directly to discover entry points - in the ``agentpool.capabilities`` group, avoiding a dependency - on the :mod:`agentpool` package (which would violate the - import-linter contract that ``agentpool_config`` must not import - from ``agentpool``). + in the ``wolfharness.capabilities`` group, avoiding a dependency + on the :mod:`wolfharness` package (which would violate the + import-linter contract that ``wolfharness_config`` must not import + from ``wolfharness``). Returns: Instantiated capability object. @@ -335,7 +337,7 @@ def build(self) -> Any: """ from importlib.metadata import entry_points - eps = entry_points(group="agentpool.capabilities") + eps = entry_points(group="wolfharness.capabilities") for ep in eps: if ep.name == self.type: cls = ep.load() diff --git a/src/agentpool_config/commands.py b/src/wolfharness_config/commands.py similarity index 100% rename from src/agentpool_config/commands.py rename to src/wolfharness_config/commands.py diff --git a/src/agentpool_config/compaction.py b/src/wolfharness_config/compaction.py similarity index 86% rename from src/agentpool_config/compaction.py rename to src/wolfharness_config/compaction.py index 5ef4d4532..4839a9eba 100644 --- a/src/agentpool_config/compaction.py +++ b/src/wolfharness_config/compaction.py @@ -9,7 +9,7 @@ if TYPE_CHECKING: - from agentpool.messaging.compaction import CompactionPipeline, CompactionStep + from wolfharness.messaging.compaction import CompactionPipeline, CompactionStep class FilterThinkingConfig(Schema): @@ -18,7 +18,7 @@ class FilterThinkingConfig(Schema): type: Literal["filter_thinking"] = "filter_thinking" def build(self) -> CompactionStep: - from agentpool.messaging.compaction import FilterThinking + from wolfharness.messaging.compaction import FilterThinking return FilterThinking() @@ -29,7 +29,7 @@ class FilterRetryPromptsConfig(Schema): type: Literal["filter_retry_prompts"] = "filter_retry_prompts" def build(self) -> CompactionStep: - from agentpool.messaging.compaction import FilterRetryPrompts + from wolfharness.messaging.compaction import FilterRetryPrompts return FilterRetryPrompts() @@ -41,7 +41,7 @@ class FilterBinaryContentConfig(Schema): keep_references: bool = False def build(self) -> CompactionStep: - from agentpool.messaging.compaction import FilterBinaryContent + from wolfharness.messaging.compaction import FilterBinaryContent return FilterBinaryContent(keep_references=self.keep_references) @@ -54,7 +54,7 @@ class FilterToolCallsConfig(Schema): include_only: list[str] | None = None def build(self) -> CompactionStep: - from agentpool.messaging.compaction import FilterToolCalls + from wolfharness.messaging.compaction import FilterToolCalls return FilterToolCalls(exclude_tools=self.exclude_tools, include_only=self.include_only) @@ -65,7 +65,7 @@ class FilterEmptyMessagesConfig(Schema): type: Literal["filter_empty"] = "filter_empty" def build(self) -> CompactionStep: - from agentpool.messaging.compaction import FilterEmptyMessages + from wolfharness.messaging.compaction import FilterEmptyMessages return FilterEmptyMessages() @@ -78,7 +78,7 @@ class TruncateToolOutputsConfig(Schema): suffix: str = "\n... [truncated]" def build(self) -> CompactionStep: - from agentpool.messaging.compaction import TruncateToolOutputs + from wolfharness.messaging.compaction import TruncateToolOutputs return TruncateToolOutputs(max_length=self.max_length, suffix=self.suffix) @@ -91,7 +91,7 @@ class TruncateTextPartsConfig(Schema): suffix: str = "\n... [truncated]" def build(self) -> CompactionStep: - from agentpool.messaging.compaction import TruncateTextParts + from wolfharness.messaging.compaction import TruncateTextParts return TruncateTextParts(max_length=self.max_length, suffix=self.suffix) @@ -104,7 +104,7 @@ class KeepLastMessagesConfig(Schema): count_pairs: bool = True def build(self) -> CompactionStep: - from agentpool.messaging.compaction import KeepLastMessages + from wolfharness.messaging.compaction import KeepLastMessages return KeepLastMessages(count=self.count, count_pairs=self.count_pairs) @@ -116,7 +116,7 @@ class KeepFirstMessagesConfig(Schema): count: int = 2 def build(self) -> CompactionStep: - from agentpool.messaging.compaction import KeepFirstMessages + from wolfharness.messaging.compaction import KeepFirstMessages return KeepFirstMessages(count=self.count) @@ -129,7 +129,7 @@ class KeepFirstAndLastConfig(Schema): last_count: int = 5 def build(self) -> CompactionStep: - from agentpool.messaging.compaction import KeepFirstAndLast + from wolfharness.messaging.compaction import KeepFirstAndLast return KeepFirstAndLast(first_count=self.first_count, last_count=self.last_count) @@ -142,7 +142,7 @@ class TokenBudgetConfig(Schema): model: str = "gpt-4o" def build(self) -> CompactionStep: - from agentpool.messaging.compaction import TokenBudget + from wolfharness.messaging.compaction import TokenBudget return TokenBudget(max_tokens=self.max_tokens, model=self.model) @@ -157,7 +157,7 @@ class SummarizeConfig(Schema): summary_prompt: str | None = None def build(self) -> CompactionStep: - from agentpool.messaging.compaction import Summarize + from wolfharness.messaging.compaction import Summarize kwargs: dict[str, Any] = { "model": self.model, @@ -177,7 +177,7 @@ class WhenMessageCountExceedsConfig(Schema): step: "CompactionStepConfig" def build(self) -> CompactionStep: - from agentpool.messaging.compaction import WhenMessageCountExceeds + from wolfharness.messaging.compaction import WhenMessageCountExceeds return WhenMessageCountExceeds(step=self.step.build(), threshold=self.threshold) @@ -239,7 +239,7 @@ class CompactionConfig(Schema): def build(self) -> CompactionPipeline: """Build a CompactionPipeline from this configuration.""" - from agentpool.messaging.compaction import ( + from wolfharness.messaging.compaction import ( CompactionPipeline, balanced_context, minimal_context, diff --git a/src/agentpool_config/conditions.py b/src/wolfharness_config/conditions.py similarity index 97% rename from src/agentpool_config/conditions.py rename to src/wolfharness_config/conditions.py index a3a33b764..c0c371c27 100644 --- a/src/agentpool_config/conditions.py +++ b/src/wolfharness_config/conditions.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: - from agentpool.talk.registry import EventContext + from wolfharness.talk.registry import EventContext class ConnectionCondition(Schema): @@ -55,7 +55,7 @@ class Jinja2Condition(ConnectionCondition): async def check(self, context: EventContext[Any]) -> bool: from jinjarope import Environment - from agentpool.utils.time_utils import get_now + from wolfharness.utils.time_utils import get_now env = Environment(trim_blocks=True, lstrip_blocks=True, enable_async=True) template = env.from_string(self.template) @@ -147,7 +147,7 @@ class TimeCondition(ConnectionCondition): async def check(self, context: EventContext[Any]) -> bool: """Check if time duration has elapsed.""" - from agentpool.utils.time_utils import get_now + from wolfharness.utils.time_utils import get_now elapsed = get_now() - context.stats.start_time return elapsed >= self.duration @@ -247,7 +247,7 @@ class CallableCondition(ConnectionCondition): async def check(self, context: EventContext[Any]) -> bool: """Execute predicate function.""" - from agentpool.utils.inspection import execute + from wolfharness.utils.inspection import execute val = await execute(self.predicate, context.message, context.stats) return bool(val) diff --git a/src/agentpool_config/context.py b/src/wolfharness_config/context.py similarity index 97% rename from src/agentpool_config/context.py rename to src/wolfharness_config/context.py index fcb3dd28e..cf493d7a9 100644 --- a/src/agentpool_config/context.py +++ b/src/wolfharness_config/context.py @@ -98,7 +98,7 @@ def __enter__(self) -> Self: self._token = CONFIG_DIR.set(self._config_dir) import logging - logging.getLogger("agentpool.config").debug( + logging.getLogger("wolfharness.config").debug( "ConfigContextManager.__enter__: set _config_dir_global=%s, previous=%s", _config_dir_global, self._previous_dir, @@ -120,7 +120,7 @@ def __exit__( _config_dir_global = self._previous_dir import logging - logging.getLogger("agentpool.config").debug( + logging.getLogger("wolfharness.config").debug( "ConfigContextManager.__exit__: restored _config_dir_global=%s (was %s)", _config_dir_global, old_value, diff --git a/src/agentpool_config/converters.py b/src/wolfharness_config/converters.py similarity index 100% rename from src/agentpool_config/converters.py rename to src/wolfharness_config/converters.py diff --git a/src/agentpool_config/durable.py b/src/wolfharness_config/durable.py similarity index 99% rename from src/agentpool_config/durable.py rename to src/wolfharness_config/durable.py index c696a2fb3..8a3a1a36d 100644 --- a/src/agentpool_config/durable.py +++ b/src/wolfharness_config/durable.py @@ -8,7 +8,7 @@ from pydantic import ConfigDict, Field, field_validator from schemez import Schema -from agentpool.utils.parse_time import parse_time_period +from wolfharness.utils.parse_time import parse_time_period class BaseDurableExecutionConfig(Schema): diff --git a/src/agentpool_config/event_handlers.py b/src/wolfharness_config/event_handlers.py similarity index 97% rename from src/agentpool_config/event_handlers.py rename to src/wolfharness_config/event_handlers.py index cd1b67238..9755c51b3 100644 --- a/src/agentpool_config/event_handlers.py +++ b/src/wolfharness_config/event_handlers.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.common_types import IndividualEventHandler + from wolfharness.common_types import IndividualEventHandler StdOutStyle = Literal["simple", "detailed"] @@ -363,7 +363,7 @@ class StdoutEventHandlerConfig(BaseEventHandlerConfig): def get_handler(self) -> IndividualEventHandler: """Get the builtin event handler.""" - from agentpool.agents.events import detailed_print_handler, simple_print_handler + from wolfharness.agents.events import detailed_print_handler, simple_print_handler handlers = {"simple": simple_print_handler, "detailed": detailed_print_handler} return handlers[self.handler] @@ -387,7 +387,7 @@ class CallbackEventHandlerConfig(BaseEventHandlerConfig): def get_handler(self) -> IndividualEventHandler: """Import and return the callback handler.""" - from agentpool.utils.importing import import_callable + from wolfharness.utils.importing import import_callable return import_callable(self.import_path) @@ -423,7 +423,7 @@ class FileStreamEventHandlerConfig(BaseEventHandlerConfig): def get_handler(self) -> IndividualEventHandler: """Create and return the file stream handler.""" - from agentpool.agents.events.builtin_handlers import create_file_stream_handler + from wolfharness.agents.events.builtin_handlers import create_file_stream_handler return create_file_stream_handler( path=self.path, @@ -498,7 +498,7 @@ class TTSEventHandlerConfig(BaseEventHandlerConfig): def get_handler(self) -> IndividualEventHandler: """Get the TTS event handler.""" - from agentpool.agents.events.tts_handlers import OpenAITTSEventHandler + from wolfharness.agents.events.tts_handlers import OpenAITTSEventHandler key = self.api_key.get_secret_value() if self.api_key else None return OpenAITTSEventHandler( @@ -595,7 +595,7 @@ class EdgeTTSEventHandlerConfig(BaseEventHandlerConfig): def get_handler(self) -> IndividualEventHandler: """Get the Edge TTS event handler.""" - from agentpool.agents.events.tts_handlers import EdgeTTSEventHandler + from wolfharness.agents.events.tts_handlers import EdgeTTSEventHandler # Convert to Edge TTS string formats # speed: 1.0 -> "+0%", 1.5 -> "+50%", 0.5 -> "-50%" diff --git a/src/agentpool_config/events.py b/src/wolfharness_config/events.py similarity index 98% rename from src/agentpool_config/events.py rename to src/wolfharness_config/events.py index 741e36c4b..08d053814 100644 --- a/src/agentpool_config/events.py +++ b/src/wolfharness_config/events.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: - from agentpool.messaging.events import ConnectionEventData + from wolfharness.messaging.events import ConnectionEventData ConnectionEventType = Literal[ diff --git a/src/agentpool_config/forward_targets.py b/src/wolfharness_config/forward_targets.py similarity index 95% rename from src/agentpool_config/forward_targets.py rename to src/wolfharness_config/forward_targets.py index 7e7df4f46..9350d9b2f 100644 --- a/src/agentpool_config/forward_targets.py +++ b/src/wolfharness_config/forward_targets.py @@ -11,7 +11,7 @@ from schemez import Schema from upathtools import to_upath -from agentpool_config.conditions import Condition +from wolfharness_config.conditions import Condition if TYPE_CHECKING: @@ -20,7 +20,7 @@ from pydantic_ai.models.function import FunctionModel from upathtools import UPath - from agentpool.messaging import ChatMessage, MessageNode + from wolfharness.messaging import ChatMessage, MessageNode ConnectionType = Literal["run", "context", "forward"] @@ -204,7 +204,7 @@ class FileConnectionConfig(ConnectionConfig): """File encoding to use.""" def get_node(self, nodes: Sequence[MessageNode[Any, Any]]) -> MessageNode[Any, Any]: - from agentpool import Agent + from wolfharness import Agent agent_name = f"file_writer_{Path(self.path).stem}" return Agent(model=self.get_model(), name=agent_name) @@ -218,7 +218,7 @@ def format_message(self, message: ChatMessage[Any]) -> str: def resolve_path(self, context: dict[str, str]) -> UPath: """Resolve path template with context variables.""" - from agentpool.utils.time_utils import get_now + from wolfharness.utils.time_utils import get_now now = get_now() date = now.strftime("%Y-%m-%d") @@ -230,7 +230,7 @@ def get_model(self) -> FunctionModel: """Get provider for file writing.""" from jinja2 import Template - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model path_obj = to_upath(self.path) template_obj = Template(self.template) @@ -270,8 +270,8 @@ def process_message(message: ChatMessage[Any], **kwargs) -> Any """Additional kwargs to pass to the callable.""" def get_node(self, nodes: Sequence[MessageNode[Any, Any]]) -> MessageNode[Any, Any]: - from agentpool import Agent - from agentpool.utils.inspection import get_fn_name + from wolfharness import Agent + from wolfharness.utils.inspection import get_fn_name return Agent(model=self.get_model(), name=get_fn_name(self.callable)) @@ -280,13 +280,13 @@ async def process_message(self, message: ChatMessage[Any]) -> Any: Handles both sync and async callables transparently. """ - from agentpool.utils.inspection import execute + from wolfharness.utils.inspection import execute return await execute(self.callable, message, **self.kw_args) def get_model(self) -> FunctionModel: """Get provider for callable.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model return function_to_model(self.callable) diff --git a/src/agentpool_config/graph_config.py b/src/wolfharness_config/graph_config.py similarity index 98% rename from src/agentpool_config/graph_config.py rename to src/wolfharness_config/graph_config.py index 1ba6e7d5d..6508bc958 100644 --- a/src/agentpool_config/graph_config.py +++ b/src/wolfharness_config/graph_config.py @@ -14,8 +14,8 @@ from pydantic import ConfigDict, Field, ImportString, model_serializer from schemez import Schema -from agentpool_config.conditions import Condition -from agentpool_config.mcp_server import MCPServerConfig +from wolfharness_config.conditions import Condition +from wolfharness_config.mcp_server import MCPServerConfig class GraphStepConfig(Schema): diff --git a/src/agentpool_config/graph_translation.py b/src/wolfharness_config/graph_translation.py similarity index 97% rename from src/agentpool_config/graph_translation.py rename to src/wolfharness_config/graph_translation.py index a8e3f26a9..e30047ae8 100644 --- a/src/agentpool_config/graph_translation.py +++ b/src/wolfharness_config/graph_translation.py @@ -45,12 +45,12 @@ from typing import TYPE_CHECKING, Any -from agentpool_config.graph_config import GraphConfig, GraphEdgeConfig, GraphStepConfig +from wolfharness_config.graph_config import GraphConfig, GraphEdgeConfig, GraphStepConfig if TYPE_CHECKING: - from agentpool_config.nodes import NodeConfig - from agentpool_config.teams import TeamConfig + from wolfharness_config.nodes import NodeConfig + from wolfharness_config.teams import TeamConfig def translate_team_to_graph( diff --git a/src/agentpool_config/hook_conditions.py b/src/wolfharness_config/hook_conditions.py similarity index 100% rename from src/agentpool_config/hook_conditions.py rename to src/wolfharness_config/hook_conditions.py diff --git a/src/agentpool_config/hooks.py b/src/wolfharness_config/hooks.py similarity index 96% rename from src/agentpool_config/hooks.py rename to src/wolfharness_config/hooks.py index 48e271726..eaaeb8c98 100644 --- a/src/agentpool_config/hooks.py +++ b/src/wolfharness_config/hooks.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: from exxec import ExecutionEnvironment - from agentpool.hooks.base import Hook, HookEvent + from wolfharness.hooks.base import Hook, HookEvent class BaseHookConfig(Schema): @@ -111,7 +111,7 @@ class CommandHookConfig(BaseHookConfig): def get_hook(self, event: HookEvent) -> Hook: """Create runtime command hook.""" - from agentpool.hooks import CommandHook + from wolfharness.hooks import CommandHook return CommandHook( event=event, @@ -172,7 +172,7 @@ class CallableHookConfig(BaseHookConfig): def get_hook(self, event: HookEvent) -> Hook: """Create runtime callable hook.""" - from agentpool.hooks import CallableHook + from wolfharness.hooks import CallableHook return CallableHook( event=event, @@ -215,7 +215,7 @@ class PromptHookConfig(BaseHookConfig): def get_hook(self, event: HookEvent) -> Hook: """Create runtime prompt hook.""" - from agentpool.hooks import PromptHook + from wolfharness.hooks import PromptHook return PromptHook( event=event, @@ -284,7 +284,7 @@ def get_agent_hooks(self) -> AgentHooks: Returns: AgentHooks instance with all hooks instantiated. """ - from agentpool.hooks import AgentHooks + from wolfharness.hooks import AgentHooks return AgentHooks( pre_turn=[cfg.get_hook("pre_turn") for cfg in self.pre_turn], @@ -297,4 +297,4 @@ def get_agent_hooks(self) -> AgentHooks: # Import for type checking only - avoid circular imports if TYPE_CHECKING: - from agentpool.hooks import AgentHooks + from wolfharness.hooks import AgentHooks diff --git a/src/agentpool_config/instructions.py b/src/wolfharness_config/instructions.py similarity index 100% rename from src/agentpool_config/instructions.py rename to src/wolfharness_config/instructions.py diff --git a/src/agentpool_config/jinja.py b/src/wolfharness_config/jinja.py similarity index 99% rename from src/agentpool_config/jinja.py rename to src/wolfharness_config/jinja.py index 5b07cce1c..b74c0134e 100644 --- a/src/agentpool_config/jinja.py +++ b/src/wolfharness_config/jinja.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field from schemez import Schema -from agentpool_config import ToolConfig +from wolfharness_config import ToolConfig if TYPE_CHECKING: diff --git a/src/agentpool_config/knowledge.py b/src/wolfharness_config/knowledge.py similarity index 87% rename from src/agentpool_config/knowledge.py rename to src/wolfharness_config/knowledge.py index a2cec2d7c..7c5762060 100644 --- a/src/agentpool_config/knowledge.py +++ b/src/wolfharness_config/knowledge.py @@ -7,13 +7,13 @@ from pydantic import ConfigDict, Field from schemez import Schema -from agentpool_config.paths import ConfigPath +from wolfharness_config.paths import ConfigPath if TYPE_CHECKING: from upathtools import UPath - from agentpool.prompts.prompts import PromptType # noqa: TC004 + from wolfharness.prompts.prompts import PromptType # noqa: TC004 class Knowledge(Schema): @@ -22,7 +22,7 @@ class Knowledge(Schema): Supports both simple paths and rich resource types for content loading, plus AgentPool's prompt system for dynamic content generation. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/knowledge_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/knowledge_configuration/ """ model_config = ConfigDict(json_schema_extra={"title": "Knowledge Configuration"}) diff --git a/src/agentpool_config/lifecycle.py b/src/wolfharness_config/lifecycle.py similarity index 100% rename from src/agentpool_config/lifecycle.py rename to src/wolfharness_config/lifecycle.py diff --git a/src/agentpool_config/loaders.py b/src/wolfharness_config/loaders.py similarity index 98% rename from src/agentpool_config/loaders.py rename to src/wolfharness_config/loaders.py index 0b3df7af3..2ff02f88d 100644 --- a/src/agentpool_config/loaders.py +++ b/src/wolfharness_config/loaders.py @@ -11,8 +11,8 @@ from schemez import Schema from upathtools import to_upath -from agentpool.common_types import JsonObject -from agentpool.utils.importing import import_callable +from wolfharness.common_types import JsonObject +from wolfharness.utils.importing import import_callable class BaseResourceLoaderConfig(Schema): @@ -120,7 +120,7 @@ def is_templated(self) -> bool: @property def mime_type(self) -> str: """Get MIME type based on file extension.""" - from agentpool.mime_utils import guess_type + from wolfharness.mime_utils import guess_type return guess_type(str(self.path)) or "application/octet-stream" diff --git a/src/agentpool_config/mcp_server.py b/src/wolfharness_config/mcp_server.py similarity index 100% rename from src/agentpool_config/mcp_server.py rename to src/wolfharness_config/mcp_server.py diff --git a/src/agentpool_config/model_capabilities.py b/src/wolfharness_config/model_capabilities.py similarity index 100% rename from src/agentpool_config/model_capabilities.py rename to src/wolfharness_config/model_capabilities.py diff --git a/src/agentpool_config/nodes.py b/src/wolfharness_config/nodes.py similarity index 94% rename from src/agentpool_config/nodes.py rename to src/wolfharness_config/nodes.py index 1f3299829..29458b9e1 100644 --- a/src/agentpool_config/nodes.py +++ b/src/wolfharness_config/nodes.py @@ -11,28 +11,28 @@ from pydantic import ConfigDict, Field, HttpUrl, ImportString, field_validator from schemez import Schema -from agentpool_config.capabilities import CapabilityConfig -from agentpool_config.event_handlers import EventHandlerConfig, StdoutEventHandlerConfig -from agentpool_config.forward_targets import ( +from wolfharness_config.capabilities import CapabilityConfig +from wolfharness_config.event_handlers import EventHandlerConfig, StdoutEventHandlerConfig +from wolfharness_config.forward_targets import ( FileConnectionConfig, ForwardingTarget, NodeConnectionConfig, ) -from agentpool_config.hooks import HooksConfig -from agentpool_config.lifecycle import LifecycleConfig -from agentpool_config.mcp_server import ( +from wolfharness_config.hooks import HooksConfig +from wolfharness_config.lifecycle import LifecycleConfig +from wolfharness_config.mcp_server import ( BaseMCPServerConfig, MCPServerConfig, StdioMCPServerConfig, StreamableHTTPMCPServerConfig, ) -from agentpool_config.team_mode import TeamModeConfig +from wolfharness_config.team_mode import TeamModeConfig if TYPE_CHECKING: from exxec import ExecutionEnvironment - from agentpool.common_types import IndividualEventHandler + from wolfharness.common_types import IndividualEventHandler ToolConfirmationMode = Literal["always", "never", "per_tool"] @@ -164,7 +164,7 @@ def get_event_handlers(self) -> list[IndividualEventHandler]: Returns: List of event handler callables. """ - from agentpool_config.event_handlers import resolve_handler_configs + from wolfharness_config.event_handlers import resolve_handler_configs return resolve_handler_configs(self.event_handlers) @@ -307,7 +307,7 @@ class BaseAgentConfig(NodeConfig): """Per-agent team mode overlay. When non-None, merges with the global ``team_mode`` from the manifest - via :func:`agentpool_config.team_mode.resolve_team_mode` to produce + via :func:`wolfharness_config.team_mode.resolve_team_mode` to produce the effective team mode config for this agent. """ @@ -392,7 +392,7 @@ def parse_elicitation_timeout(cls, v: str | timedelta | float | None) -> timedel if isinstance(v, int | float): return timedelta(seconds=v) # Parse string like "5m", "1h", "300s" — simple inline parser - # to avoid importing from agentpool (layer separation). + # to avoid importing from wolfharness (layer separation). import re pattern = re.compile( diff --git a/src/agentpool_config/observability.py b/src/wolfharness_config/observability.py similarity index 99% rename from src/agentpool_config/observability.py rename to src/wolfharness_config/observability.py index 06a2c5dd7..6f79bccef 100644 --- a/src/agentpool_config/observability.py +++ b/src/wolfharness_config/observability.py @@ -19,7 +19,7 @@ class BaseObservabilityConfig(Schema): service_name: str | None = Field( default=None, - examples=["agentpool", "my-ai-service", "production-bot"], + examples=["wolfharness", "my-ai-service", "production-bot"], title="Service name", ) environment: str | None = Field( diff --git a/src/agentpool_config/output_types.py b/src/wolfharness_config/output_types.py similarity index 94% rename from src/agentpool_config/output_types.py rename to src/wolfharness_config/output_types.py index 4c75d3c3b..3af7aad63 100644 --- a/src/agentpool_config/output_types.py +++ b/src/wolfharness_config/output_types.py @@ -9,7 +9,7 @@ class StructuredResponseConfig(Schema): """Base class for response definitions. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/response_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/response_configuration/ """ response_schema: SchemaDef = Field( diff --git a/src/agentpool_config/paths.py b/src/wolfharness_config/paths.py similarity index 97% rename from src/agentpool_config/paths.py rename to src/wolfharness_config/paths.py index 1953340a8..1b698185b 100644 --- a/src/agentpool_config/paths.py +++ b/src/wolfharness_config/paths.py @@ -52,7 +52,7 @@ def resolve_config_path(path: str | UPath) -> UPath: return UPath(config_dir_env) / upath # Priority 4 & 5: Use get_config_dir() which checks both global and ContextVar - from agentpool_config.context import get_config_dir + from wolfharness_config.context import get_config_dir config_dir_ctx = get_config_dir() if config_dir_ctx is not None: diff --git a/src/agentpool_config/pool_server.py b/src/wolfharness_config/pool_server.py similarity index 100% rename from src/agentpool_config/pool_server.py rename to src/wolfharness_config/pool_server.py diff --git a/src/agentpool_config/prompt_hubs.py b/src/wolfharness_config/prompt_hubs.py similarity index 86% rename from src/agentpool_config/prompt_hubs.py rename to src/wolfharness_config/prompt_hubs.py index 8893e5c6b..c28301660 100644 --- a/src/agentpool_config/prompt_hubs.py +++ b/src/wolfharness_config/prompt_hubs.py @@ -10,10 +10,10 @@ if TYPE_CHECKING: - from agentpool_prompts.braintrust_hub import BraintrustPromptHub - from agentpool_prompts.fabric import FabricPromptHub - from agentpool_prompts.langfuse_hub import LangfusePromptHub - from agentpool_prompts.promptlayer_provider import PromptLayerProvider + from wolfharness_prompts.braintrust_hub import BraintrustPromptHub + from wolfharness_prompts.fabric import FabricPromptHub + from wolfharness_prompts.langfuse_hub import LangfusePromptHub + from wolfharness_prompts.promptlayer_provider import PromptLayerProvider class BasePromptHubConfig(Schema): @@ -37,7 +37,7 @@ class PromptLayerConfig(BasePromptHubConfig): """API key for the PromptLayer API.""" def get_provider(self) -> PromptLayerProvider: - from agentpool_prompts.promptlayer_provider import PromptLayerProvider + from wolfharness_prompts.promptlayer_provider import PromptLayerProvider return PromptLayerProvider(self) @@ -83,7 +83,7 @@ class LangfuseConfig(BasePromptHubConfig): """Timeout for fetching responses in seconds.""" def get_provider(self) -> LangfusePromptHub: - from agentpool_prompts.langfuse_hub import LangfusePromptHub + from wolfharness_prompts.langfuse_hub import LangfusePromptHub return LangfusePromptHub(self) @@ -107,7 +107,7 @@ class BraintrustConfig(BasePromptHubConfig): """Braintrust Project name.""" def get_provider(self) -> BraintrustPromptHub: - from agentpool_prompts.braintrust_hub import BraintrustPromptHub + from wolfharness_prompts.braintrust_hub import BraintrustPromptHub return BraintrustPromptHub(self) @@ -121,7 +121,7 @@ class FabricConfig(BasePromptHubConfig): """Configuration for Fabric GitHub prompt provider.""" def get_provider(self) -> FabricPromptHub: - from agentpool_prompts.fabric import FabricPromptHub + from wolfharness_prompts.fabric import FabricPromptHub return FabricPromptHub(self) diff --git a/src/agentpool_config/prompts.py b/src/wolfharness_config/prompts.py similarity index 99% rename from src/agentpool_config/prompts.py rename to src/wolfharness_config/prompts.py index a69966b74..39e3c8321 100644 --- a/src/agentpool_config/prompts.py +++ b/src/wolfharness_config/prompts.py @@ -10,7 +10,7 @@ from schemez import Schema from upathtools import UPath -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) diff --git a/src/agentpool_config/py.typed b/src/wolfharness_config/py.typed similarity index 100% rename from src/agentpool_config/py.typed rename to src/wolfharness_config/py.typed diff --git a/src/agentpool_config/resolution.py b/src/wolfharness_config/resolution.py similarity index 97% rename from src/agentpool_config/resolution.py rename to src/wolfharness_config/resolution.py index 15f7accfa..899343d88 100644 --- a/src/agentpool_config/resolution.py +++ b/src/wolfharness_config/resolution.py @@ -4,9 +4,9 @@ supporting multiple config layers that are deep-merged together. Precedence order (later overrides earlier): -1. Global config - ~/.config/agentpool/agentpool.yml - user preferences +1. Global config - ~/.config/wolfharness/wolfharness.yml - user preferences 2. Custom config - AGENTPOOL_CONFIG env var path -3. Project config - agentpool.yml in project/git root +3. Project config - wolfharness.yml in project/git root 4. Explicit config - CLI argument (highest precedence) 5. Fallback config - Only used if no agents defined in any other layer @@ -69,10 +69,10 @@ def get_layer(self, source: ConfigSource) -> ConfigLayer | None: # Standard config file names to search for CONFIG_FILE_NAMES: tuple[str, ...] = ( - "agentpool.yml", - "agentpool.yaml", - "agentpool.json", - "agentpool.jsonc", + "wolfharness.yml", + "wolfharness.yaml", + "wolfharness.json", + "wolfharness.jsonc", ) # Environment variable names @@ -87,9 +87,9 @@ def get_global_config_dir() -> Path: """Get the global configuration directory. Returns: - Path to ~/.config/agentpool (or platform equivalent) + Path to ~/.config/wolfharness (or platform equivalent) """ - return Path(platformdirs.user_config_dir("agentpool", appauthor=False)) + return Path(platformdirs.user_config_dir("wolfharness", appauthor=False)) def get_global_config_path() -> Path | None: @@ -115,7 +115,7 @@ def get_global_config_path() -> Path | None: def find_project_config(start_dir: JoinablePathLike | None = None) -> Path | None: """Find project config by traversing up to git root or filesystem root. - Searches for agentpool.yml/yaml/json in the current directory and parent + Searches for wolfharness.yml/yaml/json in the current directory and parent directories up to the nearest git repository root or filesystem root. Args: diff --git a/src/agentpool_config/session.py b/src/wolfharness_config/session.py similarity index 98% rename from src/agentpool_config/session.py rename to src/wolfharness_config/session.py index 83d08fcde..db8ca245d 100644 --- a/src/agentpool_config/session.py +++ b/src/wolfharness_config/session.py @@ -169,8 +169,8 @@ class SessionQuery(Schema): def get_time_cutoff(self) -> datetime | None: """Get datetime from time period string.""" - from agentpool.utils.parse_time import parse_time_period - from agentpool.utils.time_utils import get_now + from wolfharness.utils.parse_time import parse_time_period + from wolfharness.utils.time_utils import get_now if not self.since: return None diff --git a/src/agentpool_config/session_pool.py b/src/wolfharness_config/session_pool.py similarity index 97% rename from src/agentpool_config/session_pool.py rename to src/wolfharness_config/session_pool.py index 40203b1d3..8560ecfae 100644 --- a/src/agentpool_config/session_pool.py +++ b/src/wolfharness_config/session_pool.py @@ -5,7 +5,7 @@ from pydantic import ConfigDict, Field from schemez import Schema -from agentpool_config.durable import CheckpointConfig +from wolfharness_config.durable import CheckpointConfig class SessionPoolConfig(Schema): diff --git a/src/agentpool_config/skill_commands.py b/src/wolfharness_config/skill_commands.py similarity index 100% rename from src/agentpool_config/skill_commands.py rename to src/wolfharness_config/skill_commands.py diff --git a/src/agentpool_config/skills.py b/src/wolfharness_config/skills.py similarity index 98% rename from src/agentpool_config/skills.py rename to src/wolfharness_config/skills.py index b532c3d21..c40a52933 100644 --- a/src/agentpool_config/skills.py +++ b/src/wolfharness_config/skills.py @@ -9,11 +9,11 @@ from schemez import Schema from upathtools import UPath -from agentpool_config.paths import ConfigPath +from wolfharness_config.paths import ConfigPath if TYPE_CHECKING: - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness_config.mcp_server import MCPServerConfig DEFAULT_SKILLS_PATHS = [ @@ -250,7 +250,7 @@ def to_mcp_server_config(self, name: str) -> MCPServerConfig: Raises: ValueError: If neither ``command`` nor ``url`` is specified. """ - from agentpool_config.mcp_server import ( + from wolfharness_config.mcp_server import ( StdioMCPServerConfig, StreamableHTTPMCPServerConfig, ) diff --git a/src/agentpool_config/storage.py b/src/wolfharness_config/storage.py similarity index 94% rename from src/agentpool_config/storage.py rename to src/wolfharness_config/storage.py index 9da0ed9d6..bb10c8af4 100644 --- a/src/agentpool_config/storage.py +++ b/src/wolfharness_config/storage.py @@ -16,13 +16,13 @@ if TYPE_CHECKING: from sqlalchemy.ext.asyncio import AsyncEngine - from agentpool_storage.base import StorageProvider + from wolfharness_storage.base import StorageProvider FilterMode = Literal["and", "override"] -APP_NAME: Final = "agentpool" -APP_AUTHOR: Final = "agentpool" +APP_NAME: Final = "wolfharness" +APP_AUTHOR: Final = "wolfharness" DATA_DIR: Final = Path(user_data_dir(APP_NAME, APP_AUTHOR)) DEFAULT_DB_NAME: Final = "history.db" DEFAULT_TITLE_PROMPT: Final = """\ @@ -141,7 +141,7 @@ def get_engine(self) -> AsyncEngine: def get_provider(self) -> StorageProvider: """Create a SQL storage provider instance.""" - from agentpool_storage.sql_provider import SQLModelProvider + from wolfharness_storage.sql_provider import SQLModelProvider return SQLModelProvider(self) @@ -165,7 +165,7 @@ class FileStorageConfig(BaseStorageProviderConfig): def get_provider(self) -> StorageProvider: """Create a file storage provider instance.""" - from agentpool_storage.file_provider import FileProvider + from wolfharness_storage.file_provider import FileProvider return FileProvider(self) @@ -180,7 +180,7 @@ class MemoryStorageConfig(BaseStorageProviderConfig): def get_provider(self) -> StorageProvider: """Create a memory storage provider instance.""" - from agentpool_storage.memory_provider import MemoryStorageProvider + from wolfharness_storage.memory_provider import MemoryStorageProvider return MemoryStorageProvider(self) @@ -206,7 +206,7 @@ class OpenCodeStorageConfig(BaseStorageProviderConfig): def get_provider(self) -> StorageProvider: """Create an OpenCode SQLite storage provider instance.""" - from agentpool_storage.opencode_provider import OpenCodeStorageProvider + from wolfharness_storage.opencode_provider import OpenCodeStorageProvider return OpenCodeStorageProvider(self) @@ -234,7 +234,7 @@ class ZedStorageConfig(BaseStorageProviderConfig): def get_provider(self) -> StorageProvider: """Create a Zed storage provider instance.""" - from agentpool_storage.zed_provider import ZedStorageProvider + from wolfharness_storage.zed_provider import ZedStorageProvider return ZedStorageProvider(self) @@ -253,7 +253,7 @@ class ACPStorageConfig(BaseStorageProviderConfig): def get_provider(self) -> StorageProvider: """Create an ACP storage provider instance.""" - from agentpool_storage.acp_provider import ACPStorageProvider + from wolfharness_storage.acp_provider import ACPStorageProvider return ACPStorageProvider(self) @@ -272,7 +272,7 @@ def get_provider(self) -> StorageProvider: class StorageConfig(Schema): """Global storage configuration. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/storage_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/storage_configuration/ """ providers: list[StorageProviderConfig] | None = Field( diff --git a/src/agentpool_config/system_prompts.py b/src/wolfharness_config/system_prompts.py similarity index 97% rename from src/agentpool_config/system_prompts.py rename to src/wolfharness_config/system_prompts.py index a320cd594..5e888a854 100644 --- a/src/agentpool_config/system_prompts.py +++ b/src/wolfharness_config/system_prompts.py @@ -8,8 +8,8 @@ from pydantic import ConfigDict, Field, ImportString from schemez import Schema -from agentpool_config.paths import ConfigPath -from agentpool_config.prompt_hubs import PromptHubConfig +from wolfharness_config.paths import ConfigPath +from wolfharness_config.prompt_hubs import PromptHubConfig SystemPromptCategory = Literal["role", "methodology", "quality", "task"] @@ -167,7 +167,7 @@ class PackagePromptConfig(BaseSystemPrompt): class PromptLibraryConfig(Schema): """Complete prompt configuration. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/prompt_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/prompt_configuration/ """ system_prompts: dict[str, StaticPromptConfig] = Field( diff --git a/src/agentpool_config/task.py b/src/wolfharness_config/task.py similarity index 89% rename from src/agentpool_config/task.py rename to src/wolfharness_config/task.py index 604101662..b6af4304b 100644 --- a/src/agentpool_config/task.py +++ b/src/wolfharness_config/task.py @@ -9,14 +9,14 @@ from pydantic import ConfigDict, Field, ImportString from schemez import Schema -from agentpool.tools.base import Tool -from agentpool_config.knowledge import Knowledge -from agentpool_config.tools import ImportToolConfig +from wolfharness.tools.base import Tool +from wolfharness_config.knowledge import Knowledge +from wolfharness_config.tools import ImportToolConfig if TYPE_CHECKING: - from agentpool.agents import Agent - from agentpool.prompts.prompts import BasePrompt # noqa: TC004 + from wolfharness.agents import Agent + from wolfharness.prompts.prompts import BasePrompt # noqa: TC004 class Job[TDeps, TResult = str](Schema): @@ -50,7 +50,7 @@ class Job[TDeps, TResult = str](Schema): """Human-readable description of what this task does""" prompt: str | ImportString[str] | BasePrompt = Field( - examples=["Analyze the following code", "agentpool.prompts.prompts:BasePrompt"], + examples=["Analyze the following code", "wolfharness.prompts.prompts:BasePrompt"], title="Task prompt", ) """The task instruction/prompt.""" @@ -66,7 +66,7 @@ class Job[TDeps, TResult = str](Schema): required_dependency: ImportString[type[TDeps]] | None = Field( default=None, validate_default=True, - examples=["agentpool.agents.context:AgentContext"], + examples=["wolfharness.agents.context:AgentContext"], title="Required dependencies", ) """Dependencies or context data needed for task execution""" @@ -113,7 +113,7 @@ async def can_be_executed_by(self, agent: Agent[Any, Any]) -> bool: # Check vision capabilities if self.requires_vision: - from agentpool.utils.model_capabilities import supports_vision + from wolfharness.utils.model_capabilities import supports_vision if not await supports_vision(agent.model_name): return False @@ -129,7 +129,7 @@ def tool_configs(self) -> list[ImportToolConfig]: ] async def get_prompt(self) -> str: - from agentpool.prompts.prompts import BasePrompt + from wolfharness.prompts.prompts import BasePrompt if isinstance(self.prompt, BasePrompt): messages = await self.prompt.format() diff --git a/src/agentpool_config/team_mode.py b/src/wolfharness_config/team_mode.py similarity index 100% rename from src/agentpool_config/team_mode.py rename to src/wolfharness_config/team_mode.py diff --git a/src/agentpool_config/teams.py b/src/wolfharness_config/teams.py similarity index 95% rename from src/agentpool_config/teams.py rename to src/wolfharness_config/teams.py index e7830b375..5ae61a578 100644 --- a/src/agentpool_config/teams.py +++ b/src/wolfharness_config/teams.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, ConfigDict, Field -from agentpool_config.nodes import NodeConfig +from wolfharness_config.nodes import NodeConfig ExecutionMode = Literal["parallel", "sequential"] @@ -41,7 +41,7 @@ class TeamConfig(NodeConfig): Teams can be either parallel execution groups or sequential chains. They can contain both agents and other teams as members. - Docs: https://phil65.github.io/agentpool/YAML%20Configuration/team_configuration/ + Docs: https://phil65.github.io/wolfharness/YAML%20Configuration/team_configuration/ """ model_config = ConfigDict( diff --git a/src/agentpool_config/tools.py b/src/wolfharness_config/tools.py similarity index 97% rename from src/agentpool_config/tools.py rename to src/wolfharness_config/tools.py index 44be596a7..a69b810ab 100644 --- a/src/agentpool_config/tools.py +++ b/src/wolfharness_config/tools.py @@ -9,11 +9,11 @@ from pydantic import ConfigDict, Field, ImportString, field_validator from schemez import Schema -from agentpool.utils.parse_time import parse_time_period +from wolfharness.utils.parse_time import parse_time_period if TYPE_CHECKING: - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool class ToolHints(Schema): @@ -156,7 +156,7 @@ class ImportToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Import and create tool from configuration.""" - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool # Load prepare callable from import string if provided prepare_callable = None diff --git a/src/agentpool_config/toolsets.py b/src/wolfharness_config/toolsets.py similarity index 91% rename from src/agentpool_config/toolsets.py rename to src/wolfharness_config/toolsets.py index 450443f75..4b545a054 100644 --- a/src/agentpool_config/toolsets.py +++ b/src/wolfharness_config/toolsets.py @@ -20,10 +20,10 @@ from upathtools_config import FilesystemConfigType from upathtools_config.base import FileSystemConfig -from agentpool.models.model_configs import AnyModelConfig -from agentpool_config.converters import ConversionConfig -from agentpool_config.tools import ImportToolConfig -from agentpool_config.workers import AgentWorkerConfig, WorkerConfig +from wolfharness.models.model_configs import AnyModelConfig +from wolfharness_config.converters import ConversionConfig +from wolfharness_config.tools import ImportToolConfig +from wolfharness_config.workers import AgentWorkerConfig, WorkerConfig if TYPE_CHECKING: @@ -32,7 +32,7 @@ from pydantic_ai.capabilities import AbstractCapability from pydantic_ai.tools import RunContext, ToolDefinition - from agentpool_toolsets.search_toolset import SearchTools + from wolfharness_toolsets.search_toolset import SearchTools def _make_tool_filter( @@ -116,7 +116,7 @@ class OpenAPIToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create OpenAPI tools provider from this config.""" - from agentpool_toolsets.openapi import OpenAPITools + from wolfharness_toolsets.openapi import OpenAPITools base_url = str(self.base_url) if self.base_url else "" return OpenAPITools(spec=self.spec, base_url=base_url) @@ -143,7 +143,7 @@ class EntryPointToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create provider from this config.""" - from agentpool_toolsets.entry_points import EntryPointTools + from wolfharness_toolsets.entry_points import EntryPointTools return EntryPointTools(module=self.module) @@ -180,7 +180,7 @@ class ComposioToolSetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create provider from this config.""" - from agentpool_toolsets.composio_toolset import ComposioTools + from wolfharness_toolsets.composio_toolset import ComposioTools key = self.api_key.get_secret_value() if self.api_key else os.getenv("COMPOSIO_API_KEY") return ComposioTools(user_id=self.user_id, toolsets=self.toolsets, api_key=key) @@ -207,13 +207,13 @@ class SubagentToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create subagent tools provider.""" - from agentpool_toolsets.builtin.subagent_tools import SubagentTools + from wolfharness_toolsets.builtin.subagent_tools import SubagentTools provider = SubagentTools( name="subagent_tools", ) if self.tools is not None: - from agentpool.capabilities.filtered_toolset import FilteredToolsetCapability + from wolfharness.capabilities.filtered_toolset import FilteredToolsetCapability return FilteredToolsetCapability( provider, _make_tool_filter(cast(dict[str, bool], self.tools)) @@ -253,7 +253,7 @@ def get_workers(self) -> list[WorkerConfig]: def get_provider(self) -> AbstractCapability: """Create workers tools provider.""" - from agentpool_toolsets.builtin.workers import WorkersTools + from wolfharness_toolsets.builtin.workers import WorkersTools return WorkersTools(workers=self.get_workers(), name="workers") @@ -285,12 +285,12 @@ class ProcessManagementToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create Process management tools provider.""" - from agentpool_toolsets.builtin import ProcessManagementTools + from wolfharness_toolsets.builtin import ProcessManagementTools env = self.environment.get_provider() if self.environment else None provider = ProcessManagementTools(env=env, name="process_management") if self.tools is not None: - from agentpool.capabilities.filtered_toolset import FilteredToolsetCapability + from wolfharness.capabilities.filtered_toolset import FilteredToolsetCapability return FilteredToolsetCapability( provider, _make_tool_filter(cast(dict[str, bool], self.tools)) @@ -302,7 +302,7 @@ class SkillsToolsetConfig(BaseToolsetConfig): """Configuration for the skills toolset (deprecated). Skills are now auto-provided by - :class:`~agentpool.capabilities.skill_manager_cap.SkillManagerCap`, which + :class:`~wolfharness.capabilities.skill_manager_cap.SkillManagerCap`, which the pool registers at every agent. An explicit ``type: skills`` toolset is therefore a no-op: ``get_provider()`` returns an empty capability so config-driven setups keep working without error, and the @@ -341,7 +341,7 @@ class SkillsToolsetConfig(BaseToolsetConfig): """(Unused) Maximum number of skills to inject. Skill injection is controlled by - :class:`~agentpool_config.skills.SkillsInstructionConfig.inject` and + :class:`~wolfharness_config.skills.SkillsInstructionConfig.inject` and ``max_skills``; this per-toolset override has no effect since ``SkillManagerCap`` owns injection. """ @@ -363,11 +363,11 @@ def get_provider(self) -> AbstractCapability: DeprecationWarning, stacklevel=2, ) - from agentpool.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability provider = FunctionToolsetCapability(name="skills") if self.tools is not None: - from agentpool.capabilities.filtered_toolset import FilteredToolsetCapability + from wolfharness.capabilities.filtered_toolset import FilteredToolsetCapability return FilteredToolsetCapability( provider, _make_tool_filter(cast(dict[str, bool], self.tools)) @@ -402,12 +402,12 @@ class CodeToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create code tools provider.""" - from agentpool_toolsets.builtin.code import CodeTools + from wolfharness_toolsets.builtin.code import CodeTools env = self.environment.get_provider() if self.environment else None provider = CodeTools(env=env, name="code") if self.tools is not None: - from agentpool.capabilities.filtered_toolset import FilteredToolsetCapability + from wolfharness.capabilities.filtered_toolset import FilteredToolsetCapability return FilteredToolsetCapability( provider, _make_tool_filter(cast(dict[str, bool], self.tools)) @@ -540,8 +540,8 @@ class FSSpecToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create FSSpec filesystem tools provider.""" - from agentpool.prompts.conversion_manager import ConversionManager - from agentpool_toolsets.fsspec_toolset import FSSpecTools + from wolfharness.prompts.conversion_manager import ConversionManager + from wolfharness_toolsets.fsspec_toolset import FSSpecTools model = ( self.model @@ -598,7 +598,7 @@ class VFSToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create VFS registry filesystem tools provider.""" - from agentpool_toolsets.vfs_toolset import VFSTools + from wolfharness_toolsets.vfs_toolset import VFSTools return VFSTools(name="vfs") @@ -630,7 +630,7 @@ def get_provider(self) -> SearchTools: """Create search tools provider.""" from searchly import BaseSearchProviderConfig, NewsSearchProvider, WebSearchProvider - from agentpool_toolsets.search_toolset import SearchTools + from wolfharness_toolsets.search_toolset import SearchTools match self.web_search: case str(): @@ -681,7 +681,7 @@ class NotificationsToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create notifications tools provider.""" - from agentpool_toolsets.notifications import NotificationsTools + from wolfharness_toolsets.notifications import NotificationsTools return NotificationsTools(channels=self.channels) @@ -716,7 +716,7 @@ def get_provider(self) -> AbstractCapability: """Create custom provider from import path.""" from pydantic_ai.capabilities import AbstractCapability - from agentpool.utils.importing import import_class + from wolfharness.utils.importing import import_class provider_cls = import_class(self.import_path) if not issubclass(provider_cls, AbstractCapability): @@ -755,7 +755,7 @@ class AggregatingToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create aggregating provider.""" - from agentpool.capabilities.combined_toolset import CombinedToolsetCapability + from wolfharness.capabilities.combined_toolset import CombinedToolsetCapability capabilities = [p.get_provider() for p in self.toolsets] return CombinedToolsetCapability(capabilities=capabilities, name="aggregating") @@ -785,7 +785,7 @@ def get_provider(self) -> AbstractCapability: NOTE: This now delegates to CombinedToolsetCapability. """ - from agentpool.capabilities.combined_toolset import CombinedToolsetCapability + from wolfharness.capabilities.combined_toolset import CombinedToolsetCapability capabilities = [p.get_provider() for p in self.toolsets] return CombinedToolsetCapability(capabilities=capabilities, name="codemode") @@ -812,7 +812,7 @@ class RemoteCodeModeToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create Codemode toolset.""" - from agentpool.capabilities.combined_toolset import CombinedToolsetCapability + from wolfharness.capabilities.combined_toolset import CombinedToolsetCapability capabilities = [p.get_provider() for p in self.toolsets] return CombinedToolsetCapability(capabilities=capabilities, name="remote_codemode") @@ -847,7 +847,7 @@ class ImportToolsToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create static provider with imported tools.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability tools = [tool_config.get_tool() for tool_config in self.tools] name = self.namespace or "import_tools" @@ -878,7 +878,7 @@ class ConfigCreationToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create config creation toolset.""" - from agentpool_toolsets.config_creation import ConfigCreationTools + from wolfharness_toolsets.config_creation import ConfigCreationTools name = self.namespace or "config_creation" return ConfigCreationTools(schema_path=self.schema_path, markup=self.markup, name=name) @@ -923,11 +923,11 @@ def get_provider(self) -> AbstractCapability: # TODO: Migrate PlanProvider to capability-native architecture. # The old PlanProvider was removed during M3 migration; plan tools # need to be reimplemented as a FunctionToolsetCapability subclass. - from agentpool.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability provider = FunctionToolsetCapability(name="plan") if self.tools is not None: - from agentpool.capabilities.filtered_toolset import FilteredToolsetCapability + from wolfharness.capabilities.filtered_toolset import FilteredToolsetCapability return FilteredToolsetCapability( provider, _make_tool_filter(cast(dict[str, bool], self.tools)) @@ -957,7 +957,7 @@ class DebugToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create debug tools provider.""" - from agentpool_toolsets.builtin.debug import DebugTools + from wolfharness_toolsets.builtin.debug import DebugTools return DebugTools(name=self.namespace or "debug") @@ -968,7 +968,7 @@ class MCPDiscoveryToolsetConfig(BaseToolsetConfig): Enables dynamic discovery and use of MCP servers without preloading tools. Uses semantic search over 1000+ indexed servers for intelligent matching. - Requires the `mcp-discovery` extra: `pip install agentpool[mcp-discovery]` + Requires the `mcp-discovery` extra: `pip install wolfharness[mcp-discovery]` """ model_config = ConfigDict( @@ -1001,7 +1001,7 @@ class MCPDiscoveryToolsetConfig(BaseToolsetConfig): def get_provider(self) -> AbstractCapability: """Create MCP discovery tools provider.""" - from agentpool_toolsets.mcp_discovery.toolset import MCPDiscoveryToolset + from wolfharness_toolsets.mcp_discovery.toolset import MCPDiscoveryToolset return MCPDiscoveryToolset( name=self.namespace or "mcp_discovery", @@ -1017,7 +1017,7 @@ class CronToolsetConfig(BaseToolsetConfig): Gives agents the ability to schedule, list, and remove recurring or one-shot jobs at runtime. Jobs are persisted to a JSON file. - Requires the ``bot`` extra: ``pip install agentpool[bot]`` + Requires the ``bot`` extra: ``pip install wolfharness[bot]`` """ model_config = ConfigDict( @@ -1031,9 +1031,9 @@ class CronToolsetConfig(BaseToolsetConfig): """Cron scheduling toolset.""" store_path: str = Field( - default="~/.agentpool/cron_jobs.json", + default="~/.wolfharness/cron_jobs.json", title="Job store path", - examples=["~/.agentpool/cron_jobs.json", "/tmp/cron.json"], + examples=["~/.wolfharness/cron_jobs.json", "/tmp/cron.json"], ) """Path to the JSON file where scheduled jobs are persisted.""" @@ -1041,8 +1041,8 @@ def get_provider(self) -> AbstractCapability: """Create cron tools provider.""" from pathlib import Path - from agentpool_bot.cron.service import CronService - from agentpool_toolsets.cron import CronTools + from wolfharness_bot.cron.service import CronService + from wolfharness_toolsets.cron import CronTools resolved = Path(self.store_path).expanduser() service = CronService(store_path=resolved) diff --git a/src/agentpool_config/agentpool_tools.py b/src/wolfharness_config/wolfharness_tools.py similarity index 93% rename from src/agentpool_config/agentpool_tools.py rename to src/wolfharness_config/wolfharness_tools.py index 3f713c2ef..575c770d5 100644 --- a/src/agentpool_config/agentpool_tools.py +++ b/src/wolfharness_config/wolfharness_tools.py @@ -1,4 +1,4 @@ -"""Models for agentpool standalone tool configuration.""" +"""Models for wolfharness standalone tool configuration.""" from __future__ import annotations @@ -8,12 +8,12 @@ from exxec_config import ExecutionEnvironmentConfig # noqa: TC002 from pydantic import ConfigDict, Field -from agentpool_config.converters import ConversionConfig # noqa: TC001 -from agentpool_config.tools import BaseToolConfig +from wolfharness_config.converters import ConversionConfig # noqa: TC001 +from wolfharness_config.tools import BaseToolConfig if TYPE_CHECKING: - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool class BashToolConfig(BaseToolConfig): @@ -59,7 +59,7 @@ class BashToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to BashTool instance.""" - from agentpool.tool_impls.bash import create_bash_tool + from wolfharness.tool_impls.bash import create_bash_tool env = self.environment.get_provider() if self.environment else None return create_bash_tool( @@ -89,7 +89,7 @@ class AgentCliToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to AgentCliTool instance.""" - from agentpool.tool_impls.agent_cli import create_agent_cli_tool + from wolfharness.tool_impls.agent_cli import create_agent_cli_tool return create_agent_cli_tool( name=self.name or "run_agent_cli_command", @@ -131,7 +131,7 @@ def get_tool(self) -> Tool: DeprecationWarning, stacklevel=2, ) - from agentpool.tool_impls.question import create_question_tool + from wolfharness.tool_impls.question import create_question_tool return create_question_tool( name=self.name or "question", @@ -167,7 +167,7 @@ class ExecuteCodeToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to ExecuteCodeTool instance.""" - from agentpool.tool_impls.execute_code import create_execute_code_tool + from wolfharness.tool_impls.execute_code import create_execute_code_tool env = self.environment.get_provider() if self.environment else None return create_execute_code_tool( @@ -254,7 +254,7 @@ class ReadToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to ReadTool instance.""" - from agentpool.tool_impls.read import create_read_tool + from wolfharness.tool_impls.read import create_read_tool env = self.environment.get_provider() if self.environment else None @@ -262,7 +262,7 @@ def get_tool(self) -> Tool: converter = None if self.conversion is not None: try: - from agentpool.prompts.conversion_manager import ConversionManager + from wolfharness.prompts.conversion_manager import ConversionManager converter = ConversionManager(self.conversion) except Exception: # noqa: BLE001 @@ -323,7 +323,7 @@ class ListDirectoryToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to ListDirectoryTool instance.""" - from agentpool.tool_impls.list_directory import create_list_directory_tool + from wolfharness.tool_impls.list_directory import create_list_directory_tool env = self.environment.get_provider() if self.environment else None return create_list_directory_tool( @@ -382,7 +382,7 @@ class GrepToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to GrepTool instance.""" - from agentpool.tool_impls.grep import create_grep_tool + from wolfharness.tool_impls.grep import create_grep_tool env = self.environment.get_provider() if self.environment else None return create_grep_tool( @@ -428,7 +428,7 @@ class DeletePathToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to DeletePathTool instance.""" - from agentpool.tool_impls.delete_path import create_delete_path_tool + from wolfharness.tool_impls.delete_path import create_delete_path_tool env = self.environment.get_provider() if self.environment else None return create_delete_path_tool( @@ -487,7 +487,7 @@ class DownloadFileToolConfig(BaseToolConfig): def get_tool(self) -> Tool: """Convert config to DownloadFileTool instance.""" - from agentpool.tool_impls.download_file import create_download_file_tool + from wolfharness.tool_impls.download_file import create_download_file_tool env = self.environment.get_provider() if self.environment else None return create_download_file_tool( @@ -501,7 +501,7 @@ def get_tool(self) -> Tool: ) -# Union type for agentpool tool configs +# Union type for wolfharness tool configs AgentpoolToolConfig = ( AgentCliToolConfig | QuestionToolConfig diff --git a/src/agentpool_config/workers.py b/src/wolfharness_config/workers.py similarity index 100% rename from src/agentpool_config/workers.py rename to src/wolfharness_config/workers.py diff --git a/src/agentpool_prompts/__init__.py b/src/wolfharness_prompts/__init__.py similarity index 100% rename from src/agentpool_prompts/__init__.py rename to src/wolfharness_prompts/__init__.py diff --git a/src/agentpool_prompts/braintrust_hub.py b/src/wolfharness_prompts/braintrust_hub.py similarity index 97% rename from src/agentpool_prompts/braintrust_hub.py rename to src/wolfharness_prompts/braintrust_hub.py index a85cd22c4..d45656378 100644 --- a/src/agentpool_prompts/braintrust_hub.py +++ b/src/wolfharness_prompts/braintrust_hub.py @@ -11,8 +11,8 @@ from braintrust import init_logger, load_prompt import httpx -from agentpool.prompts.base import BasePromptProvider -from agentpool_config.prompt_hubs import BraintrustConfig +from wolfharness.prompts.base import BasePromptProvider +from wolfharness_config.prompt_hubs import BraintrustConfig @dataclass @@ -214,7 +214,7 @@ def get_prompts_by_model(self, model: str) -> list[str]: if __name__ == "__main__": import anyio - from agentpool_config.prompt_hubs import BraintrustConfig + from wolfharness_config.prompt_hubs import BraintrustConfig config = BraintrustConfig(project="test") prompt_hub = BraintrustPromptHub(config) diff --git a/src/agentpool_prompts/fabric.py b/src/wolfharness_prompts/fabric.py similarity index 93% rename from src/agentpool_prompts/fabric.py rename to src/wolfharness_prompts/fabric.py index ab6764e5c..66310ca20 100644 --- a/src/agentpool_prompts/fabric.py +++ b/src/wolfharness_prompts/fabric.py @@ -6,11 +6,11 @@ import httpx -from agentpool.prompts.base import BasePromptProvider +from wolfharness.prompts.base import BasePromptProvider if TYPE_CHECKING: - from agentpool_config.prompt_hubs import FabricConfig + from wolfharness_config.prompt_hubs import FabricConfig class FabricPromptHub(BasePromptProvider): @@ -58,7 +58,7 @@ async def list_prompts(self) -> list[str]: if __name__ == "__main__": import anyio - from agentpool_config.prompt_hubs import FabricConfig + from wolfharness_config.prompt_hubs import FabricConfig async def main() -> None: hub = FabricPromptHub(FabricConfig()) diff --git a/src/agentpool_prompts/langfuse_hub.py b/src/wolfharness_prompts/langfuse_hub.py similarity index 92% rename from src/agentpool_prompts/langfuse_hub.py rename to src/wolfharness_prompts/langfuse_hub.py index 076afa740..26d87d689 100644 --- a/src/agentpool_prompts/langfuse_hub.py +++ b/src/wolfharness_prompts/langfuse_hub.py @@ -6,11 +6,11 @@ from langfuse import Langfuse # pyright: ignore -from agentpool.prompts.base import BasePromptProvider +from wolfharness.prompts.base import BasePromptProvider if TYPE_CHECKING: - from agentpool_config.prompt_hubs import LangfuseConfig + from wolfharness_config.prompt_hubs import LangfuseConfig class LangfusePromptHub(BasePromptProvider): @@ -68,7 +68,7 @@ async def list_prompts(self) -> list[str]: import anyio from pydantic import SecretStr - from agentpool_config.prompt_hubs import LangfuseConfig + from wolfharness_config.prompt_hubs import LangfuseConfig config = LangfuseConfig(secret_key=SecretStr("test"), public_key=SecretStr("")) prompt_hub = LangfusePromptHub(config) diff --git a/src/agentpool_prompts/promptlayer_provider.py b/src/wolfharness_prompts/promptlayer_provider.py similarity index 89% rename from src/agentpool_prompts/promptlayer_provider.py rename to src/wolfharness_prompts/promptlayer_provider.py index a03c1d16b..16de76b56 100644 --- a/src/agentpool_prompts/promptlayer_provider.py +++ b/src/wolfharness_prompts/promptlayer_provider.py @@ -6,11 +6,11 @@ from promptlayer import PromptLayer -from agentpool.prompts.base import BasePromptProvider +from wolfharness.prompts.base import BasePromptProvider if TYPE_CHECKING: - from agentpool_config.prompt_hubs import PromptLayerConfig + from wolfharness_config.prompt_hubs import PromptLayerConfig class PromptLayerProvider(BasePromptProvider): @@ -48,7 +48,7 @@ async def list_prompts(self) -> list[str]: import anyio from pydantic import SecretStr - from agentpool_config.prompt_hubs import PromptLayerConfig + from wolfharness_config.prompt_hubs import PromptLayerConfig async def main() -> None: config = PromptLayerConfig(api_key=SecretStr("pl_480ead79b098fc25c63cdb4c95115deb")) diff --git a/src/agentpool_prompts/py.typed b/src/wolfharness_prompts/py.typed similarity index 100% rename from src/agentpool_prompts/py.typed rename to src/wolfharness_prompts/py.typed diff --git a/src/agentpool_server/AGENTS.md b/src/wolfharness_server/AGENTS.md similarity index 97% rename from src/agentpool_server/AGENTS.md rename to src/wolfharness_server/AGENTS.md index b219ed181..a398f055e 100644 --- a/src/agentpool_server/AGENTS.md +++ b/src/wolfharness_server/AGENTS.md @@ -1,4 +1,4 @@ -# agentpool_server — Protocol Servers +# wolfharness_server — Protocol Servers ## Overview @@ -56,7 +56,7 @@ - **OpenCode `session_pool_integration.py`** is 1442 lines — the largest file. It's the central bridge between OpenCode HTTP routes and SessionPool orchestration. Touch with caution. - **OpenCode `.rules`** file in the `opencode_server/` directory documents SDK integration conventions, logging setup, and endpoint status. It's a reference doc, not a linter config. - **ACP `acp_agent.py`** (1111 lines) implements all ACP protocol methods (`sessions/*`, `providers/*`, `tools/*`, etc.) by delegating to the SessionPool and EventBus. It's the ACP entry point. -- **ACP `session.py`** (905 lines) manages `ACPSession` state including MCP server registration, provider config, mode toggles, and the `CommandStore` for slash commands. It bridges to `agentpool_commands.base.NodeCommand`. +- **ACP `session.py`** (905 lines) manages `ACPSession` state including MCP server registration, provider config, mode toggles, and the `CommandStore` for slash commands. It bridges to `wolfharness_commands.base.NodeCommand`. - **ACP `event_converter.py`** (755 lines) is pure conversion logic (no I/O) and is fully testable without mocks. It tracks tool call state internally. - **OpenCode `event_processor.py`** (798 lines) translates `RichAgentStreamEvent` → OpenCode SSE events with recursive subagent handling. Each level gets its own `EventProcessorContext`. - **ACP `provider_router.py`** derives provider metadata from `model_variants` in the manifest, not from runtime model discovery. This is a static snapshot at server start. @@ -72,7 +72,7 @@ ### UserMessageInsertedEvent — ACP Server -The `UserMessageInsertedEvent` (defined in `agentpool.agents.events.events`) carries inserted user message content through the EventBus. For ACP, this is handled in two places: +The `UserMessageInsertedEvent` (defined in `wolfharness.agents.events.events`) carries inserted user message content through the EventBus. For ACP, this is handled in two places: **1. `_meta.delivery` extraction at `acp_agent.py:prompt()`** diff --git a/src/wolfharness_server/__init__.py b/src/wolfharness_server/__init__.py new file mode 100644 index 000000000..efd71d353 --- /dev/null +++ b/src/wolfharness_server/__init__.py @@ -0,0 +1,9 @@ +"""AgentPool Server implementations.""" + +from wolfharness_server.a2a_server import A2AServer +from wolfharness_server.aggregating_server import AggregatingServer +from wolfharness_server.agui_server import AGUIServer +from wolfharness_server.base import BaseServer +from wolfharness_server.http_server import HTTPServer + +__all__ = ["A2AServer", "AGUIServer", "AggregatingServer", "BaseServer", "HTTPServer"] diff --git a/src/agentpool_server/a2a_server/__init__.py b/src/wolfharness_server/a2a_server/__init__.py similarity index 100% rename from src/agentpool_server/a2a_server/__init__.py rename to src/wolfharness_server/a2a_server/__init__.py diff --git a/src/agentpool_server/a2a_server/a2a_types.py b/src/wolfharness_server/a2a_server/a2a_types.py similarity index 100% rename from src/agentpool_server/a2a_server/a2a_types.py rename to src/wolfharness_server/a2a_server/a2a_types.py diff --git a/src/agentpool_server/a2a_server/agent_worker.py b/src/wolfharness_server/a2a_server/agent_worker.py similarity index 99% rename from src/agentpool_server/a2a_server/agent_worker.py rename to src/wolfharness_server/a2a_server/agent_worker.py index 4fcc93b76..650da18b6 100644 --- a/src/agentpool_server/a2a_server/agent_worker.py +++ b/src/wolfharness_server/a2a_server/agent_worker.py @@ -44,7 +44,7 @@ from starlette.routing import Route from starlette.types import ExceptionHandler, Lifespan - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent @asynccontextmanager diff --git a/src/agentpool_server/a2a_server/server.py b/src/wolfharness_server/a2a_server/server.py similarity index 94% rename from src/agentpool_server/a2a_server/server.py rename to src/wolfharness_server/a2a_server/server.py index 6d8451974..63c7cf9a5 100644 --- a/src/agentpool_server/a2a_server/server.py +++ b/src/wolfharness_server/a2a_server/server.py @@ -1,4 +1,4 @@ -"""A2A server implementation for agentpool pool. +"""A2A server implementation for wolfharness pool. This module provides a server that exposes all agents in an AgentPool via the A2A (Agent-to-Agent) protocol, with each agent accessible at its own route. @@ -9,8 +9,8 @@ from functools import partial from typing import TYPE_CHECKING -from agentpool.log import get_logger -from agentpool_server.http_server import HTTPServer +from wolfharness.log import get_logger +from wolfharness_server.http_server import HTTPServer if TYPE_CHECKING: @@ -18,7 +18,7 @@ from starlette.responses import Response from starlette.routing import Route - from agentpool import AgentPool + from wolfharness import AgentPool logger = get_logger(__name__) @@ -79,7 +79,7 @@ async def get_routes(self) -> list[Route]: from starlette.responses import JSONResponse, Response from starlette.routing import Route - from agentpool_server.a2a_server.agent_worker import AgentWorker, worker_lifespan + from wolfharness_server.a2a_server.agent_worker import AgentWorker, worker_lifespan routes: list[Route] = [] # Create route for each agent in the pool @@ -89,7 +89,7 @@ async def agent_handler(request: Request, agent_name: str = agent_name) -> Respo """Handle A2A requests for a specific agent.""" try: # Create a session via SessionPool (unified creation path) - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id sp = self.pool.session_pool agent = None @@ -138,7 +138,7 @@ async def list_agents(request: Request) -> Response: """List all available agents.""" from starlette.responses import JSONResponse - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig agent_list = [ { diff --git a/src/agentpool_server/a2a_server/storage.py b/src/wolfharness_server/a2a_server/storage.py similarity index 92% rename from src/agentpool_server/a2a_server/storage.py rename to src/wolfharness_server/a2a_server/storage.py index 7d21d5657..8d223c255 100644 --- a/src/agentpool_server/a2a_server/storage.py +++ b/src/wolfharness_server/a2a_server/storage.py @@ -5,12 +5,12 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any -from agentpool_server.a2a_server.a2a_types import TaskData +from wolfharness_server.a2a_server.a2a_types import TaskData if TYPE_CHECKING: - from agentpool.messaging.messages import ChatMessage - from agentpool_server.a2a_server.a2a_types import A2ARequest, TaskStatus + from wolfharness.messaging.messages import ChatMessage + from wolfharness_server.a2a_server.a2a_types import A2ARequest, TaskStatus @dataclass diff --git a/src/wolfharness_server/acp_server/__init__.py b/src/wolfharness_server/acp_server/__init__.py new file mode 100644 index 000000000..d9fe66b17 --- /dev/null +++ b/src/wolfharness_server/acp_server/__init__.py @@ -0,0 +1,24 @@ +"""ACP (Agent Client Protocol) integration for wolfharness.""" + +from __future__ import annotations + +from wolfharness_server.acp_server.handler import ACPProtocolHandler +from wolfharness_server.acp_server.server import ACPServer +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.session import ACPSession +from wolfharness_server.acp_server.session_manager import ACPSessionManager +from wolfharness_server.acp_server.converters import ( + convert_acp_mcp_server_to_config, + from_acp_content, +) + + +__all__ = [ + "ACPProtocolHandler", + "ACPServer", + "ACPSession", + "ACPSessionManager", + "AgentPoolACPAgent", + "convert_acp_mcp_server_to_config", + "from_acp_content", +] diff --git a/src/agentpool_server/acp_server/acp_agent.py b/src/wolfharness_server/acp_server/acp_agent.py similarity index 96% rename from src/agentpool_server/acp_server/acp_agent.py rename to src/wolfharness_server/acp_server/acp_agent.py index 65602f7c0..d211b0f24 100644 --- a/src/agentpool_server/acp_server/acp_agent.py +++ b/src/wolfharness_server/acp_server/acp_agent.py @@ -41,12 +41,12 @@ SetSessionModeRequest, SetSessionModeResponse, ) -from agentpool.log import get_logger -from agentpool.utils.task_group import ManagedTaskGroup -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager -from agentpool_server.acp_server.converters import to_session_config_option, to_session_info -from agentpool_server.acp_server.provider_router import ProviderRouter -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.log import get_logger +from wolfharness.utils.task_group import ManagedTaskGroup +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness_server.acp_server.converters import to_session_config_option, to_session_info +from wolfharness_server.acp_server.provider_router import ProviderRouter +from wolfharness_server.acp_server.session_manager import ACPSessionManager if TYPE_CHECKING: @@ -71,12 +71,12 @@ SetSessionModeRequest, ) from acp.schema.mcp import AcpMcpServer - from agentpool.agents.base_agent import BaseAgent - from agentpool.host.context import HostContext - from agentpool.models.agents import NativeAgentConfig - from agentpool.storage.manager import SessionMetadataGeneratedEvent - from agentpool_server.acp_server.handler import ACPProtocolHandler - from agentpool_server.acp_server.server import ACPServer + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.host.context import HostContext + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.storage.manager import SessionMetadataGeneratedEvent + from wolfharness_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.server import ACPServer logger = get_logger(__name__) @@ -97,7 +97,7 @@ async def get_session_model_state( Returns: SessionModelState with all available models, None if no models available """ - from agentpool_server.shared.model_utils import build_model_state_for_acp + from wolfharness_server.shared.model_utils import build_model_state_for_acp try: state = await build_model_state_for_acp(agent, provider_router) @@ -121,7 +121,7 @@ async def get_session_model_state( async def get_session_mode_state(agent: BaseAgent) -> SessionModeState | None: """Get SessionModeState from an agent using its get_modes() method. - Converts agentpool ModeCategory to ACP SessionModeState format. + Converts wolfharness ModeCategory to ACP SessionModeState format. Uses the first category that looks like permissions (not model). Args: @@ -274,7 +274,7 @@ def __post_init__(self) -> None: # RFC-0034: Initialize provider router with None manifest (will be updated in initialize) self.provider_router = ProviderRouter(None) # NEW: Cache agent config for per-session creation (RFC-0031) - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig ctx = self.host_context if ctx is not None and ctx.main_agent_name and ctx.main_agent_name in ctx.manifest.agents: @@ -288,8 +288,8 @@ def __post_init__(self) -> None: if self.host_context and ( self.host_context.manifest.acp and self.host_context.manifest.acp.use_session_pool ): - from agentpool_server.acp_server.event_converter import ACPEventConverter - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.handler import ACPProtocolHandler self._protocol_handler = ACPProtocolHandler( host_context=self.host_context, @@ -381,8 +381,8 @@ async def initialize(self, params: InitializeRequest) -> InitializeResponse: # True (optimistic) to preserve backward-compatible behavior. audio_prompts = True image_prompts = True - from agentpool.agents.native_agent.agent import Agent - from agentpool.models.model_configs import BaseModelConfig + from wolfharness.agents.native_agent.agent import Agent + from wolfharness.models.model_configs import BaseModelConfig # Derive modality support from the default agent's model config. # When the agent or its config is unavailable (e.g. test mocks), @@ -399,9 +399,9 @@ async def initialize(self, params: InitializeRequest) -> InitializeResponse: image_prompts = caps.image_input if caps.image_input is not None else True return InitializeResponse.create( protocol_version=version, - name="agentpool", + name="wolfharness", title="AgentPool", - version=_version("agentpool"), + version=_version("wolfharness"), load_session=True, list_sessions=True, resume_session=True, @@ -419,7 +419,7 @@ async def initialize(self, params: InitializeRequest) -> InitializeResponse: async def new_session(self, params: NewSessionRequest) -> NewSessionResponse: """Create a new session.""" - from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient + from wolfharness.agents.acp_agent import ACPAgent as ACPAgentClient if not self._initialized: raise RuntimeError("Agent not initialized") @@ -497,7 +497,7 @@ async def load_session(self, params: LoadSessionRequest) -> LoadSessionResponse: Delegates to the agent's load_session method, which populates agent.conversation. Then replays the conversation to the client via ACP notifications. """ - from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient + from wolfharness.agents.acp_agent import ACPAgent as ACPAgentClient if not self._initialized: raise RuntimeError("Agent not initialized") @@ -645,7 +645,7 @@ async def resume_session(self, params: ResumeSessionRequest) -> ResumeSessionRes UNSTABLE: This feature is not part of the spec yet. """ - from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient + from wolfharness.agents.acp_agent import ACPAgent as ACPAgentClient if not self._initialized: raise RuntimeError("Agent not initialized") @@ -940,7 +940,7 @@ async def set_session_mode( Calls set_mode directly on the agent with the mode_id, allowing the agent to handle mode-specific logic (e.g., acceptEdits auto-allowing edit tools). """ - from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient + from wolfharness.agents.acp_agent import ACPAgent as ACPAgentClient session = self.session_manager.get_session(params.session_id) if not session: @@ -996,7 +996,7 @@ async def set_session_model( # Also include resolved identifiers from StringModelConfig variants, # since configOptions sends resolved identifiers (e.g., "openai-chat:svc/glm-4.7") # while model_variants.keys() are variant names (e.g., "glm47"). - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig for config in manifest.model_variants.values(): if isinstance(config, StringModelConfig): @@ -1187,7 +1187,7 @@ async def swap_pool(self, config_path: str, agent_name: str | None = None) -> li # Re-resolve _agent_config from the new pool's manifest if ctx.main_agent_name and ctx.main_agent_name in ctx.manifest.agents: cfg = ctx.manifest.agents[ctx.main_agent_name] - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig if isinstance(cfg, NativeAgentConfig): if cfg.name is None: @@ -1195,7 +1195,7 @@ async def swap_pool(self, config_path: str, agent_name: str | None = None) -> li self._agent_config = cfg elif ctx.manifest.agents: cfg = next(iter(ctx.manifest.agents.values())) - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig if isinstance(cfg, NativeAgentConfig): self._agent_config = cfg diff --git a/src/agentpool_server/acp_server/acp_mcp_manager.py b/src/wolfharness_server/acp_server/acp_mcp_manager.py similarity index 99% rename from src/agentpool_server/acp_server/acp_mcp_manager.py rename to src/wolfharness_server/acp_server/acp_mcp_manager.py index cfbc0f91f..ffb076361 100644 --- a/src/agentpool_server/acp_server/acp_mcp_manager.py +++ b/src/wolfharness_server/acp_server/acp_mcp_manager.py @@ -11,7 +11,7 @@ import anyio -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -25,7 +25,7 @@ from mcp.types import JSONRPCMessage from pydantic import ValidationError -from agentpool.mcp_server.session_stream_pair import SessionStreamPair +from wolfharness.mcp_server.session_stream_pair import SessionStreamPair logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/acp_mcp_transport.py b/src/wolfharness_server/acp_server/acp_mcp_transport.py similarity index 96% rename from src/agentpool_server/acp_server/acp_mcp_transport.py rename to src/wolfharness_server/acp_server/acp_mcp_transport.py index 21dd217ac..b6b1a7c99 100644 --- a/src/agentpool_server/acp_server/acp_mcp_transport.py +++ b/src/wolfharness_server/acp_server/acp_mcp_transport.py @@ -14,13 +14,13 @@ from fastmcp.client.transports import ClientTransport from mcp import ClientSession -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import AsyncIterator, Callable - from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnection + from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnection logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/commands/__init__.py b/src/wolfharness_server/acp_server/commands/__init__.py similarity index 100% rename from src/agentpool_server/acp_server/commands/__init__.py rename to src/wolfharness_server/acp_server/commands/__init__.py diff --git a/src/agentpool_server/acp_server/commands/debug_commands.py b/src/wolfharness_server/acp_server/commands/debug_commands.py similarity index 97% rename from src/agentpool_server/acp_server/commands/debug_commands.py rename to src/wolfharness_server/acp_server/commands/debug_commands.py index ea6c0c8fc..6a4db0498 100644 --- a/src/agentpool_server/acp_server/commands/debug_commands.py +++ b/src/wolfharness_server/acp_server/commands/debug_commands.py @@ -22,10 +22,10 @@ ToolCallStart, UserMessageChunk, ) -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_server.acp_server.session import ACPSession # noqa: TC001 +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_server.acp_server.session import ACPSession # noqa: TC001 logger = get_logger(__name__) @@ -368,7 +368,7 @@ class DebugSetPoolCommand(NodeCommand): client displays and the actual server state. The configuration can be specified as: - - A stored config name (from `agentpool add`) + - A stored config name (from `wolfharness add`) - A direct path to a configuration file Options: @@ -400,7 +400,7 @@ async def execute_command( import importlib from pathlib import Path as FilePath - agent_store = importlib.import_module("agentpool_cli").agent_store + agent_store = importlib.import_module("wolfharness_cli").agent_store session = ctx.context.data if not session: diff --git a/src/agentpool_server/acp_server/commands/docs_commands/__init__.py b/src/wolfharness_server/acp_server/commands/docs_commands/__init__.py similarity index 100% rename from src/agentpool_server/acp_server/commands/docs_commands/__init__.py rename to src/wolfharness_server/acp_server/commands/docs_commands/__init__.py diff --git a/src/agentpool_server/acp_server/commands/docs_commands/fetch_repo.py b/src/wolfharness_server/acp_server/commands/docs_commands/fetch_repo.py similarity index 96% rename from src/agentpool_server/acp_server/commands/docs_commands/fetch_repo.py rename to src/wolfharness_server/acp_server/commands/docs_commands/fetch_repo.py index 05cf3c7a3..86f57bbf1 100644 --- a/src/agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +++ b/src/wolfharness_server/acp_server/commands/docs_commands/fetch_repo.py @@ -9,10 +9,10 @@ from pydantic_ai import UserPromptPart from slashed import CommandContext # noqa: TC002 -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_server.acp_server.session import ACPSession # noqa: TC001 +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_server.acp_server.session import ACPSession # noqa: TC001 logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/commands/docs_commands/get_schema.py b/src/wolfharness_server/acp_server/commands/docs_commands/get_schema.py similarity index 96% rename from src/agentpool_server/acp_server/commands/docs_commands/get_schema.py rename to src/wolfharness_server/acp_server/commands/docs_commands/get_schema.py index b86ba464e..2e12a9b60 100644 --- a/src/agentpool_server/acp_server/commands/docs_commands/get_schema.py +++ b/src/wolfharness_server/acp_server/commands/docs_commands/get_schema.py @@ -9,10 +9,10 @@ from schemez import jsonschema_to_code, openapi_to_code from slashed import CommandContext # noqa: TC002 -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_server.acp_server.session import ACPSession # noqa: TC001 +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_server.acp_server.session import ACPSession # noqa: TC001 from .helpers import SCHEMA_EXTRACTION_SCRIPT diff --git a/src/agentpool_server/acp_server/commands/docs_commands/get_source.py b/src/wolfharness_server/acp_server/commands/docs_commands/get_source.py similarity index 91% rename from src/agentpool_server/acp_server/commands/docs_commands/get_source.py rename to src/wolfharness_server/acp_server/commands/docs_commands/get_source.py index 916bfebcd..ae5c6d028 100644 --- a/src/agentpool_server/acp_server/commands/docs_commands/get_source.py +++ b/src/wolfharness_server/acp_server/commands/docs_commands/get_source.py @@ -7,11 +7,11 @@ from pydantic_ai import UserPromptPart from slashed import CommandContext # noqa: TC002 -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -import agentpool.utils.importing -from agentpool_server.acp_server.session import ACPSession # noqa: TC001 +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +import wolfharness.utils.importing +from wolfharness_server.acp_server.session import ACPSession # noqa: TC001 logger = get_logger(__name__) @@ -54,7 +54,7 @@ async def execute_command( await session.notifications.send_agent_text(msg) return - importing_py_path = agentpool.utils.importing.__file__ + importing_py_path = wolfharness.utils.importing.__file__ assert isinstance(importing_py_path, str) await session.notifications.tool_call_start( tool_call_id=tool_call_id, diff --git a/src/agentpool_server/acp_server/commands/docs_commands/git_diff.py b/src/wolfharness_server/acp_server/commands/docs_commands/git_diff.py similarity index 94% rename from src/agentpool_server/acp_server/commands/docs_commands/git_diff.py rename to src/wolfharness_server/acp_server/commands/docs_commands/git_diff.py index 3a17a6042..6b8c1f5e6 100644 --- a/src/agentpool_server/acp_server/commands/docs_commands/git_diff.py +++ b/src/wolfharness_server/acp_server/commands/docs_commands/git_diff.py @@ -7,10 +7,10 @@ from pydantic_ai import UserPromptPart from slashed import CommandContext # noqa: TC002 -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_server.acp_server.session import ACPSession # noqa: TC001 +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_server.acp_server.session import ACPSession # noqa: TC001 logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/commands/docs_commands/helpers.py b/src/wolfharness_server/acp_server/commands/docs_commands/helpers.py similarity index 100% rename from src/agentpool_server/acp_server/commands/docs_commands/helpers.py rename to src/wolfharness_server/acp_server/commands/docs_commands/helpers.py diff --git a/src/agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py b/src/wolfharness_server/acp_server/commands/docs_commands/url_to_markdown.py similarity index 92% rename from src/agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py rename to src/wolfharness_server/acp_server/commands/docs_commands/url_to_markdown.py index 59da07646..504951fbf 100644 --- a/src/agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py +++ b/src/wolfharness_server/acp_server/commands/docs_commands/url_to_markdown.py @@ -9,10 +9,10 @@ from pydantic_ai import UserPromptPart from slashed import CommandContext # noqa: TC002 -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool.messaging.context import NodeContext # noqa: TC001 -from agentpool_server.acp_server.session import ACPSession # noqa: TC001 +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness.messaging.context import NodeContext # noqa: TC001 +from wolfharness_server.acp_server.session import ACPSession # noqa: TC001 logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/commands/skill_commands.py b/src/wolfharness_server/acp_server/commands/skill_commands.py similarity index 93% rename from src/agentpool_server/acp_server/commands/skill_commands.py rename to src/wolfharness_server/acp_server/commands/skill_commands.py index a0d327434..c0c6a6fcd 100644 --- a/src/agentpool_server/acp_server/commands/skill_commands.py +++ b/src/wolfharness_server/acp_server/commands/skill_commands.py @@ -6,8 +6,8 @@ import logfire -from agentpool.log import get_logger -from agentpool_server.opencode_server.skill_bridge import create_skill_command +from wolfharness.log import get_logger +from wolfharness_server.opencode_server.skill_bridge import create_skill_command logger = get_logger(__name__) @@ -15,7 +15,7 @@ if TYPE_CHECKING: from slashed import Command as SlashedCommand - from agentpool.skills.command import SkillCommand + from wolfharness.skills.command import SkillCommand class ACPSkillBridge: diff --git a/src/agentpool_server/acp_server/converters.py b/src/wolfharness_server/acp_server/converters.py similarity index 94% rename from src/agentpool_server/acp_server/converters.py rename to src/wolfharness_server/acp_server/converters.py index 7338cb8b4..f6b12b166 100644 --- a/src/agentpool_server/acp_server/converters.py +++ b/src/wolfharness_server/acp_server/converters.py @@ -28,13 +28,13 @@ TextResourceContents, ) from acp.schema.mcp import AcpMcpServer -from agentpool.log import get_logger -from agentpool.utils.pydantic_ai_helpers import ( +from wolfharness.log import get_logger +from wolfharness.utils.pydantic_ai_helpers import ( format_uri_as_link, get_file_url_obj, uri_to_path_reference, ) -from agentpool_config.mcp_server import ( +from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, @@ -48,11 +48,11 @@ from acp.schema import ContentBlock, McpServer, SessionConfigOption from acp.schema.content_blocks import ResourceContents - from agentpool.agents.modes import ModeCategory, ModeInfo - from agentpool.common_types import PathReference - from agentpool.messaging import MessageNode - from agentpool.sessions import SessionData - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness.agents.modes import ModeCategory, ModeInfo + from wolfharness.common_types import PathReference + from wolfharness.messaging import MessageNode + from wolfharness.sessions import SessionData + from wolfharness_config.mcp_server import MCPServerConfig logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/event_converter.py b/src/wolfharness_server/acp_server/event_converter.py similarity index 99% rename from src/agentpool_server/acp_server/event_converter.py rename to src/wolfharness_server/acp_server/event_converter.py index 21403f86a..038f73939 100644 --- a/src/agentpool_server/acp_server/event_converter.py +++ b/src/wolfharness_server/acp_server/event_converter.py @@ -51,7 +51,7 @@ UserMessageChunk, ) from acp.utils import generate_tool_title, infer_tool_kind, to_acp_content_blocks -from agentpool.agents.events import ( +from wolfharness.agents.events import ( CompactionEvent, CustomEvent, DiffContentItem, @@ -78,8 +78,8 @@ ToolResultMetadataEvent, UserMessageInsertedEvent, ) -from agentpool.log import get_logger -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.log import get_logger +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict if TYPE_CHECKING: @@ -89,7 +89,7 @@ from acp.schema import TextContentBlock from acp.schema.tool_call import ToolCallContent, ToolCallKind - from agentpool.agents.events import RichAgentStreamEvent + from wolfharness.agents.events import RichAgentStreamEvent logger = get_logger(__name__) @@ -549,7 +549,7 @@ async def convert( # noqa: PLR0915 PlanEntry as ACPPlanEntry, TerminalToolCallContent, ) - from agentpool_server.acp_server.syntax_detection import format_zed_code_block + from wolfharness_server.acp_server.syntax_detection import format_zed_code_block match event: # Text output diff --git a/src/agentpool_server/acp_server/handler.py b/src/wolfharness_server/acp_server/handler.py similarity index 97% rename from src/agentpool_server/acp_server/handler.py rename to src/wolfharness_server/acp_server/handler.py index fc7cb4960..962785e1a 100644 --- a/src/agentpool_server/acp_server/handler.py +++ b/src/wolfharness_server/acp_server/handler.py @@ -20,11 +20,11 @@ from acp.agent.acp_requests import ACPRequests from acp.schema.capabilities import ClientCapabilities -from agentpool.agents.events.events import ElicitationDeferredEvent, SpawnSessionStart -from agentpool.log import get_logger -from agentpool_server.acp_server.event_converter import ACPEventConverter, SubagentContext -from agentpool_server.acp_server.input_provider import ACPInputProvider -from agentpool_server.mixins import ConsumerShutdown, ProtocolEventConsumerMixin +from wolfharness.agents.events.events import ElicitationDeferredEvent, SpawnSessionStart +from wolfharness.log import get_logger +from wolfharness_server.acp_server.event_converter import ACPEventConverter, SubagentContext +from wolfharness_server.acp_server.input_provider import ACPInputProvider +from wolfharness_server.mixins import ConsumerShutdown, ProtocolEventConsumerMixin if TYPE_CHECKING: @@ -32,9 +32,9 @@ from acp import Client from acp.schema import ContentBlock, PromptResponse, StopReason - from agentpool.host.context import HostContext - from agentpool.orchestrator.core import EventBus, EventEnvelope - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness.host.context import HostContext + from wolfharness.orchestrator.core import EventBus, EventEnvelope + from wolfharness_server.acp_server.session_manager import ACPSessionManager logger = get_logger(__name__) @@ -344,7 +344,7 @@ async def _handle_elicitation_deferred( session_id: The session that has a pending elicitation. event: The elicitation deferred event with params. """ - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload session_pool = self._host_context.session_pool if session_pool is None: @@ -391,7 +391,7 @@ async def _handle_elicitation_deferred( else: match response.action: case "accept": - from agentpool.ui.elicitation import normalize_elicit_content + from wolfharness.ui.elicitation import normalize_elicit_content payload = ElicitationResumePayload( deferred_handle=event.deferred_handle, @@ -524,8 +524,8 @@ async def handle_prompt( # noqa: PLR0915 Returns: A ``PromptResponse`` with the stop reason. """ - from agentpool.lifecycle.types import DeliveryMode - from agentpool_server.acp_server.converters import from_acp_content + from wolfharness.lifecycle.types import DeliveryMode + from wolfharness_server.acp_server.converters import from_acp_content session_pool = self._host_context.session_pool if session_pool is None: @@ -590,7 +590,7 @@ async def handle_prompt( # noqa: PLR0915 # Commands inject expanded prompts into the SessionPool per-session # agent's staged_content, which the agent run loop consumes automatically. if acp_session is not None: - from agentpool_server.acp_server.session import SLASH_PATTERN, split_commands + from wolfharness_server.acp_server.session import SLASH_PATTERN, split_commands commands, non_command_content = split_commands(contents, acp_session.command_store) if commands: @@ -653,7 +653,7 @@ async def handle_prompt( # noqa: PLR0915 message_id = str(uuid.uuid4()) # Build ACP meta from content blocks for the EventBus event. - from agentpool_server.acp_server.event_converter import ACPUserMessageMeta + from wolfharness_server.acp_server.event_converter import ACPUserMessageMeta content_block_dicts = [block.model_dump() for block in prompt] acp_meta = ACPUserMessageMeta(content_blocks=content_block_dicts) diff --git a/src/agentpool_server/acp_server/input_provider.py b/src/wolfharness_server/acp_server/input_provider.py similarity index 98% rename from src/agentpool_server/acp_server/input_provider.py rename to src/wolfharness_server/acp_server/input_provider.py index 147aa58d9..d8045c74c 100644 --- a/src/agentpool_server/acp_server/input_provider.py +++ b/src/wolfharness_server/acp_server/input_provider.py @@ -10,17 +10,17 @@ from acp import AllowedOutcome, DeniedOutcome, PermissionOption from acp.utils import DEFAULT_PERMISSION_OPTIONS -from agentpool.log import get_logger -from agentpool.ui.base import InputProvider -from agentpool.ui.elicitation import normalize_elicit_content +from wolfharness.log import get_logger +from wolfharness.ui.base import InputProvider +from wolfharness.ui.elicitation import normalize_elicit_content if TYPE_CHECKING: from acp import RequestPermissionResponse from acp.schema.elicitation import ElicitationCreateResponse - from agentpool import AgentContext - from agentpool.agents.context import ConfirmationResult - from agentpool_server.acp_server.session import ACPSession + from wolfharness import AgentContext + from wolfharness.agents.context import ConfirmationResult + from wolfharness_server.acp_server.session import ACPSession logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/provider_router.py b/src/wolfharness_server/acp_server/provider_router.py similarity index 97% rename from src/agentpool_server/acp_server/provider_router.py rename to src/wolfharness_server/acp_server/provider_router.py index 6a1cdee3f..7f74ce782 100644 --- a/src/agentpool_server/acp_server/provider_router.py +++ b/src/wolfharness_server/acp_server/provider_router.py @@ -10,12 +10,12 @@ from typing import TYPE_CHECKING from acp.schema.providers import ProviderCurrentConfig, ProviderInfo -from agentpool.log import get_logger -from agentpool_server.shared.model_utils import _extract_provider +from wolfharness.log import get_logger +from wolfharness_server.shared.model_utils import _extract_provider if TYPE_CHECKING: - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.manifest import AgentsManifest logger = get_logger(__name__) diff --git a/src/agentpool_server/acp_server/server.py b/src/wolfharness_server/acp_server/server.py similarity index 95% rename from src/agentpool_server/acp_server/server.py rename to src/wolfharness_server/acp_server/server.py index 37c1f401e..a1d37036e 100644 --- a/src/agentpool_server/acp_server/server.py +++ b/src/wolfharness_server/acp_server/server.py @@ -1,4 +1,4 @@ -"""ACP (Agent Client Protocol) server implementation for agentpool. +"""ACP (Agent Client Protocol) server implementation for wolfharness. This module provides the main server class for exposing AgentPool via the Agent Client Protocol. @@ -12,14 +12,14 @@ from typing import TYPE_CHECKING, Any, Literal, Self from acp import ACPWebSocketTransport, StdioTransport, serve -from agentpool import AgentPool -from agentpool.log import get_logger -from agentpool.models.manifest import AgentsManifest -from agentpool_config.context import ConfigContextManager -from agentpool_config.pool_server import ACPPoolServerConfig -from agentpool_server import BaseServer -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness import AgentPool +from wolfharness.log import get_logger +from wolfharness.models.manifest import AgentsManifest +from wolfharness_config.context import ConfigContextManager +from wolfharness_config.pool_server import ACPPoolServerConfig +from wolfharness_server import BaseServer +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.session_manager import ACPSessionManager if TYPE_CHECKING: @@ -29,7 +29,7 @@ from acp import Transport from acp.agent.connection import AgentSideConnection - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent logger = get_logger(__name__) @@ -85,9 +85,9 @@ def observer(event: Any) -> None: class ACPServer(BaseServer): - """ACP (Agent Client Protocol) server for agentpool using external library. + """ACP (Agent Client Protocol) server for wolfharness using external library. - Provides a bridge between agentpool's Agent system and the standard ACP + Provides a bridge between wolfharness's Agent system and the standard ACP JSON-RPC protocol using the external acp library for robust communication. The actual client communication happens via the AgentSideConnection created diff --git a/src/agentpool_server/acp_server/session.py b/src/wolfharness_server/acp_server/session.py similarity index 91% rename from src/agentpool_server/acp_server/session.py rename to src/wolfharness_server/acp_server/session.py index 4e045276a..d4dc219c5 100644 --- a/src/agentpool_server/acp_server/session.py +++ b/src/wolfharness_server/acp_server/session.py @@ -1,4 +1,4 @@ -"""ACP (Agent Client Protocol) session management for agentpool. +"""ACP (Agent Client Protocol) session management for wolfharness. This module provides session lifecycle management, state tracking, and coordination between agents and ACP clients through the JSON-RPC protocol. @@ -24,14 +24,14 @@ from acp.agent.notifications import ACPNotifications from acp.filesystem import ACPFileSystem from acp.schema import AvailableCommand, ClientCapabilities -from agentpool import Agent -from agentpool.agents.acp_agent import ACPAgent -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool_server.acp_server.input_provider import ACPInputProvider -from agentpool_server.acp_server.session_agent_mgmt import ACPSessionAgentMgmtMixin -from agentpool_server.acp_server.session_events import ACPSessionEventsMixin -from agentpool_server.acp_server.session_lifecycle import ACPSessionLifecycleMixin +from wolfharness import Agent +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness_server.acp_server.input_provider import ACPInputProvider +from wolfharness_server.acp_server.session_agent_mgmt import ACPSessionAgentMgmtMixin +from wolfharness_server.acp_server.session_events import ACPSessionEventsMixin +from wolfharness_server.acp_server.session_lifecycle import ACPSessionLifecycleMixin if TYPE_CHECKING: @@ -47,12 +47,12 @@ StopReason, Usage, ) - from agentpool.agents.base_agent import BaseAgent - from agentpool.common_types import PathReference - from agentpool.host.context import HostContext - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent - from agentpool_server.acp_server.event_converter import ACPEventConverter - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.common_types import PathReference + from wolfharness.host.context import HostContext + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.session_manager import ACPSessionManager logger = get_logger(__name__) @@ -94,7 +94,7 @@ def get_all_commands() -> Sequence[BaseCommand]: """Return empty command list to align with OpenCode behavior. All built-in framework commands are hidden to keep ACP consistent - with OpenCode, which does not register agentpool_commands at all. + with OpenCode, which does not register wolfharness_commands at all. """ return [] @@ -230,7 +230,7 @@ def __post_init__(self) -> None: self._remote_commands: list[AvailableCommand] = [] # Skill bridge: converts SkillCommand → SlashedCommand for command_store - from agentpool_server.acp_server.commands.skill_commands import ACPSkillBridge + from wolfharness_server.acp_server.commands.skill_commands import ACPSkillBridge self._skill_bridge: ACPSkillBridge = ACPSkillBridge() self._skill_change_task: asyncio.Task[None] | None = None diff --git a/src/agentpool_server/acp_server/session_agent_mgmt.py b/src/wolfharness_server/acp_server/session_agent_mgmt.py similarity index 97% rename from src/agentpool_server/acp_server/session_agent_mgmt.py rename to src/wolfharness_server/acp_server/session_agent_mgmt.py index e00355a2b..0335eebba 100644 --- a/src/agentpool_server/acp_server/session_agent_mgmt.py +++ b/src/wolfharness_server/acp_server/session_agent_mgmt.py @@ -14,10 +14,10 @@ import anyio import logfire -from agentpool import Agent -from agentpool.commands.base import NodeCommand -from agentpool.log import get_logger -from agentpool_config.commands import BaseCommandConfig +from wolfharness import Agent +from wolfharness.commands.base import NodeCommand +from wolfharness.log import get_logger +from wolfharness_config.commands import BaseCommandConfig if TYPE_CHECKING: @@ -126,7 +126,7 @@ def _register_skill_commands(self) -> None: objects in command_store with replace=True for idempotent updates. Also removes stale commands that are no longer present or invocable. """ - from agentpool.skills.command import SkillCommand + from wolfharness.skills.command import SkillCommand ctx = self.host_context skills_registry = ctx.skills_registry @@ -191,7 +191,7 @@ async def _watch_skill_changes(self) -> None: POOL scope. When a skills_changed event arrives, rebuilds skill commands and sends an update to the client. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel ctx = self.host_context if ctx.extension_registry is None: @@ -323,7 +323,7 @@ async def execute_slash_command(self, command_text: str) -> None: command_text: Full command text (including slash) session: ACP session context """ - from agentpool_server.acp_server.session import SLASH_PATTERN + from wolfharness_server.acp_server.session import SLASH_PATTERN if match := SLASH_PATTERN.match(command_text.strip()): command_name = match.group(1) diff --git a/src/agentpool_server/acp_server/session_events.py b/src/wolfharness_server/acp_server/session_events.py similarity index 92% rename from src/agentpool_server/acp_server/session_events.py rename to src/wolfharness_server/acp_server/session_events.py index d87ab64d4..14979504e 100644 --- a/src/agentpool_server/acp_server/session_events.py +++ b/src/wolfharness_server/acp_server/session_events.py @@ -10,13 +10,13 @@ from tokonomics.model_discovery.model_info import ModelInfo -from agentpool.agents.events.events import ToastInfo -from agentpool.agents.modes import ConfigOptionChanged, ModeInfo -from agentpool.log import get_logger +from wolfharness.agents.events.events import ToastInfo +from wolfharness.agents.modes import ConfigOptionChanged, ModeInfo +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool.agents.base_agent import StateUpdate + from wolfharness.agents.base_agent import StateUpdate logger = get_logger(__name__) @@ -52,7 +52,7 @@ async def _on_state_updated(self, state: StateUpdate) -> None: CurrentModelUpdate, CurrentModeUpdate, ) - from agentpool_server.acp_server.acp_agent import get_session_config_options + from wolfharness_server.acp_server.acp_agent import get_session_config_options update: CurrentModeUpdate | CurrentModelUpdate | ConfigOptionUpdate match state: diff --git a/src/agentpool_server/acp_server/session_lifecycle.py b/src/wolfharness_server/acp_server/session_lifecycle.py similarity index 95% rename from src/agentpool_server/acp_server/session_lifecycle.py rename to src/wolfharness_server/acp_server/session_lifecycle.py index 620b271fb..1d492db52 100644 --- a/src/agentpool_server/acp_server/session_lifecycle.py +++ b/src/wolfharness_server/acp_server/session_lifecycle.py @@ -14,17 +14,17 @@ import anyio from pydantic_ai import UsageLimitExceeded -from agentpool import Agent -from agentpool.log import get_logger -from agentpool_server.acp_server.converters import from_acp_content -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness import Agent +from wolfharness.log import get_logger +from wolfharness_server.acp_server.converters import from_acp_content +from wolfharness_server.acp_server.event_converter import ACPEventConverter if TYPE_CHECKING: from collections.abc import Sequence from acp.schema import ContentBlock, McpServer, StopReason - from agentpool.mcp_server.config_snapshot import McpConfigEntry + from wolfharness.mcp_server.config_snapshot import McpConfigEntry logger = get_logger(__name__) @@ -95,7 +95,7 @@ def _make_provider_name(self, display_name: str) -> str: """ import hashlib - from agentpool_server.acp_server.session import _MAX_PROVIDER_NAME_LENGTH + from wolfharness_server.acp_server.session import _MAX_PROVIDER_NAME_LENGTH prefix = "session_" suffix = f"_{display_name}" @@ -118,12 +118,12 @@ async def initialize_mcp_servers(self) -> None: capability building can reuse it. """ from acp.schema.mcp import AcpMcpServer - from agentpool import Agent - from agentpool.mcp_server.config_snapshot import ( + from wolfharness import Agent + from wolfharness.mcp_server.config_snapshot import ( McpConfigEntry, McpConfigSnapshot, ) - from agentpool_server.acp_server.converters import ( + from wolfharness_server.acp_server.converters import ( convert_acp_mcp_server_to_config, ) @@ -153,7 +153,7 @@ async def _init_server(server: McpServer) -> None: raise RuntimeError( # noqa: TRY301 f"AcpMcpConnection not found for {connection_id}" ) - from agentpool_server.acp_server.acp_mcp_transport import ( + from wolfharness_server.acp_server.acp_mcp_transport import ( AcpMcpTransport, ) @@ -256,7 +256,7 @@ async def process_prompt(self, content_blocks: Sequence[ContentBlock]) -> StopRe Returns: Stop reason """ - from agentpool_server.acp_server.session import infer_stop_reason, split_commands + from wolfharness_server.acp_server.session import infer_stop_reason, split_commands self._cancelled = False fs = self.agent.env.get_fs() @@ -376,7 +376,7 @@ async def _send_toast( ) -> None: """Send a toast notification via ExtNotification. - Uses _agentpool/toast ext notification instead of polluting chat + Uses _wolfharness/toast ext notification instead of polluting chat history with error messages disguised as agent text. Args: @@ -389,7 +389,7 @@ async def _send_toast( return try: await self.notifications.send_ext_notification( - method="_agentpool/toast", + method="_wolfharness/toast", params={ "message": message, "level": level, diff --git a/src/agentpool_server/acp_server/session_manager.py b/src/wolfharness_server/acp_server/session_manager.py similarity index 96% rename from src/agentpool_server/acp_server/session_manager.py rename to src/wolfharness_server/acp_server/session_manager.py index 3cff7b211..cbef4882f 100644 --- a/src/agentpool_server/acp_server/session_manager.py +++ b/src/wolfharness_server/acp_server/session_manager.py @@ -6,8 +6,8 @@ from typing import TYPE_CHECKING, Any, Literal, Self from acp.schema import ClientCapabilities -from agentpool.log import get_logger -from agentpool_server.acp_server.session import ACPSession +from wolfharness.log import get_logger +from wolfharness_server.acp_server.session import ACPSession if TYPE_CHECKING: @@ -15,11 +15,11 @@ from acp import Client from acp.schema import Implementation, McpServer - from agentpool import AgentPool - from agentpool.orchestrator import SessionController - from agentpool.storage.manager import StorageManager - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent - from agentpool_storage.protocols import SessionPersistence + from wolfharness import AgentPool + from wolfharness.orchestrator import SessionController + from wolfharness.storage.manager import StorageManager + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_storage.protocols import SessionPersistence logger = get_logger(__name__) @@ -114,7 +114,7 @@ async def create_session( # noqa: PLR0915 if parent_session_id is not None and self._pool.session_pool is not None: # Child session path: delegate to SessionPool which # inherits project_id/cwd from the parent automatically. - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id child_session_id = session_id or generate_session_id() await self._pool.session_pool.create_session( @@ -144,14 +144,14 @@ async def create_session( # noqa: PLR0915 # Top-level session path: delegate to SessionPool.create_session() # which persists SessionData via SessionController.get_or_create_session(). if self._pool.session_pool is not None: - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id if session_id is None: session_id = generate_session_id() # Compute project_id from cwd so that ACP sessions are # correctly associated with the project in the TUI sidebar. - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id project_id = compute_project_id(cwd) @@ -164,8 +164,8 @@ async def create_session( # noqa: PLR0915 ) else: # Fallback: no SessionPool (tests only) — persist directly. - from agentpool.sessions import SessionData - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness.sessions import SessionData + from wolfharness_storage.opencode_provider.helpers import compute_project_id # Use storage.generate_session_id for backward compat with # tests that mock it. @@ -199,7 +199,7 @@ async def create_session( # noqa: PLR0915 raise ValueError(msg) if cfg.name is None: cfg = cfg.model_copy(update={"name": agent_name}) - from agentpool_config.context import ConfigContextManager + from wolfharness_config.context import ConfigContextManager with ConfigContextManager(self._pool._config_file_path): session_agent = cfg.get_agent(pool=self._pool) diff --git a/src/agentpool_server/acp_server/syntax_detection.py b/src/wolfharness_server/acp_server/syntax_detection.py similarity index 100% rename from src/agentpool_server/acp_server/syntax_detection.py rename to src/wolfharness_server/acp_server/syntax_detection.py diff --git a/src/agentpool_server/acp_server/zed_tools.md b/src/wolfharness_server/acp_server/zed_tools.md similarity index 100% rename from src/agentpool_server/acp_server/zed_tools.md rename to src/wolfharness_server/acp_server/zed_tools.md diff --git a/src/agentpool_server/aggregating_server.py b/src/wolfharness_server/aggregating_server.py similarity index 98% rename from src/agentpool_server/aggregating_server.py rename to src/wolfharness_server/aggregating_server.py index 97a69febd..dbb3f56aa 100644 --- a/src/agentpool_server/aggregating_server.py +++ b/src/wolfharness_server/aggregating_server.py @@ -7,15 +7,15 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Literal, Self -from agentpool.log import get_logger -from agentpool_server.base import BaseServer +from wolfharness.log import get_logger +from wolfharness_server.base import BaseServer if TYPE_CHECKING: from collections.abc import Sequence from types import TracebackType - from agentpool import AgentPool + from wolfharness import AgentPool # Type-safe server status literals ServerStatus = Literal["not_initialized", "initialized", "running", "failed", "stopped"] diff --git a/src/agentpool_server/agui_server/__init__.py b/src/wolfharness_server/agui_server/__init__.py similarity index 63% rename from src/agentpool_server/agui_server/__init__.py rename to src/wolfharness_server/agui_server/__init__.py index bf812f06f..184395793 100644 --- a/src/agentpool_server/agui_server/__init__.py +++ b/src/wolfharness_server/agui_server/__init__.py @@ -1,4 +1,4 @@ -"""AG-UI server module for agentpool. +"""AG-UI server module for wolfharness. This module provides server implementation for exposing AgentPool agents via the AG-UI protocol with each agent on its own route. @@ -9,7 +9,7 @@ from __future__ import annotations -from agentpool_server.agui_server.base_agent_adapter import BaseAgentAGUIAdapter -from agentpool_server.agui_server.server import AGUIServer +from wolfharness_server.agui_server.base_agent_adapter import BaseAgentAGUIAdapter +from wolfharness_server.agui_server.server import AGUIServer __all__ = ["AGUIServer", "BaseAgentAGUIAdapter"] diff --git a/src/agentpool_server/agui_server/base_agent_adapter.py b/src/wolfharness_server/agui_server/base_agent_adapter.py similarity index 98% rename from src/agentpool_server/agui_server/base_agent_adapter.py rename to src/wolfharness_server/agui_server/base_agent_adapter.py index 030c78c14..ad9915e8d 100644 --- a/src/agentpool_server/agui_server/base_agent_adapter.py +++ b/src/wolfharness_server/agui_server/base_agent_adapter.py @@ -14,7 +14,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any -from agentpool.agents.events.events import StepUsageEvent +from wolfharness.agents.events.events import StepUsageEvent if TYPE_CHECKING: @@ -24,7 +24,7 @@ from starlette.requests import Request from starlette.responses import StreamingResponse - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent __all__ = ["BaseAgentAGUIAdapter"] diff --git a/src/agentpool_server/agui_server/server.py b/src/wolfharness_server/agui_server/server.py similarity index 92% rename from src/agentpool_server/agui_server/server.py rename to src/wolfharness_server/agui_server/server.py index d77db49cd..bf0d34c5f 100644 --- a/src/agentpool_server/agui_server/server.py +++ b/src/wolfharness_server/agui_server/server.py @@ -1,4 +1,4 @@ -"""AG-UI server implementation for agentpool pool. +"""AG-UI server implementation for wolfharness pool. This module provides a server that exposes all agents in an AgentPool via the AG-UI protocol, with each agent accessible at its own route. @@ -11,10 +11,10 @@ from typing import TYPE_CHECKING -from agentpool.log import get_logger -from agentpool_server.agui_server.base_agent_adapter import BaseAgentAGUIAdapter -from agentpool_server.http_server import HTTPServer -from agentpool_server.mixins import ProtocolEventConsumerMixin +from wolfharness.log import get_logger +from wolfharness_server.agui_server.base_agent_adapter import BaseAgentAGUIAdapter +from wolfharness_server.http_server import HTTPServer +from wolfharness_server.mixins import ProtocolEventConsumerMixin if TYPE_CHECKING: @@ -22,8 +22,8 @@ from starlette.responses import Response from starlette.routing import Route - from agentpool import AgentPool - from agentpool.orchestrator.core import EventBus, EventEnvelope + from wolfharness import AgentPool + from wolfharness.orchestrator.core import EventBus, EventEnvelope logger = get_logger(__name__) @@ -129,7 +129,7 @@ async def agent_handler(request: Request, agent_name: str = agent_name) -> Respo sp = self.pool.session_pool if sp is not None: - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id session_id = generate_session_id() await sp.create_session(session_id, agent_name=agent_name) diff --git a/src/agentpool_server/agui_server/skill_tools.py b/src/wolfharness_server/agui_server/skill_tools.py similarity index 97% rename from src/agentpool_server/agui_server/skill_tools.py rename to src/wolfharness_server/agui_server/skill_tools.py index df0086b7e..fdcb08bf4 100644 --- a/src/agentpool_server/agui_server/skill_tools.py +++ b/src/wolfharness_server/agui_server/skill_tools.py @@ -8,13 +8,13 @@ from ag_ui.core import Tool import logfire -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) if TYPE_CHECKING: - from agentpool.skills.command import SkillCommand + from wolfharness.skills.command import SkillCommand def _hash_args(args: str) -> str: diff --git a/src/agentpool_server/base.py b/src/wolfharness_server/base.py similarity index 98% rename from src/agentpool_server/base.py rename to src/wolfharness_server/base.py index e57f1d745..02030ac26 100644 --- a/src/agentpool_server/base.py +++ b/src/wolfharness_server/base.py @@ -6,15 +6,15 @@ from contextlib import asynccontextmanager from typing import TYPE_CHECKING, Any, Self -from agentpool.log import get_logger -from agentpool.utils.task_group import ManagedTaskGroup +from wolfharness.log import get_logger +from wolfharness.utils.task_group import ManagedTaskGroup if TYPE_CHECKING: from collections.abc import AsyncIterator from types import TracebackType - from agentpool import AgentPool + from wolfharness import AgentPool logger = get_logger(__name__) diff --git a/src/agentpool_server/http_server.py b/src/wolfharness_server/http_server.py similarity index 92% rename from src/agentpool_server/http_server.py rename to src/wolfharness_server/http_server.py index 52cce198f..9d4a6e698 100644 --- a/src/agentpool_server/http_server.py +++ b/src/wolfharness_server/http_server.py @@ -8,8 +8,8 @@ from abc import abstractmethod from typing import TYPE_CHECKING, Self -from agentpool.log import get_logger -from agentpool_server.base import BaseServer +from wolfharness.log import get_logger +from wolfharness_server.base import BaseServer if TYPE_CHECKING: @@ -17,7 +17,7 @@ from starlette.routing import Route from upathtools import JoinablePathLike - from agentpool import AgentPool + from wolfharness import AgentPool logger = get_logger(__name__) @@ -139,7 +139,7 @@ def from_config( """Create HTTP server from configuration file. Args: - config_path: Path to agentpool YAML config file + config_path: Path to wolfharness YAML config file host: Host to bind server to port: Port to bind server to raise_exceptions: Whether to raise exceptions during server start @@ -147,9 +147,9 @@ def from_config( Returns: Configured server instance with agent pool from config """ - from agentpool import AgentPool - from agentpool.models.manifest import AgentsManifest - from agentpool_config.context import ConfigContextManager + from wolfharness import AgentPool + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.context import ConfigContextManager with ConfigContextManager(config_path): manifest = AgentsManifest.from_file(config_path) diff --git a/src/agentpool_server/mcp_server/__init__.py b/src/wolfharness_server/mcp_server/__init__.py similarity index 58% rename from src/agentpool_server/mcp_server/__init__.py rename to src/wolfharness_server/mcp_server/__init__.py index c9d903539..1efae72ec 100644 --- a/src/agentpool_server/mcp_server/__init__.py +++ b/src/wolfharness_server/mcp_server/__init__.py @@ -1,6 +1,6 @@ """MCP protocol server implementation for AgentPool.""" -from agentpool_server.mcp_server.server import MCPServer +from wolfharness_server.mcp_server.server import MCPServer __all__ = ["MCPServer"] diff --git a/src/agentpool_server/mcp_server/server.py b/src/wolfharness_server/mcp_server/server.py similarity index 93% rename from src/agentpool_server/mcp_server/server.py rename to src/wolfharness_server/mcp_server/server.py index 316be7601..0e31438be 100644 --- a/src/agentpool_server/mcp_server/server.py +++ b/src/wolfharness_server/mcp_server/server.py @@ -11,11 +11,11 @@ from mcp import types import platformdirs -import agentpool -from agentpool.capabilities.subagent_capability import SubagentCapability -from agentpool.log import get_logger -from agentpool.mcp_server import constants -from agentpool_server import BaseServer +import wolfharness +from wolfharness.capabilities.subagent_capability import SubagentCapability +from wolfharness.log import get_logger +from wolfharness.mcp_server import constants +from wolfharness_server import BaseServer if TYPE_CHECKING: @@ -26,9 +26,9 @@ from mcp.server.lowlevel.server import LifespanResultT from pydantic import AnyUrl - from agentpool import AgentPool - from agentpool.prompts.prompts import BasePrompt - from agentpool_config.pool_server import MCPPoolServerConfig + from wolfharness import AgentPool + from wolfharness.prompts.prompts import BasePrompt + from wolfharness_config.pool_server import MCPPoolServerConfig LifespanHandler = Callable[ [FastMCP[LifespanResultT]], @@ -38,9 +38,9 @@ logger = get_logger(__name__) -agentpool_dir = platformdirs.user_config_dir("agentpool") +wolfharness_dir = platformdirs.user_config_dir("wolfharness") -store = DiskStore(directory=agentpool_dir) +store = DiskStore(directory=wolfharness_dir) middleware = ResponseCachingMiddleware(cache_storage=store) @@ -58,7 +58,7 @@ def __init__( config: MCPPoolServerConfig, lifespan: LifespanHandler | None = None, instructions: str | None = None, - name: str = "agentpool-server", + name: str = "wolfharness-server", raise_exceptions: bool = False, ) -> None: """Initialize server with agent pool. @@ -85,7 +85,7 @@ def __init__( self.fastmcp = FastMCP( instructions=instructions, lifespan=lifespan, - version=agentpool.__version__, + version=wolfharness.__version__, middleware=[middleware], ) self.server = self.fastmcp._mcp_server diff --git a/src/agentpool_server/mcp_server/zed_wrapper.py b/src/wolfharness_server/mcp_server/zed_wrapper.py similarity index 94% rename from src/agentpool_server/mcp_server/zed_wrapper.py rename to src/wolfharness_server/mcp_server/zed_wrapper.py index 6ca6db299..5d5997cc9 100644 --- a/src/agentpool_server/mcp_server/zed_wrapper.py +++ b/src/wolfharness_server/mcp_server/zed_wrapper.py @@ -5,15 +5,15 @@ from functools import wraps from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool.prompts.prompts import DynamicPrompt -from agentpool.utils.importing import import_callable +from wolfharness.log import get_logger +from wolfharness.prompts.prompts import DynamicPrompt +from wolfharness.utils.importing import import_callable if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.prompts.prompts import BasePrompt + from wolfharness.prompts.prompts import BasePrompt logger = get_logger(__name__) diff --git a/src/agentpool_server/mixins.py b/src/wolfharness_server/mixins.py similarity index 97% rename from src/agentpool_server/mixins.py rename to src/wolfharness_server/mixins.py index 6d0e4050d..826c20431 100644 --- a/src/agentpool_server/mixins.py +++ b/src/wolfharness_server/mixins.py @@ -12,11 +12,11 @@ import anyio -from agentpool.agents.events.events import SpawnSessionStart +from wolfharness.agents.events.events import SpawnSessionStart if TYPE_CHECKING: - from agentpool.orchestrator.core import EventBus, EventEnvelope + from wolfharness.orchestrator.core import EventBus, EventEnvelope class ConsumerShutdown(Exception): # noqa: N818 @@ -248,8 +248,8 @@ async def _event_consumer_loop(self, session_id: str) -> None: started = True # Deferred import to avoid circular dependency: - # agentpool.orchestrator.core -> agentpool_server.* -> mixins.py - from agentpool.orchestrator.core import drain_and_merge + # wolfharness.orchestrator.core -> wolfharness_server.* -> mixins.py + from wolfharness.orchestrator.core import drain_and_merge async for envelope in drain_and_merge(stream): if isinstance(envelope.event, SpawnSessionStart): diff --git a/src/agentpool_server/openai_api_server/__init__.py b/src/wolfharness_server/openai_api_server/__init__.py similarity index 100% rename from src/agentpool_server/openai_api_server/__init__.py rename to src/wolfharness_server/openai_api_server/__init__.py diff --git a/src/agentpool_server/openai_api_server/completions/__init__.py b/src/wolfharness_server/openai_api_server/completions/__init__.py similarity index 100% rename from src/agentpool_server/openai_api_server/completions/__init__.py rename to src/wolfharness_server/openai_api_server/completions/__init__.py diff --git a/src/agentpool_server/openai_api_server/completions/helpers.py b/src/wolfharness_server/openai_api_server/completions/helpers.py similarity index 93% rename from src/agentpool_server/openai_api_server/completions/helpers.py rename to src/wolfharness_server/openai_api_server/completions/helpers.py index 55fe45603..e90dd9621 100644 --- a/src/agentpool_server/openai_api_server/completions/helpers.py +++ b/src/wolfharness_server/openai_api_server/completions/helpers.py @@ -7,16 +7,16 @@ import anyenv from pydantic_ai import PartDeltaEvent, TextPartDelta, ThinkingPartDelta -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool.agents.events.events import StepUsageEvent -from agentpool.log import get_logger -from agentpool.utils.time_utils import now_ms +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness.agents.events.events import StepUsageEvent +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import now_ms if TYPE_CHECKING: from collections.abc import AsyncGenerator, AsyncIterator - from agentpool_server.openai_api_server.completions.models import ChatCompletionRequest + from wolfharness_server.openai_api_server.completions.models import ChatCompletionRequest logger = get_logger(__name__) diff --git a/src/agentpool_server/openai_api_server/completions/models.py b/src/wolfharness_server/openai_api_server/completions/models.py similarity index 96% rename from src/agentpool_server/openai_api_server/completions/models.py rename to src/wolfharness_server/openai_api_server/completions/models.py index 4f7e21151..5aacb867e 100644 --- a/src/agentpool_server/openai_api_server/completions/models.py +++ b/src/wolfharness_server/openai_api_server/completions/models.py @@ -7,7 +7,7 @@ from pydantic import Field from schemez import Schema -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) @@ -26,7 +26,7 @@ class OpenAIModelInfo(Schema): id: str object: str = "model" - owned_by: str = "agentpool" + owned_by: str = "wolfharness" created: int description: str | None = None permissions: list[str] = Field(default_factory=list) diff --git a/src/agentpool_server/openai_api_server/responses/__init__.py b/src/wolfharness_server/openai_api_server/responses/__init__.py similarity index 100% rename from src/agentpool_server/openai_api_server/responses/__init__.py rename to src/wolfharness_server/openai_api_server/responses/__init__.py diff --git a/src/agentpool_server/openai_api_server/responses/helpers.py b/src/wolfharness_server/openai_api_server/responses/helpers.py similarity index 93% rename from src/agentpool_server/openai_api_server/responses/helpers.py rename to src/wolfharness_server/openai_api_server/responses/helpers.py index 4a57a1a80..4c4298412 100644 --- a/src/agentpool_server/openai_api_server/responses/helpers.py +++ b/src/wolfharness_server/openai_api_server/responses/helpers.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any from uuid import uuid4 -from agentpool_server.openai_api_server.responses.models import ( +from wolfharness_server.openai_api_server.responses.models import ( Response, ResponseMessage, ResponseOutputReasoning, @@ -16,8 +16,8 @@ if TYPE_CHECKING: - from agentpool.messaging.messages import ChatMessage - from agentpool_server.openai_api_server.responses.models import ResponseRequest + from wolfharness.messaging.messages import ChatMessage + from wolfharness_server.openai_api_server.responses.models import ResponseRequest async def handle_request(request: ResponseRequest, message: ChatMessage[Any]) -> Response: diff --git a/src/agentpool_server/openai_api_server/responses/models.py b/src/wolfharness_server/openai_api_server/responses/models.py similarity index 100% rename from src/agentpool_server/openai_api_server/responses/models.py rename to src/wolfharness_server/openai_api_server/responses/models.py diff --git a/src/agentpool_server/openai_api_server/server.py b/src/wolfharness_server/openai_api_server/server.py similarity index 94% rename from src/agentpool_server/openai_api_server/server.py rename to src/wolfharness_server/openai_api_server/server.py index 421e96b39..45893c0fa 100644 --- a/src/agentpool_server/openai_api_server/server.py +++ b/src/wolfharness_server/openai_api_server/server.py @@ -8,13 +8,13 @@ import anyenv from fastapi import Header -from agentpool.agents.events import StreamCompleteEvent -from agentpool.log import get_logger -from agentpool.utils.identifiers import generate_session_id -from agentpool_server import BaseServer -from agentpool_server.mixins import ProtocolEventConsumerMixin -from agentpool_server.openai_api_server.completions.helpers import stream_response -from agentpool_server.openai_api_server.completions.models import ( +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.log import get_logger +from wolfharness.utils.identifiers import generate_session_id +from wolfharness_server import BaseServer +from wolfharness_server.mixins import ProtocolEventConsumerMixin +from wolfharness_server.openai_api_server.completions.helpers import stream_response +from wolfharness_server.openai_api_server.completions.models import ( ChatCompletionRequest, ChatCompletionResponse, Choice, @@ -22,8 +22,8 @@ OpenAIMessage, OpenAIModelInfo, ) -from agentpool_server.openai_api_server.responses.helpers import handle_request -from agentpool_server.openai_api_server.responses.models import ( # noqa: TC001 +from wolfharness_server.openai_api_server.responses.helpers import handle_request +from wolfharness_server.openai_api_server.responses.models import ( # noqa: TC001 Response as ResponsesResponse, ResponseRequest, ) @@ -32,8 +32,8 @@ if TYPE_CHECKING: from fastapi import Response - from agentpool import AgentPool - from agentpool.orchestrator.core import EventBus, EventEnvelope + from wolfharness import AgentPool + from wolfharness.orchestrator.core import EventBus, EventEnvelope logger = get_logger(__name__) @@ -290,7 +290,7 @@ async def create_response(self, req_body: ResponseRequest) -> ResponsesResponse: session_id = generate_session_id() await session_pool.create_session(session_id, agent_name=req_body.model) - from agentpool.agents.events import StreamCompleteEvent + from wolfharness.agents.events import StreamCompleteEvent message = None try: @@ -332,7 +332,7 @@ async def _start_async(self) -> None: import anyio import httpx - from agentpool import AgentPool + from wolfharness import AgentPool async def test_completions() -> None: """Test the chat completions API.""" @@ -381,7 +381,7 @@ async def test_responses() -> None: async def main() -> None: """Run server and test both endpoints.""" - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig pool = AgentPool() pool.manifest.agents["gpt-5-mini"] = NativeAgentConfig( diff --git a/src/agentpool_server/opencode_server/.rules b/src/wolfharness_server/opencode_server/.rules similarity index 93% rename from src/agentpool_server/opencode_server/.rules rename to src/wolfharness_server/opencode_server/.rules index c7047e302..239b6b5c5 100644 --- a/src/agentpool_server/opencode_server/.rules +++ b/src/wolfharness_server/opencode_server/.rules @@ -73,16 +73,16 @@ See `ENDPOINTS.md` for the full checklist of endpoints and their implementation ## Logging -Log file location: `~/.local/state/agentpool/log/opencode.log` +Log file location: `~/.local/state/wolfharness/log/opencode.log` To tail the logs: ```bash -tail -f ~/.local/state/agentpool/log/opencode.log +tail -f ~/.local/state/wolfharness/log/opencode.log ``` To filter for SSE events: ```bash -tail -f ~/.local/state/agentpool/log/opencode.log | grep -i sse +tail -f ~/.local/state/wolfharness/log/opencode.log | grep -i sse ``` ## Testing with the SDK diff --git a/src/agentpool_server/opencode_server/ENDPOINTS.md b/src/wolfharness_server/opencode_server/ENDPOINTS.md similarity index 100% rename from src/agentpool_server/opencode_server/ENDPOINTS.md rename to src/wolfharness_server/opencode_server/ENDPOINTS.md diff --git a/src/agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md b/src/wolfharness_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md similarity index 100% rename from src/agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md rename to src/wolfharness_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md diff --git a/src/agentpool_server/opencode_server/__init__.py b/src/wolfharness_server/opencode_server/__init__.py similarity index 80% rename from src/agentpool_server/opencode_server/__init__.py rename to src/wolfharness_server/opencode_server/__init__.py index f7ce5ed9c..5f6c6ace0 100644 --- a/src/agentpool_server/opencode_server/__init__.py +++ b/src/wolfharness_server/opencode_server/__init__.py @@ -5,8 +5,8 @@ Example usage: - from agentpool import AgentPool - from agentpool_server.opencode_server import OpenCodeServer + from wolfharness import AgentPool + from wolfharness_server.opencode_server import OpenCodeServer async with AgentPool("config.yml") as pool: assert pool.session_pool is not None @@ -18,7 +18,7 @@ Or programmatically: - from agentpool_server.opencode_server import create_app + from wolfharness_server.opencode_server import create_app app = create_app(agent=my_agent, working_dir="/path/to/project") # Use with uvicorn or other ASGI server diff --git a/src/agentpool_server/opencode_server/command_validation.py b/src/wolfharness_server/opencode_server/command_validation.py similarity index 100% rename from src/agentpool_server/opencode_server/command_validation.py rename to src/wolfharness_server/opencode_server/command_validation.py diff --git a/src/agentpool_server/opencode_server/converters.py b/src/wolfharness_server/opencode_server/converters.py similarity index 94% rename from src/agentpool_server/opencode_server/converters.py rename to src/wolfharness_server/opencode_server/converters.py index db9d979c1..7899d0ee9 100644 --- a/src/agentpool_server/opencode_server/converters.py +++ b/src/wolfharness_server/opencode_server/converters.py @@ -17,13 +17,13 @@ UserPromptPart, ) -from agentpool import log -from agentpool.messaging.messages import ChatMessage -from agentpool.sessions.models import SessionData -from agentpool.utils import identifiers as identifier -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict, to_user_content_or_path_ref -from agentpool.utils.time_utils import datetime_to_ms, ms_to_datetime -from agentpool_server.opencode_server.models import ( +from wolfharness import log +from wolfharness.messaging.messages import ChatMessage +from wolfharness.sessions.models import SessionData +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict, to_user_content_or_path_ref +from wolfharness.utils.time_utils import datetime_to_ms, ms_to_datetime +from wolfharness_server.opencode_server.models import ( AgentPartInput, FilePart, FilePartInput, @@ -60,15 +60,15 @@ from fsspec.asyn import AsyncFileSystem from pydantic_ai import UserContent - from agentpool.agents.base_agent import BaseAgent - from agentpool.common_types import MCPConnectionStatus, MCPServerStatus, PathReference - from agentpool.models.model_configs import AnyModelConfig - from agentpool_server.opencode_server.models import ToolState - from agentpool_server.opencode_server.models.mcp import ( + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.common_types import MCPConnectionStatus, MCPServerStatus, PathReference + from wolfharness.models.model_configs import AnyModelConfig + from wolfharness_server.opencode_server.models import ToolState + from wolfharness_server.opencode_server.models.mcp import ( MCPConnectionStatus as OpenCodeMCPConnectionStatus, ) - from agentpool_server.opencode_server.models.message import PartInput - from agentpool_server.opencode_server.models.parts import ResourceSource + from wolfharness_server.opencode_server.models.message import PartInput + from wolfharness_server.opencode_server.models.parts import ResourceSource logger = log.get_logger(__name__) @@ -141,8 +141,8 @@ async def _resolve_resource( Returns None if the resource is not found. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.resource_resolver import resolve_resource_content + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.resource_resolver import resolve_resource_content scope = Scope( level=ScopeLevel.SESSION, @@ -192,7 +192,7 @@ async def extract_user_prompt_from_parts( Returns: Either a simple string (text-only) or a list of UserContent/PathReference items """ - from agentpool_server.opencode_server.models.parts import ResourceSource + from wolfharness_server.opencode_server.models.parts import ResourceSource result: list[UserContent | PathReference] = [] for part in parts: @@ -242,7 +242,7 @@ def chat_message_to_opencode( # noqa: PLR0915 working_dir: str = "", agent_name: str = "default", model_id: str = "unknown", - provider_id: str = "agentpool", + provider_id: str = "wolfharness", model_variants: dict[str, AnyModelConfig] | None = None, ) -> MessageWithParts: """Convert a ChatMessage to OpenCode MessageWithParts. @@ -284,7 +284,7 @@ def chat_message_to_opencode( # noqa: PLR0915 if isinstance(content, str): text = content else: - from agentpool.agents.native_agent.helpers import _summarize_content_block + from wolfharness.agents.native_agent.helpers import _summarize_content_block text = " ".join(_summarize_content_block(c) for c in content) if text: @@ -298,7 +298,7 @@ def chat_message_to_opencode( # noqa: PLR0915 tokens = Tokens.from_pydantic_ai(msg.usage) if model_variants: - from agentpool_server.shared.model_utils import resolve_model_info_from_response + from wolfharness_server.shared.model_utils import resolve_model_info_from_response resolved_id, resolved_provider = resolve_model_info_from_response( msg.model_name, msg.provider_name, model_variants @@ -618,7 +618,7 @@ def session_data_to_opencode(data: SessionData) -> Session: # sync fix have stored created_at from get_now() (a separate wall- # clock call), which can differ from the session ID's timestamp by # milliseconds — enough to cause sort mismatches in the TUI. - from agentpool.utils.identifiers import extract_timestamp_ms + from wolfharness.utils.identifiers import extract_timestamp_ms id_ts = extract_timestamp_ms(data.session_id) if id_ts is not None: @@ -637,7 +637,7 @@ def session_data_to_opencode(data: SessionData) -> Session: project_id = data.project_id if not project_id or project_id == "default": if data.cwd: - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers project_id = helpers.compute_project_id(data.cwd) else: diff --git a/src/agentpool_server/opencode_server/dependencies.py b/src/wolfharness_server/opencode_server/dependencies.py similarity index 77% rename from src/agentpool_server/opencode_server/dependencies.py rename to src/wolfharness_server/opencode_server/dependencies.py index 09207bcda..99730bf30 100644 --- a/src/agentpool_server/opencode_server/dependencies.py +++ b/src/wolfharness_server/opencode_server/dependencies.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def get_state(request: Request) -> ServerState: @@ -16,7 +16,7 @@ def get_state(request: Request) -> ServerState: The state is stored on app.state.server_state during app creation. """ - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState return cast(ServerState, request.app.state.server_state) diff --git a/src/agentpool_server/opencode_server/event_adapter.py b/src/wolfharness_server/opencode_server/event_adapter.py similarity index 93% rename from src/agentpool_server/opencode_server/event_adapter.py rename to src/wolfharness_server/opencode_server/event_adapter.py index 422545b55..e33a026b5 100644 --- a/src/agentpool_server/opencode_server/event_adapter.py +++ b/src/wolfharness_server/opencode_server/event_adapter.py @@ -33,7 +33,7 @@ Usage:: - from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter + from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter adapter = OpenCodeEventAdapter.from_stream_adapter(stream_adapter) async for oc_event in adapter.convert_stream(agent_stream): @@ -49,11 +49,11 @@ from typing import TYPE_CHECKING, Any -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, # noqa: TC001 ) -from agentpool_server.opencode_server.stream_adapter import ( # noqa: TC001 +from wolfharness_server.opencode_server.stream_adapter import ( # noqa: TC001 OpenCodeStreamAdapter, ) @@ -61,8 +61,8 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.agents.events.events import RichAgentStreamEvent - from agentpool_server.opencode_server.models.events import Event + from wolfharness.agents.events.events import RichAgentStreamEvent + from wolfharness_server.opencode_server.models.events import Event class OpenCodeEventAdapter: diff --git a/src/agentpool_server/opencode_server/event_bridge.py b/src/wolfharness_server/opencode_server/event_bridge.py similarity index 94% rename from src/agentpool_server/opencode_server/event_bridge.py rename to src/wolfharness_server/opencode_server/event_bridge.py index d79252d8b..bf30c93af 100644 --- a/src/agentpool_server/opencode_server/event_bridge.py +++ b/src/wolfharness_server/opencode_server/event_bridge.py @@ -18,14 +18,14 @@ from typing import TYPE_CHECKING, Any -from agentpool.agents.events.events import CustomEvent -from agentpool.log import get_logger +from wolfharness.agents.events.events import CustomEvent +from wolfharness.log import get_logger if TYPE_CHECKING: - from agentpool.orchestrator.core import EventBus - from agentpool_server.opencode_server.models.events import Event - from agentpool_server.opencode_server.state import ServerState + from wolfharness.orchestrator.core import EventBus + from wolfharness_server.opencode_server.models.events import Event + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) diff --git a/src/agentpool_server/opencode_server/event_processor.py b/src/wolfharness_server/opencode_server/event_processor.py similarity index 97% rename from src/agentpool_server/opencode_server/event_processor.py rename to src/wolfharness_server/opencode_server/event_processor.py index 270f2684a..953aac6f8 100644 --- a/src/agentpool_server/opencode_server/event_processor.py +++ b/src/wolfharness_server/opencode_server/event_processor.py @@ -22,7 +22,7 @@ ToolCallPart as PydanticToolCallPart, ) -from agentpool.agents.events import ( +from wolfharness.agents.events import ( DiffContentItem, ElicitationDeferredEvent, FileContentItem, @@ -37,13 +37,13 @@ ToolCallStartEvent, UserMessageInsertedEvent, ) -from agentpool.agents.events.infer_info import derive_rich_tool_info -from agentpool.log import get_logger -from agentpool.utils import identifiers as identifier -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.converters import _convert_params_for_ui -from agentpool_server.opencode_server.models import ( +from wolfharness.agents.events.infer_info import derive_rich_tool_info +from wolfharness.log import get_logger +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.converters import _convert_params_for_ui +from wolfharness_server.opencode_server.models import ( MessageUpdatedEvent, Part, PartDeltaEvent, @@ -57,13 +57,13 @@ # Cross-layer import: McpToolsChangedEvent is an OpenCode SSE event that # EventProcessor creates from core-layer ChangeEvent(kind="tools_changed"). -from agentpool_server.opencode_server.models.events import McpToolsChangedEvent -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.events import McpToolsChangedEvent +from wolfharness_server.opencode_server.models.message import ( AssistantMessage, MessageWithParts, UserMessage, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( FilePart, ReasoningPart, StepFinishPart, @@ -83,15 +83,15 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator, Iterator, Sequence - from agentpool.agents.events import ToolCallContentItem - from agentpool.agents.events.events import RichAgentStreamEvent - from agentpool.messaging import ChatMessage - from agentpool_server.opencode_server.event_processor_context import ( + from wolfharness.agents.events import ToolCallContentItem + from wolfharness.agents.events.events import RichAgentStreamEvent + from wolfharness.messaging import ChatMessage + from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) - from agentpool_server.opencode_server.models.events import Event - from agentpool_server.opencode_server.models.parts import ToolState - from agentpool_server.opencode_server.models.session import SessionStatusType + from wolfharness_server.opencode_server.models.events import Event + from wolfharness_server.opencode_server.models.parts import ToolState + from wolfharness_server.opencode_server.models.session import SessionStatusType logger = get_logger(__name__) @@ -1011,7 +1011,7 @@ def _process_stream_complete( # model), emit a MessageUpdatedEvent so the TUI receives the correction. model_changed = False if isinstance(ctx.assistant_msg.info, AssistantMessage): - from agentpool_server.shared.model_utils import resolve_model_info_from_response + from wolfharness_server.shared.model_utils import resolve_model_info_from_response resolved_model_id, resolved_provider_id = resolve_model_info_from_response( msg.model_name, msg.provider_name, ctx.state.model_variants @@ -1167,7 +1167,7 @@ async def _process_user_message_inserted( user_msg_with_parts.add_text_part(item.content) # Append to session state - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( append_message_to_session, ) @@ -1276,9 +1276,9 @@ def _message_content_to_text(content: Any) -> str: if content is None: return "" if isinstance(content, list): - from agentpool.agents.native_agent.helpers import _summarize_content_block + from wolfharness.agents.native_agent.helpers import _summarize_content_block return " ".join(_summarize_content_block(c) for c in content) - from agentpool.agents.native_agent.helpers import _summarize_content_block + from wolfharness.agents.native_agent.helpers import _summarize_content_block return _summarize_content_block(content) diff --git a/src/agentpool_server/opencode_server/event_processor_context.py b/src/wolfharness_server/opencode_server/event_processor_context.py similarity index 96% rename from src/agentpool_server/opencode_server/event_processor_context.py rename to src/wolfharness_server/opencode_server/event_processor_context.py index d1a561a90..3310556c9 100644 --- a/src/agentpool_server/opencode_server/event_processor_context.py +++ b/src/wolfharness_server/opencode_server/event_processor_context.py @@ -14,13 +14,13 @@ if TYPE_CHECKING: - from agentpool_server.opencode_server.models import MessageWithParts - from agentpool_server.opencode_server.models.parts import ( + from wolfharness_server.opencode_server.models import MessageWithParts + from wolfharness_server.opencode_server.models.parts import ( ReasoningPart, TextPart, ToolPart, ) - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def _model_validate_or_none(model_cls: type[Any], data: Any) -> Any | None: @@ -98,7 +98,7 @@ class EventProcessorContext: is_errored: bool = field(default=False, init=False) def __post_init__(self) -> None: - from agentpool.utils.time_utils import now_ms + from wolfharness.utils.time_utils import now_ms self.stream_start_ms = now_ms() @@ -160,8 +160,12 @@ def deserialize( Raises: ValueError: If required fields are missing from ``data``. """ - from agentpool_server.opencode_server.models import MessageWithParts - from agentpool_server.opencode_server.models.parts import ReasoningPart, TextPart, ToolPart + from wolfharness_server.opencode_server.models import MessageWithParts + from wolfharness_server.opencode_server.models.parts import ( + ReasoningPart, + TextPart, + ToolPart, + ) # --- reconstruct assistant message --- assistant_msg_raw: dict[str, Any] = data.get("assistant_msg", {}) diff --git a/src/agentpool_server/opencode_server/input_provider.py b/src/wolfharness_server/opencode_server/input_provider.py similarity index 96% rename from src/agentpool_server/opencode_server/input_provider.py rename to src/wolfharness_server/opencode_server/input_provider.py index 09e420602..847325bd7 100644 --- a/src/agentpool_server/opencode_server/input_provider.py +++ b/src/wolfharness_server/opencode_server/input_provider.py @@ -8,9 +8,9 @@ from mcp import types -from agentpool.log import get_logger -from agentpool.ui.base import InputProvider -from agentpool_server.opencode_server.models import ( +from wolfharness.log import get_logger +from wolfharness.ui.base import InputProvider +from wolfharness_server.opencode_server.models import ( PermissionAskedProperties, PermissionRequestEvent, PermissionToolInfo, @@ -18,10 +18,10 @@ if TYPE_CHECKING: - from agentpool.agents.context import AgentContext, ConfirmationResult - from agentpool_server.opencode_server.models import PermissionReply - from agentpool_server.opencode_server.models.question import QuestionInfo, QuestionRequest - from agentpool_server.opencode_server.state import ServerState + from wolfharness.agents.context import AgentContext, ConfirmationResult + from wolfharness_server.opencode_server.models import PermissionReply + from wolfharness_server.opencode_server.models.question import QuestionInfo, QuestionRequest + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) @@ -273,7 +273,7 @@ def get_pending_questions(self) -> list[QuestionRequest]: Returns: List of pending question requests. """ - from agentpool_server.opencode_server.models.question import QuestionRequest + from wolfharness_server.opencode_server.models.question import QuestionRequest result: list[QuestionRequest] = [] for question_id, pending in self._pending_questions_dict.items(): @@ -351,9 +351,9 @@ async def _handle_single_enum( Returns: Elicit result with user's answer """ - from agentpool_server.opencode_server.models.events import QuestionAskedEvent - from agentpool_server.opencode_server.models.question import QuestionInfo, QuestionOption - from agentpool_server.opencode_server.state import PendingQuestion + from wolfharness_server.opencode_server.models.events import QuestionAskedEvent + from wolfharness_server.opencode_server.models.question import QuestionInfo, QuestionOption + from wolfharness_server.opencode_server.state import PendingQuestion # Extract enum values match schema: @@ -436,7 +436,7 @@ def _property_to_question(self, key: str, prop_schema: dict[str, Any]) -> Questi Returns: QuestionInfo configured for the property type """ - from agentpool_server.opencode_server.models.question import ( + from wolfharness_server.opencode_server.models.question import ( QuestionInfo, QuestionOption, ) @@ -524,8 +524,8 @@ async def _handle_multi_question( Returns: Elicit result with dict of property names to answers """ - from agentpool_server.opencode_server.models.events import QuestionAskedEvent - from agentpool_server.opencode_server.state import PendingQuestion + from wolfharness_server.opencode_server.models.events import QuestionAskedEvent + from wolfharness_server.opencode_server.state import PendingQuestion max_questions = 10 @@ -638,8 +638,8 @@ async def broadcast_elicitation_question( True if the question was broadcast successfully, False if the schema type is not supported. """ - from agentpool_server.opencode_server.models.events import QuestionAskedEvent - from agentpool_server.opencode_server.state import PendingQuestion + from wolfharness_server.opencode_server.models.events import QuestionAskedEvent + from wolfharness_server.opencode_server.state import PendingQuestion match params: case types.ElicitRequestFormParams( diff --git a/src/agentpool_server/opencode_server/models/__init__.py b/src/wolfharness_server/opencode_server/models/__init__.py similarity index 88% rename from src/agentpool_server/opencode_server/models/__init__.py rename to src/wolfharness_server/opencode_server/models/__init__.py index 69370923e..a0d8f0b0c 100644 --- a/src/agentpool_server/opencode_server/models/__init__.py +++ b/src/wolfharness_server/opencode_server/models/__init__.py @@ -5,8 +5,8 @@ - by_alias=True serialization by default """ -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import ( +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import ( FileDiff, FileDiffStatus, ModelRef, @@ -15,7 +15,7 @@ TokenCache, Tokens, ) -from agentpool_server.opencode_server.models.app import ( +from wolfharness_server.opencode_server.models.app import ( App, AppTimeInfo, DiagnosticResponse, @@ -30,7 +30,7 @@ UpgradeResponse, VcsInfo, ) -from agentpool_server.opencode_server.models.provider import ( +from wolfharness_server.opencode_server.models.provider import ( CostCache, Model, ModelCost, @@ -43,7 +43,7 @@ ProviderListResponse, ProvidersResponse, ) -from agentpool_server.opencode_server.models.session import ( +from wolfharness_server.opencode_server.models.session import ( Session, SessionCreateRequest, SessionForkRequest, @@ -57,7 +57,7 @@ SummarizeRequest, Todo, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( APIError, APIErrorData, AssistantMessage, @@ -92,7 +92,7 @@ UnknownErrorData, UserMessage, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( AgentPart, APIErrorInfo, CompactionPart, @@ -118,7 +118,7 @@ ToolStatePending, ToolStateRunning, ) -from agentpool_server.opencode_server.models.file import ( +from wolfharness_server.opencode_server.models.file import ( FileContent, FileNode, FileStatus, @@ -126,7 +126,7 @@ Symbol, SubmatchInfo, ) -from agentpool_server.opencode_server.models.agent import ( +from wolfharness_server.opencode_server.models.agent import ( Agent, AuthInfo, Command, @@ -140,19 +140,19 @@ WorkspaceCreateRequest, WorkspaceInfo, ) -from agentpool_server.opencode_server.models.diagnostics import ( +from wolfharness_server.opencode_server.models.diagnostics import ( FormatterStatus, Diagnostic, DiagnosticRange, ) -from agentpool_server.opencode_server.models.pty import ( +from wolfharness_server.opencode_server.models.pty import ( PtyCreateRequest, PtyInfo, PtySize, PtyUpdateRequest, ) -from agentpool_server.opencode_server.models.events import ( +from wolfharness_server.opencode_server.models.events import ( CommandExecutedEvent, Event, FileEditedEvent, @@ -204,25 +204,25 @@ SessionUpdatedEvent, TuiSessionSelectEvent, ) -from agentpool_server.opencode_server.models.mcp import ( +from wolfharness_server.opencode_server.models.mcp import ( LogRequest, McpAuthorizationResponse, MCPStatus, McpResource, ) -from agentpool_server.opencode_server.models.config import Config -from agentpool_server.opencode_server.models.question_permission import ( +from wolfharness_server.opencode_server.models.config import Config +from wolfharness_server.opencode_server.models.question_permission import ( OpenCodePendingPermission, OpenCodePendingQuestion, ) -from agentpool_server.opencode_server.models.question import ( +from wolfharness_server.opencode_server.models.question import ( QuestionInfo, QuestionOption, QuestionReply, QuestionRequest, QuestionToolInfo, ) -from agentpool_server.opencode_server.models.session_info import SessionInfo +from wolfharness_server.opencode_server.models.session_info import SessionInfo __all__ = [ "APIError", diff --git a/src/agentpool_server/opencode_server/models/agent.py b/src/wolfharness_server/opencode_server/models/agent.py similarity index 96% rename from src/agentpool_server/opencode_server/models/agent.py rename to src/wolfharness_server/opencode_server/models/agent.py index 17ddfdb75..102847d65 100644 --- a/src/agentpool_server/opencode_server/models/agent.py +++ b/src/wolfharness_server/opencode_server/models/agent.py @@ -6,8 +6,8 @@ from pydantic import Field -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import ModelRef # noqa: TC001 +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import ModelRef # noqa: TC001 PermissionBehavior = Literal["ask", "allow", "deny"] diff --git a/src/agentpool_server/opencode_server/models/app.py b/src/wolfharness_server/opencode_server/models/app.py similarity index 98% rename from src/agentpool_server/opencode_server/models/app.py rename to src/wolfharness_server/opencode_server/models/app.py index bdd06a78c..dfbb3aa6b 100644 --- a/src/agentpool_server/opencode_server/models/app.py +++ b/src/wolfharness_server/opencode_server/models/app.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Any, Self -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel _APP_NAME = "opencode" diff --git a/src/agentpool_server/opencode_server/models/base.py b/src/wolfharness_server/opencode_server/models/base.py similarity index 100% rename from src/agentpool_server/opencode_server/models/base.py rename to src/wolfharness_server/opencode_server/models/base.py diff --git a/src/agentpool_server/opencode_server/models/common.py b/src/wolfharness_server/opencode_server/models/common.py similarity index 95% rename from src/agentpool_server/opencode_server/models/common.py rename to src/wolfharness_server/opencode_server/models/common.py index ff56ac14e..af24c4aa3 100644 --- a/src/agentpool_server/opencode_server/models/common.py +++ b/src/wolfharness_server/opencode_server/models/common.py @@ -6,14 +6,14 @@ from pydantic import Field -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel if TYPE_CHECKING: from pydantic_ai.usage import UsageBase - from agentpool.utils.streams import FileChange + from wolfharness.utils.streams import FileChange FileDiffStatus = Literal["added", "deleted", "modified"] diff --git a/src/agentpool_server/opencode_server/models/config.py b/src/wolfharness_server/opencode_server/models/config.py similarity index 97% rename from src/agentpool_server/opencode_server/models/config.py rename to src/wolfharness_server/opencode_server/models/config.py index 06bf6cdde..9a29f30a4 100644 --- a/src/agentpool_server/opencode_server/models/config.py +++ b/src/wolfharness_server/opencode_server/models/config.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, Field -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel DEFAULT_IGNORE = ["node_modules/**", "__pycache__/**", ".venv/**", "*.pyc", ".mypy_cache/**"] @@ -109,7 +109,7 @@ class Config(OpenCodeBaseModel): watcher: WatcherConfig = Field(default_factory=lambda: WatcherConfig(ignore=DEFAULT_IGNORE)) """File watcher configuration for ignore patterns.""" - # Additional fields OpenCode supports (not typically needed for agentpool): + # Additional fields OpenCode supports (not typically needed for wolfharness): # - $schema: JSON schema reference # - logLevel: Server-side log level # - tui: TUI-specific settings (scrollbar, sidebar width, etc.) diff --git a/src/agentpool_server/opencode_server/models/diagnostics.py b/src/wolfharness_server/opencode_server/models/diagnostics.py similarity index 100% rename from src/agentpool_server/opencode_server/models/diagnostics.py rename to src/wolfharness_server/opencode_server/models/diagnostics.py diff --git a/src/agentpool_server/opencode_server/models/events.py b/src/wolfharness_server/opencode_server/models/events.py similarity index 97% rename from src/agentpool_server/opencode_server/models/events.py rename to src/wolfharness_server/opencode_server/models/events.py index 23c54d6b1..3ce652280 100644 --- a/src/agentpool_server/opencode_server/models/events.py +++ b/src/wolfharness_server/opencode_server/models/events.py @@ -6,17 +6,17 @@ from pydantic import Field -from agentpool_server.opencode_server.models.app import Project # noqa: TC001 -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import FileDiff # noqa: TC001 -from agentpool_server.opencode_server.models.message import MessageInfo # noqa: TC001 -from agentpool_server.opencode_server.models.parts import Part # noqa: TC001 -from agentpool_server.opencode_server.models.pty import PtyInfo # noqa: TC001 -from agentpool_server.opencode_server.models.question import ( # noqa: TC001 +from wolfharness_server.opencode_server.models.app import Project # noqa: TC001 +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import FileDiff # noqa: TC001 +from wolfharness_server.opencode_server.models.message import MessageInfo # noqa: TC001 +from wolfharness_server.opencode_server.models.parts import Part # noqa: TC001 +from wolfharness_server.opencode_server.models.pty import PtyInfo # noqa: TC001 +from wolfharness_server.opencode_server.models.question import ( # noqa: TC001 QuestionInfo, QuestionToolInfo, ) -from agentpool_server.opencode_server.models.session import ( +from wolfharness_server.opencode_server.models.session import ( Session, SessionStatus, SessionStatusType, diff --git a/src/agentpool_server/opencode_server/models/file.py b/src/wolfharness_server/opencode_server/models/file.py similarity index 91% rename from src/agentpool_server/opencode_server/models/file.py rename to src/wolfharness_server/opencode_server/models/file.py index 3d8d309f6..8c935946d 100644 --- a/src/agentpool_server/opencode_server/models/file.py +++ b/src/wolfharness_server/opencode_server/models/file.py @@ -6,8 +6,8 @@ from pydantic import BaseModel, Field -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import FileDiffStatus # noqa: TC001 +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import FileDiffStatus # noqa: TC001 class FileNode(OpenCodeBaseModel): diff --git a/src/agentpool_server/opencode_server/models/mcp.py b/src/wolfharness_server/opencode_server/models/mcp.py similarity index 94% rename from src/agentpool_server/opencode_server/models/mcp.py rename to src/wolfharness_server/opencode_server/models/mcp.py index d8e85f7a4..995de18f2 100644 --- a/src/agentpool_server/opencode_server/models/mcp.py +++ b/src/wolfharness_server/opencode_server/models/mcp.py @@ -4,7 +4,7 @@ from pydantic import Field -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel MCPConnectionStatus = Literal["connected", "disconnected", "error"] diff --git a/src/agentpool_server/opencode_server/models/message.py b/src/wolfharness_server/opencode_server/models/message.py similarity index 98% rename from src/agentpool_server/opencode_server/models/message.py rename to src/wolfharness_server/opencode_server/models/message.py index ab6d86250..995302c28 100644 --- a/src/agentpool_server/opencode_server/models/message.py +++ b/src/wolfharness_server/opencode_server/models/message.py @@ -6,16 +6,16 @@ from pydantic import Field, model_validator -from agentpool.utils import identifiers as identifier -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import ( +from wolfharness.utils import identifiers as identifier +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import ( FileDiff, ModelRef, TextSpan, TimeCreated, Tokens, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( AgentPart, APIErrorInfo, FilePart, diff --git a/src/agentpool_server/opencode_server/models/parts.py b/src/wolfharness_server/opencode_server/models/parts.py similarity index 97% rename from src/agentpool_server/opencode_server/models/parts.py rename to src/wolfharness_server/opencode_server/models/parts.py index 923827c40..2bdcacb4d 100644 --- a/src/agentpool_server/opencode_server/models/parts.py +++ b/src/wolfharness_server/opencode_server/models/parts.py @@ -6,9 +6,9 @@ from pydantic import Field -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import ( +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import ( ModelRef, TextSpan, TimeCreated, diff --git a/src/agentpool_server/opencode_server/models/provider.py b/src/wolfharness_server/opencode_server/models/provider.py similarity index 96% rename from src/agentpool_server/opencode_server/models/provider.py rename to src/wolfharness_server/opencode_server/models/provider.py index 540ae5bfd..835dd81d5 100644 --- a/src/agentpool_server/opencode_server/models/provider.py +++ b/src/wolfharness_server/opencode_server/models/provider.py @@ -7,14 +7,14 @@ from pydantic import Field, model_validator -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import ModelRef # noqa: TC001 +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import ModelRef # noqa: TC001 if TYPE_CHECKING: from tokonomics.model_discovery.model_info import ModelInfo as TokoModelInfo - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities logger = logging.getLogger(__name__) @@ -187,7 +187,7 @@ def from_tokonomics( # Passively populate the CapabilityCache from this ModelInfo. # Zero additional network requests — data is already available. try: - from agentpool.host.stubs import _get_default_cache + from wolfharness.host.stubs import _get_default_cache _get_default_cache().populate_cache_from_model_info(model) except Exception: # noqa: BLE001 diff --git a/src/agentpool_server/opencode_server/models/pty.py b/src/wolfharness_server/opencode_server/models/pty.py similarity index 94% rename from src/agentpool_server/opencode_server/models/pty.py rename to src/wolfharness_server/opencode_server/models/pty.py index 619820654..f326f0e13 100644 --- a/src/agentpool_server/opencode_server/models/pty.py +++ b/src/wolfharness_server/opencode_server/models/pty.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Literal -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel if TYPE_CHECKING: diff --git a/src/agentpool_server/opencode_server/models/question.py b/src/wolfharness_server/opencode_server/models/question.py similarity index 94% rename from src/agentpool_server/opencode_server/models/question.py rename to src/wolfharness_server/opencode_server/models/question.py index 121bd9c09..132460574 100644 --- a/src/agentpool_server/opencode_server/models/question.py +++ b/src/wolfharness_server/opencode_server/models/question.py @@ -4,7 +4,7 @@ from pydantic import Field -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel class QuestionOption(OpenCodeBaseModel): diff --git a/src/agentpool_server/opencode_server/models/question_permission.py b/src/wolfharness_server/opencode_server/models/question_permission.py similarity index 100% rename from src/agentpool_server/opencode_server/models/question_permission.py rename to src/wolfharness_server/opencode_server/models/question_permission.py diff --git a/src/agentpool_server/opencode_server/models/session.py b/src/wolfharness_server/opencode_server/models/session.py similarity index 95% rename from src/agentpool_server/opencode_server/models/session.py rename to src/wolfharness_server/opencode_server/models/session.py index f2084f72d..b779d7d0c 100644 --- a/src/agentpool_server/opencode_server/models/session.py +++ b/src/wolfharness_server/opencode_server/models/session.py @@ -6,8 +6,8 @@ from pydantic import Field -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel -from agentpool_server.opencode_server.models.common import ( # noqa: TC001 +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.common import ( # noqa: TC001 FileDiff, TimeCreatedUpdated, ) diff --git a/src/agentpool_server/opencode_server/models/session_info.py b/src/wolfharness_server/opencode_server/models/session_info.py similarity index 100% rename from src/agentpool_server/opencode_server/models/session_info.py rename to src/wolfharness_server/opencode_server/models/session_info.py diff --git a/src/agentpool_server/opencode_server/models/tool_metadata.py b/src/wolfharness_server/opencode_server/models/tool_metadata.py similarity index 99% rename from src/agentpool_server/opencode_server/models/tool_metadata.py rename to src/wolfharness_server/opencode_server/models/tool_metadata.py index 0f5b07e83..bacfedc0c 100644 --- a/src/agentpool_server/opencode_server/models/tool_metadata.py +++ b/src/wolfharness_server/opencode_server/models/tool_metadata.py @@ -19,7 +19,7 @@ from typing import Any, Literal, NotRequired, TypedDict -from agentpool_server.opencode_server.models.common import FileDiffStatus # noqa: TC001 +from wolfharness_server.opencode_server.models.common import FileDiffStatus # noqa: TC001 ChangeType = Literal["add", "update", "delete", "move"] diff --git a/src/agentpool_server/opencode_server/opencode_event_bridge.py b/src/wolfharness_server/opencode_server/opencode_event_bridge.py similarity index 97% rename from src/agentpool_server/opencode_server/opencode_event_bridge.py rename to src/wolfharness_server/opencode_server/opencode_event_bridge.py index 42620dcd0..578f30e30 100644 --- a/src/agentpool_server/opencode_server/opencode_event_bridge.py +++ b/src/wolfharness_server/opencode_server/opencode_event_bridge.py @@ -12,7 +12,7 @@ import asyncio from typing import TYPE_CHECKING, Any, cast -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( CustomEvent, RunErrorEvent, RunFailedEvent, @@ -21,14 +21,14 @@ StreamCompleteEvent, UserMessageInsertedEvent, ) -from agentpool.log import get_logger -from agentpool.utils import identifiers as identifier -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.log import get_logger +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageAbortedError, MessageAbortedErrorData, @@ -45,10 +45,10 @@ Tokens, UserMessage, ) -from agentpool_server.opencode_server.opencode_message_bridge import ( +from wolfharness_server.opencode_server.opencode_message_bridge import ( append_message_to_session, ) -from agentpool_server.opencode_server.opencode_session_routes import ( +from wolfharness_server.opencode_server.opencode_session_routes import ( ensure_session, set_session_status, ) @@ -57,9 +57,9 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.agents.base_agent import BaseAgent - from agentpool.orchestrator.core import EventBus, EventEnvelope - from agentpool_server.opencode_server.state import ServerState + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.orchestrator.core import EventBus, EventEnvelope + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) @@ -167,7 +167,7 @@ def _create_assistant_message(self, session_id: str) -> tuple[str, MessageWithPa if assistant_msg_id is None: assistant_msg_id = identifier.ascending("message") - agent_name = "agentpool" + agent_name = "wolfharness" model_id, provider_id = self.server_state.resolve_default_model_info() session_state = self.session_pool.sessions.get_session(session_id) if session_state is not None: @@ -186,7 +186,7 @@ def _create_assistant_message(self, session_id: str) -> tuple[str, MessageWithPa if session_state.agent is not None: agent_model_name = cast("BaseAgent[Any, Any]", session_state.agent).model_name model_variants = self.server_state.model_variants - from agentpool_server.shared.model_utils import ( + from wolfharness_server.shared.model_utils import ( _extract_provider, _find_variant_name, ) @@ -266,7 +266,7 @@ async def _persist_assistant_message(self, session_id: str) -> None: RunFailedEvent. This replaces the storage persistence previously done by _wait_and_finalize in the synchronous POST /message path. """ - from agentpool_server.opencode_server.routes.message_routes import ( + from wolfharness_server.opencode_server.routes.message_routes import ( persist_message_to_storage, ) @@ -463,7 +463,7 @@ async def _ensure_child_session_visible( # TUI shows the enriched title (e.g. "Team 'X' · Member (@Y subagent)"). if title is not None and session.title != title: session.title = title - from agentpool_server.opencode_server.models import SessionUpdatedEvent + from wolfharness_server.opencode_server.models import SessionUpdatedEvent await self.server_state.broadcast_event(SessionUpdatedEvent.create(session)) @@ -936,7 +936,7 @@ async def subscribe_to_events(self, session_id: str) -> AsyncIterator[Any]: event_stream = await self.session_pool.event_bus.subscribe(session_id) try: - from agentpool.orchestrator.core import drain_and_merge + from wolfharness.orchestrator.core import drain_and_merge async for event in drain_and_merge(event_stream): async for oc_event in event_adapter.convert_event(event.event): diff --git a/src/agentpool_server/opencode_server/opencode_message_bridge.py b/src/wolfharness_server/opencode_server/opencode_message_bridge.py similarity index 96% rename from src/agentpool_server/opencode_server/opencode_message_bridge.py rename to src/wolfharness_server/opencode_server/opencode_message_bridge.py index f76e8b10d..20a91e139 100644 --- a/src/agentpool_server/opencode_server/opencode_message_bridge.py +++ b/src/wolfharness_server/opencode_server/opencode_message_bridge.py @@ -9,20 +9,20 @@ from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.converters import ( +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.converters import ( chat_message_to_opencode, opencode_to_chat_message, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, PartUpdatedEvent, TimeCreatedUpdated, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( TimeStart, TimeStartEnd, TimeStartEndCompacted, @@ -31,7 +31,7 @@ ToolStateError, ToolStateRunning, ) -from agentpool_server.opencode_server.models.session import Session +from wolfharness_server.opencode_server.models.session import Session # ToolState variants that have a ``metadata`` attribute. @@ -41,8 +41,12 @@ if TYPE_CHECKING: - from agentpool.agents.events.events import RunErrorEvent, SpawnSessionStart, StreamCompleteEvent - from agentpool_server.opencode_server.state import ServerState + from wolfharness.agents.events.events import ( + RunErrorEvent, + SpawnSessionStart, + StreamCompleteEvent, + ) + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) @@ -242,7 +246,7 @@ def _session_state_to_opencode(state: Any) -> Session: Returns: OpenCode Session model. """ - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers created_ms = state.created_at_ns // 1_000_000 updated_ms = state.last_active_at_ns // 1_000_000 @@ -284,9 +288,9 @@ def _reconstruct_tool_parts_from_checkpoint( if not pending_calls: return - from agentpool.utils import identifiers as identifier - from agentpool.utils.time_utils import now_ms - from agentpool_server.opencode_server.models.parts import ( + from wolfharness.utils import identifiers as identifier + from wolfharness.utils.time_utils import now_ms + from wolfharness_server.opencode_server.models.parts import ( TimeStart, ToolPart, ToolStateRunning, @@ -296,9 +300,9 @@ def _reconstruct_tool_parts_from_checkpoint( assistant_msg_id = identifier.ascending("message") # Agent/model propagation: look up the real agent_name from the - # session state instead of hardcoding "agentpool". Falls back to - # "agentpool" when the session state is unavailable. - agent_name = "agentpool" + # session state instead of hardcoding "wolfharness". Falls back to + # "wolfharness" when the session state is unavailable. + agent_name = "wolfharness" pool = state.pool_or_none session_pool = pool.session_pool if pool is not None else None if session_pool is not None: @@ -377,7 +381,7 @@ async def _create_subagent_tool_part( Returns: The created ToolPart, or None if one already exists for this child. """ - from agentpool.utils import identifiers as identifier + from wolfharness.utils import identifiers as identifier # Find the parent session's latest assistant message from in-memory state # (not via get_messages_for_session, which may return copies when diff --git a/src/agentpool_server/opencode_server/opencode_session_routes.py b/src/wolfharness_server/opencode_server/opencode_session_routes.py similarity index 95% rename from src/agentpool_server/opencode_server/opencode_session_routes.py rename to src/wolfharness_server/opencode_server/opencode_session_routes.py index 7dae20180..5dbb0274a 100644 --- a/src/agentpool_server/opencode_server/opencode_session_routes.py +++ b/src/wolfharness_server/opencode_server/opencode_session_routes.py @@ -11,26 +11,26 @@ import asyncio from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool.utils.identifiers import extract_timestamp_ms -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from wolfharness.log import get_logger +from wolfharness.utils.identifiers import extract_timestamp_ms +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( SessionCreatedEvent, SessionErrorEvent, SessionStatus, SessionStatusEvent, TimeCreatedUpdated, ) -from agentpool_server.opencode_server.opencode_message_bridge import ( +from wolfharness_server.opencode_server.opencode_message_bridge import ( _reconstruct_tool_parts_from_checkpoint, ) if TYPE_CHECKING: - from agentpool.orchestrator.core import SessionPool - from agentpool.sessions.models import SessionData - from agentpool_server.opencode_server.models.session import Session - from agentpool_server.opencode_server.state import ServerState + from wolfharness.orchestrator.core import SessionPool + from wolfharness.sessions.models import SessionData + from wolfharness_server.opencode_server.models.session import Session + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) @@ -116,8 +116,8 @@ async def ensure_session( Returns: The Session object (existing or newly created) """ - from agentpool_server.opencode_server.converters import session_data_to_opencode - from agentpool_server.opencode_server.models import SessionUpdatedEvent + from wolfharness_server.opencode_server.converters import session_data_to_opencode + from wolfharness_server.opencode_server.models import SessionUpdatedEvent # --- Fast path: already in memory ----------------------------------- if session_id in state.sessions: @@ -169,7 +169,7 @@ async def ensure_session( if sp_session is not None: sp_session.input_provider = input_provider - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( SessionCreatedEvent, ) @@ -205,13 +205,13 @@ async def _create_and_persist_session( Returns: The newly created and persisted ``Session``. """ - from agentpool_server.opencode_server.converters import opencode_to_session_data - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.converters import opencode_to_session_data + from wolfharness_server.opencode_server.models import ( Session, SessionCreatedEvent, SessionUpdatedEvent, ) - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers now = extract_timestamp_ms(session_id) or now_ms() if parent_id is not None: @@ -377,7 +377,7 @@ async def create_session( Returns: The session state from the SessionPool. """ - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( _session_state_to_opencode, ) @@ -514,7 +514,7 @@ async def route_message( self._pending_message_ids[session_id] = pending_id # Store model metadata so _before_consumer_loop can propagate the # real agent/model onto the assistant message instead of hardcoding - # "agentpool"/"default"/"agentpool". + # "wolfharness"/"default"/"wolfharness". self._pending_message_metadata[session_id] = { "message_id": message_id, "model_id": model_id, @@ -536,7 +536,7 @@ async def route_message( if session_state is not None: session_state.input_provider = input_provider - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode delivery_mode = DeliveryMode.STEER if priority == "asap" else DeliveryMode.QUEUE return await self.session_pool.send_message( diff --git a/src/agentpool_server/opencode_server/provider_auth.py b/src/wolfharness_server/opencode_server/provider_auth.py similarity index 98% rename from src/agentpool_server/opencode_server/provider_auth.py rename to src/wolfharness_server/opencode_server/provider_auth.py index 8ea15338e..a172a9991 100644 --- a/src/agentpool_server/opencode_server/provider_auth.py +++ b/src/wolfharness_server/opencode_server/provider_auth.py @@ -13,7 +13,7 @@ import httpx -from agentpool.auth.anthropic_auth import ( +from wolfharness.auth.anthropic_auth import ( OAUTH_MANUAL_REDIRECT_URI, AnthropicOAuthToken, AnthropicTokenStore, @@ -21,14 +21,14 @@ exchange_code_for_token, generate_pkce, ) -from agentpool_server.opencode_server.models.agent import ( +from wolfharness_server.opencode_server.models.agent import ( ProviderAuthAuthorization, ProviderAuthMethod, ) if TYPE_CHECKING: - from agentpool_server.opencode_server.models.agent import AuthInfo + from wolfharness_server.opencode_server.models.agent import AuthInfo class ProviderAuthBackend(ABC): diff --git a/src/wolfharness_server/opencode_server/routes/__init__.py b/src/wolfharness_server/opencode_server/routes/__init__.py new file mode 100644 index 000000000..4b86bdba7 --- /dev/null +++ b/src/wolfharness_server/opencode_server/routes/__init__.py @@ -0,0 +1,29 @@ +"""Route handlers for the OpenCode API.""" + +from wolfharness_server.opencode_server.routes.global_routes import router as global_router +from wolfharness_server.opencode_server.routes.app_routes import router as app_router +from wolfharness_server.opencode_server.routes.config_routes import router as config_router +from wolfharness_server.opencode_server.routes.session_routes import router as session_router +from wolfharness_server.opencode_server.routes.message_routes import router as message_router +from wolfharness_server.opencode_server.routes.file_routes import router as file_router +from wolfharness_server.opencode_server.routes.agent_routes import router as agent_router +from wolfharness_server.opencode_server.routes.permission_routes import router as permission_router +from wolfharness_server.opencode_server.routes.question_routes import router as question_router +from wolfharness_server.opencode_server.routes.pty_routes import router as pty_router +from wolfharness_server.opencode_server.routes.tui_routes import router as tui_router +from wolfharness_server.opencode_server.routes.lsp_routes import router as lsp_router + +__all__ = [ + "agent_router", + "app_router", + "config_router", + "file_router", + "global_router", + "lsp_router", + "message_router", + "permission_router", + "pty_router", + "question_router", + "session_router", + "tui_router", +] diff --git a/src/agentpool_server/opencode_server/routes/agent_routes.py b/src/wolfharness_server/opencode_server/routes/agent_routes.py similarity index 95% rename from src/agentpool_server/opencode_server/routes/agent_routes.py rename to src/wolfharness_server/opencode_server/routes/agent_routes.py index cab2e8ba7..07184542b 100644 --- a/src/agentpool_server/opencode_server/routes/agent_routes.py +++ b/src/wolfharness_server/opencode_server/routes/agent_routes.py @@ -9,17 +9,17 @@ from fastapi import APIRouter, HTTPException from pydantic import BaseModel, HttpUrl -from agentpool.capabilities.combined_toolset import CombinedToolsetCapability -from agentpool.log import get_logger -from agentpool.mcp_server.manager import MCPManager -from agentpool_config.mcp_server import ( +from wolfharness.capabilities.combined_toolset import CombinedToolsetCapability +from wolfharness.log import get_logger +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_config.mcp_server import ( SSEMCPServerConfig, StdioMCPServerConfig, StreamableHTTPMCPServerConfig, ) -from agentpool_server.opencode_server.converters import to_mcp_status -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.converters import to_mcp_status +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( Agent, AuthInfo, Command, @@ -102,7 +102,7 @@ def _find_mcp_manager(state: Any, session_id: str | None = None) -> MCPManager | state: Server state session_id: Optional session ID for scoped capability lookup. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel agent = state.agent host_ctx = agent.host_context @@ -500,8 +500,8 @@ async def list_mcp_resources(state: StateDep) -> dict[str, McpResource]: result: dict[str, McpResource] = {} import asyncio - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.resource_protocols import ResourceAccess + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.resource_protocols import ResourceAccess agent = state.agent host_ctx = agent.host_context @@ -551,7 +551,7 @@ async def list_mcp_resources(state: StateDep) -> dict[str, McpResource]: @router.post("/experimental/worktree") async def create_worktree(request: WorktreeCreateRequest, state: StateDep) -> WorktreeInfo: """Create a new git worktree for isolated agent work.""" - from agentpool.utils.worktree import create_worktree + from wolfharness.utils.worktree import create_worktree repo_dir = state.agent.env.cwd or state.working_dir try: @@ -564,7 +564,7 @@ async def create_worktree(request: WorktreeCreateRequest, state: StateDep) -> Wo @router.get("/experimental/worktree") async def list_worktrees(state: StateDep) -> list[str]: """List all sandbox worktree directories.""" - from agentpool.utils.worktree import list_worktrees + from wolfharness.utils.worktree import list_worktrees repo_dir = state.agent.env.cwd or state.working_dir return await list_worktrees(repo_dir) @@ -573,7 +573,7 @@ async def list_worktrees(state: StateDep) -> list[str]: @router.delete("/experimental/worktree") async def remove_worktree(request: WorktreeRemoveRequest, state: StateDep) -> bool: """Remove a git worktree and delete its branch.""" - from agentpool.utils.worktree import remove_worktree + from wolfharness.utils.worktree import remove_worktree repo_dir = state.agent.env.cwd or state.working_dir try: @@ -586,7 +586,7 @@ async def remove_worktree(request: WorktreeRemoveRequest, state: StateDep) -> bo @router.post("/experimental/worktree/reset") async def reset_worktree(request: WorktreeResetRequest, state: StateDep) -> bool: """Reset a worktree branch to the primary default branch.""" - from agentpool.utils.worktree import reset_worktree + from wolfharness.utils.worktree import reset_worktree repo_dir = state.agent.env.cwd or state.working_dir try: @@ -598,7 +598,7 @@ async def reset_worktree(request: WorktreeResetRequest, state: StateDep) -> bool async def _get_project_id(state: StateDep) -> str: """Return the current OpenCode project ID for compatibility responses.""" - from agentpool_storage.project_store import ProjectStore + from wolfharness_storage.project_store import ProjectStore project = await ProjectStore(state.storage).get_or_create(state.working_dir) return project.project_id @@ -626,7 +626,7 @@ def _workspace_from_worktree( @router.get("/experimental/workspace") async def list_workspaces(state: StateDep) -> list[WorkspaceInfo]: """List worktree-backed workspaces for OpenCode TUI compatibility.""" - from agentpool.utils.worktree import list_worktrees + from wolfharness.utils.worktree import list_worktrees repo_dir = state.agent.env.cwd or state.working_dir project_id = await _get_project_id(state) @@ -639,7 +639,7 @@ async def list_workspaces(state: StateDep) -> list[WorkspaceInfo]: @router.post("/experimental/workspace") async def create_workspace(request: WorkspaceCreateRequest, state: StateDep) -> WorkspaceInfo: """Create a worktree-backed workspace through OpenCode's workspace API.""" - from agentpool.utils.worktree import create_worktree + from wolfharness.utils.worktree import create_worktree if request.type != "worktree": raise HTTPException(status_code=400, detail="Only worktree workspaces are supported") @@ -660,7 +660,7 @@ async def create_workspace(request: WorkspaceCreateRequest, state: StateDep) -> @router.delete("/experimental/workspace/{workspace_id}") async def remove_workspace(workspace_id: str, state: StateDep) -> WorkspaceInfo | None: """Remove a worktree-backed workspace by OpenCode workspace ID.""" - from agentpool.utils.worktree import list_worktrees, remove_worktree + from wolfharness.utils.worktree import list_worktrees, remove_worktree repo_dir = state.agent.env.cwd or state.working_dir project_id = await _get_project_id(state) @@ -694,7 +694,7 @@ async def list_sessions_global( Supports pagination via cursor (timestamp-based). """ - from agentpool_server.opencode_server.converters import session_data_to_opencode + from wolfharness_server.opencode_server.converters import session_data_to_opencode effective_limit = limit or 100 sessions: list[Session] = [] diff --git a/src/agentpool_server/opencode_server/routes/app_routes.py b/src/wolfharness_server/opencode_server/routes/app_routes.py similarity index 95% rename from src/agentpool_server/opencode_server/routes/app_routes.py rename to src/wolfharness_server/opencode_server/routes/app_routes.py index e5d5113f8..2a8a1b08d 100644 --- a/src/agentpool_server/opencode_server/routes/app_routes.py +++ b/src/wolfharness_server/opencode_server/routes/app_routes.py @@ -9,9 +9,9 @@ import anyenv from fastapi import APIRouter, HTTPException -from agentpool.utils.time_utils import datetime_to_ms -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness.utils.time_utils import datetime_to_ms +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( App, AppTimeInfo, PathInfo, @@ -22,12 +22,12 @@ ProjectUpdateRequest, VcsInfo, ) -from agentpool_storage.opencode_provider import helpers -from agentpool_storage.project_store import ProjectStore +from wolfharness_storage.opencode_provider import helpers +from wolfharness_storage.project_store import ProjectStore if TYPE_CHECKING: - from agentpool.sessions.models import ProjectData + from wolfharness.sessions.models import ProjectData router = APIRouter(tags=["app"]) diff --git a/src/agentpool_server/opencode_server/routes/config_routes.py b/src/wolfharness_server/opencode_server/routes/config_routes.py similarity index 97% rename from src/agentpool_server/opencode_server/routes/config_routes.py rename to src/wolfharness_server/opencode_server/routes/config_routes.py index 791f57ebc..c4306bf7f 100644 --- a/src/agentpool_server/opencode_server/routes/config_routes.py +++ b/src/wolfharness_server/opencode_server/routes/config_routes.py @@ -10,10 +10,10 @@ from fastapi import APIRouter -from agentpool.log import get_logger -from agentpool.models.manifest import AgentsManifest -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness.log import get_logger +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( Config, Mode, Model, @@ -24,13 +24,13 @@ ProviderListResponse, ProvidersResponse, ) -from agentpool_server.shared.constants import ( +from wolfharness_server.shared.constants import ( DEFAULT_MODEL_CONTEXT_LIMIT, DEFAULT_MODEL_INPUT_COST, DEFAULT_MODEL_OUTPUT_COST, DEFAULT_MODEL_OUTPUT_LIMIT, ) -from agentpool_server.shared.model_utils import ( +from wolfharness_server.shared.model_utils import ( _build_providers_from_tokonomics, _extract_provider, ) @@ -306,7 +306,7 @@ def _infer_default_model(state: StateDep) -> str | None: # agent_cfg.model may be a raw str or a structured config (e.g. # StringModelConfig with identifier). The identifier itself can be # either a variant name ("glm47") or a full model id ("openai-chat:svc/glm-4.7"). - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig if not isinstance(agent_cfg, NativeAgentConfig): return None @@ -340,7 +340,7 @@ def _infer_default_model(state: StateDep) -> str | None: @router.get("/config") async def get_config(state: StateDep) -> Config: """Get server configuration.""" - from agentpool_server.opencode_server.models.config import Keybinds, WatcherConfig + from wolfharness_server.opencode_server.models.config import Keybinds, WatcherConfig # Initialize config if not yet set if state.config is None: diff --git a/src/agentpool_server/opencode_server/routes/file_routes.py b/src/wolfharness_server/opencode_server/routes/file_routes.py similarity index 98% rename from src/agentpool_server/opencode_server/routes/file_routes.py rename to src/wolfharness_server/opencode_server/routes/file_routes.py index 199484e3c..82058c898 100644 --- a/src/agentpool_server/opencode_server/routes/file_routes.py +++ b/src/wolfharness_server/opencode_server/routes/file_routes.py @@ -11,8 +11,8 @@ from fastapi import APIRouter, HTTPException, Query import ripgrep_rs -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( FileContent, FileNode, FindMatch, diff --git a/src/agentpool_server/opencode_server/routes/global_routes.py b/src/wolfharness_server/opencode_server/routes/global_routes.py similarity index 96% rename from src/agentpool_server/opencode_server/routes/global_routes.py rename to src/wolfharness_server/opencode_server/routes/global_routes.py index 6667ef47b..e09d2ab91 100644 --- a/src/agentpool_server/opencode_server/routes/global_routes.py +++ b/src/wolfharness_server/opencode_server/routes/global_routes.py @@ -11,16 +11,16 @@ from fastapi import APIRouter, Query from sse_starlette.sse import EventSourceResponse -from agentpool import log -from agentpool.agents.events.events import CustomEvent -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import Event, GlobalEvent, HealthResponse -from agentpool_server.opencode_server.models.app import ( +from wolfharness import log +from wolfharness.agents.events.events import CustomEvent +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import Event, GlobalEvent, HealthResponse +from wolfharness_server.opencode_server.models.app import ( DiagnosticResponse, DisposeResponse, UpgradeResponse, ) -from agentpool_server.opencode_server.models.events import ( +from wolfharness_server.opencode_server.models.events import ( MessageUpdatedEvent, PartUpdatedEvent, ServerConnectedEvent, @@ -30,7 +30,7 @@ SessionIdProperties, SessionUpdatedEvent, ) -from agentpool_server.opencode_server.routes.routing import ( +from wolfharness_server.opencode_server.routes.routing import ( RoutingCheckResponse, tui_event_filter, ) @@ -39,7 +39,7 @@ if TYPE_CHECKING: from collections.abc import AsyncGenerator - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState logger = log.get_logger(__name__) @@ -280,7 +280,7 @@ async def _event_generator( # noqa: PLR0915 except asyncio.QueueShutDown: break - from agentpool.orchestrator.core import EventEnvelope + from wolfharness.orchestrator.core import EventEnvelope inner_event: Any inner_event = raw_event.event if isinstance(raw_event, EventEnvelope) else raw_event diff --git a/src/agentpool_server/opencode_server/routes/lsp_routes.py b/src/wolfharness_server/opencode_server/routes/lsp_routes.py similarity index 98% rename from src/agentpool_server/opencode_server/routes/lsp_routes.py rename to src/wolfharness_server/opencode_server/routes/lsp_routes.py index ef1d7d86b..653d341d8 100644 --- a/src/agentpool_server/opencode_server/routes/lsp_routes.py +++ b/src/wolfharness_server/opencode_server/routes/lsp_routes.py @@ -11,8 +11,8 @@ from fastapi import APIRouter, HTTPException, Query -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( Diagnostic, DiagnosticRange, FormatterStatus, diff --git a/src/agentpool_server/opencode_server/routes/message_routes.py b/src/wolfharness_server/opencode_server/routes/message_routes.py similarity index 97% rename from src/agentpool_server/opencode_server/routes/message_routes.py rename to src/wolfharness_server/opencode_server/routes/message_routes.py index 15bee8aa3..02cee0412 100644 --- a/src/agentpool_server/opencode_server/routes/message_routes.py +++ b/src/wolfharness_server/opencode_server/routes/message_routes.py @@ -9,15 +9,15 @@ from fastapi import APIRouter, HTTPException, Query, status -from agentpool.log import get_logger -from agentpool.utils import identifiers as identifier -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.converters import ( +from wolfharness.log import get_logger +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.converters import ( extract_user_prompt_from_parts, opencode_to_chat_message, ) -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( AgentPartInput, AssistantMessage, FilePartInput, @@ -40,13 +40,13 @@ Tokens, UserMessage, ) -from agentpool_server.opencode_server.routes.session_routes import get_or_load_session -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session +from wolfharness_server.opencode_server.session_pool_integration import ( append_message_to_session, get_messages_for_session, set_session_status, ) -from agentpool_server.opencode_server.stream_adapter import OpenCodeStreamAdapter +from wolfharness_server.opencode_server.stream_adapter import OpenCodeStreamAdapter if TYPE_CHECKING: @@ -54,13 +54,13 @@ from pydantic_ai import UserContent - from agentpool.common_types import PathReference - from agentpool.messaging import ChatMessage - from agentpool.orchestrator.session_pool import SessionPool - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness.common_types import PathReference + from wolfharness.messaging import ChatMessage + from wolfharness.orchestrator.session_pool import SessionPool + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) @@ -516,7 +516,7 @@ async def _route_message_locked( # noqa: PLR0915 session_id=session_id, parent_id=user_msg_id, model_id=request.model.model_id if request.model else "default", # ty: ignore[invalid-argument-type] - provider_id=request.model.provider_id if request.model else "agentpool", # ty: ignore[invalid-argument-type] + provider_id=request.model.provider_id if request.model else "wolfharness", # ty: ignore[invalid-argument-type] mode=agent_name, agent=agent_name, path=MessagePath(cwd=state.working_dir, root=state.working_dir), @@ -693,7 +693,7 @@ async def _route_message_locked( # noqa: PLR0915 # Build meta from parts — the EventBus event carries this so the # EventProcessor can reconstruct and broadcast the full user message # as the sole publication point. - from agentpool_server.opencode_server.event_processor import ( + from wolfharness_server.opencode_server.event_processor import ( OpenCodeUserMessageMeta, ) @@ -713,7 +713,7 @@ async def _route_message_locked( # noqa: PLR0915 meta=route_meta, ) else: - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode delivery_mode = DeliveryMode.STEER if delivery_priority == "asap" else DeliveryMode.QUEUE message_id = await session_pool.send_message( @@ -1025,7 +1025,7 @@ async def send_message_async(session_id: str, request: MessageRequest, state: St # Serialize parts for meta — the EventBus event carries this so # the EventProcessor can reconstruct and broadcast the full user # message (parts + message) as the sole publication point. - from agentpool_server.opencode_server.event_processor import ( + from wolfharness_server.opencode_server.event_processor import ( OpenCodeUserMessageMeta, ) @@ -1071,7 +1071,7 @@ async def send_message_async(session_id: str, request: MessageRequest, state: St ) sp_state.input_provider = input_provider - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode delivery_mode = ( DeliveryMode.STEER if delivery_priority == "asap" else DeliveryMode.QUEUE diff --git a/src/agentpool_server/opencode_server/routes/permission_routes.py b/src/wolfharness_server/opencode_server/routes/permission_routes.py similarity index 93% rename from src/agentpool_server/opencode_server/routes/permission_routes.py rename to src/wolfharness_server/opencode_server/routes/permission_routes.py index f853c0ebb..ae73ffc32 100644 --- a/src/agentpool_server/opencode_server/routes/permission_routes.py +++ b/src/wolfharness_server/opencode_server/routes/permission_routes.py @@ -4,9 +4,9 @@ from fastapi import APIRouter, HTTPException -from agentpool import log -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness import log +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( PermissionAskedProperties, PermissionReplyRequest, PermissionResolvedEvent, diff --git a/src/agentpool_server/opencode_server/routes/pty_routes.py b/src/wolfharness_server/opencode_server/routes/pty_routes.py similarity index 97% rename from src/agentpool_server/opencode_server/routes/pty_routes.py rename to src/wolfharness_server/opencode_server/routes/pty_routes.py index b957c22e0..1e07d881d 100644 --- a/src/agentpool_server/opencode_server/routes/pty_routes.py +++ b/src/wolfharness_server/opencode_server/routes/pty_routes.py @@ -12,9 +12,9 @@ from fastapi import APIRouter, HTTPException, WebSocket, WebSocketDisconnect -from agentpool import log -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models import ( +from wolfharness import log +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models import ( PtyCreatedEvent, PtyCreateRequest, PtyDeletedEvent, @@ -28,7 +28,7 @@ if TYPE_CHECKING: from exxec.pty_manager import PtyManagerProtocol - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState logger = log.get_logger(__name__) router = APIRouter(prefix="/pty", tags=["pty"]) diff --git a/src/agentpool_server/opencode_server/routes/question_routes.py b/src/wolfharness_server/opencode_server/routes/question_routes.py similarity index 97% rename from src/agentpool_server/opencode_server/routes/question_routes.py rename to src/wolfharness_server/opencode_server/routes/question_routes.py index c0a025397..218a42264 100644 --- a/src/agentpool_server/opencode_server/routes/question_routes.py +++ b/src/wolfharness_server/opencode_server/routes/question_routes.py @@ -6,9 +6,9 @@ from fastapi import APIRouter, HTTPException -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider +from wolfharness_server.opencode_server.models import ( PermissionReply, PermissionResolvedEvent, QuestionRejectedEvent, diff --git a/src/agentpool_server/opencode_server/routes/routing.py b/src/wolfharness_server/opencode_server/routes/routing.py similarity index 93% rename from src/agentpool_server/opencode_server/routes/routing.py rename to src/wolfharness_server/opencode_server/routes/routing.py index 6096c28b6..ef1c6a27a 100644 --- a/src/agentpool_server/opencode_server/routes/routing.py +++ b/src/wolfharness_server/opencode_server/routes/routing.py @@ -14,11 +14,11 @@ from typing import TYPE_CHECKING, Literal -from agentpool_server.opencode_server.models.base import OpenCodeBaseModel +from wolfharness_server.opencode_server.models.base import OpenCodeBaseModel if TYPE_CHECKING: - from agentpool_server.opencode_server.models.events import GlobalEvent + from wolfharness_server.opencode_server.models.events import GlobalEvent RoutingReason = Literal[ diff --git a/src/agentpool_server/opencode_server/routes/session_routes.py b/src/wolfharness_server/opencode_server/routes/session_routes.py similarity index 98% rename from src/agentpool_server/opencode_server/routes/session_routes.py rename to src/wolfharness_server/opencode_server/routes/session_routes.py index c59f75165..72c5ee4b0 100644 --- a/src/agentpool_server/opencode_server/routes/session_routes.py +++ b/src/wolfharness_server/opencode_server/routes/session_routes.py @@ -13,20 +13,20 @@ from pydantic_ai import FileUrl from slashed import CommandContext -from agentpool.log import get_logger -from agentpool.repomap import RepoMap, find_src_files -from agentpool.utils import identifiers as identifier -from agentpool.utils.identifiers import extract_timestamp_ms -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.command_validation import validate_command -from agentpool_server.opencode_server.converters import ( +from wolfharness.log import get_logger +from wolfharness.repomap import RepoMap, find_src_files +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.identifiers import extract_timestamp_ms +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.command_validation import validate_command +from wolfharness_server.opencode_server.converters import ( chat_message_to_opencode, opencode_to_session_data, session_data_to_opencode, ) -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider +from wolfharness_server.opencode_server.models import ( AssistantMessage, CommandExecutedEvent, CommandRequest, @@ -64,22 +64,22 @@ Todo, Tokens, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( append_message_to_session, get_messages_for_session, get_session_status as _get_single_session_status, set_messages_for_session, set_session_status, ) -from agentpool_server.opencode_server.stream_adapter import OpenCodeStreamAdapter -from agentpool_server.opencode_server.todo_utils import build_opencode_todos -from agentpool_storage.opencode_provider import helpers -from agentpool_storage.protocols import SessionPersistence +from wolfharness_server.opencode_server.stream_adapter import OpenCodeStreamAdapter +from wolfharness_server.opencode_server.todo_utils import build_opencode_todos +from wolfharness_storage.opencode_provider import helpers +from wolfharness_storage.protocols import SessionPersistence if TYPE_CHECKING: - from agentpool.sessions.models import SessionData - from agentpool_server.opencode_server.state import ServerState + from wolfharness.sessions.models import SessionData + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) @@ -456,7 +456,7 @@ async def _execute_skill_command( # TextPart parts allows the EventProcessor to reconstruct the user # message from meta.parts, displaying the raw command in the TUI. # This mirrors the normal path (message_routes.py:700-701). - from agentpool_server.opencode_server.event_processor import ( + from wolfharness_server.opencode_server.event_processor import ( OpenCodeUserMessageMeta, ) @@ -516,7 +516,7 @@ async def _execute_skill_command( # test setups without integration. Note: send_message does not # support assistant_msg_id, so the SSE-delivered ID may diverge # from the HTTP placeholder in this path (cosmetic mismatch only). - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode await session_pool.send_message( session_id=session_id, @@ -1254,7 +1254,7 @@ async def fork_session( # noqa: D417 if session_pool is not None: fork_agent = await session_pool.sessions.get_or_create_session_agent(new_session_id) fork_agent.conversation.chat_messages.clear() - from agentpool_server.opencode_server.converters import opencode_to_chat_message + from wolfharness_server.opencode_server.converters import opencode_to_chat_message for msg_with_parts in copied_messages: chat_msg = opencode_to_chat_message(msg_with_parts, session_id=new_session_id) @@ -1614,8 +1614,8 @@ async def summarize_session( # noqa: PLR0915 TextPartDelta, ) - from agentpool.agents.events import StreamCompleteEvent, UserMessageInsertedEvent - from agentpool.messaging.compaction import compact_conversation, summarizing_context + from wolfharness.agents.events import StreamCompleteEvent, UserMessageInsertedEvent + from wolfharness.messaging.compaction import compact_conversation, summarizing_context session = await get_or_load_session(state, session_id) if session is None: @@ -1957,7 +1957,7 @@ async def revert_session(session_id: str, request: RevertRequest, state: StateDe the old ``FileOpsTracker.reverted_changes`` are cleared before the new STAGE overwrites the marker. """ - from agentpool_server.opencode_server.models import MessageRemovedEvent, PartRemovedEvent + from wolfharness_server.opencode_server.models import MessageRemovedEvent, PartRemovedEvent # 3.1: Ensure session is idle before proceeding. await _ensure_session_idle(state, session_id) diff --git a/src/agentpool_server/opencode_server/routes/tui_routes.py b/src/wolfharness_server/opencode_server/routes/tui_routes.py similarity index 97% rename from src/agentpool_server/opencode_server/routes/tui_routes.py rename to src/wolfharness_server/opencode_server/routes/tui_routes.py index b1dd26b38..d31993f0b 100644 --- a/src/agentpool_server/opencode_server/routes/tui_routes.py +++ b/src/wolfharness_server/opencode_server/routes/tui_routes.py @@ -11,8 +11,8 @@ from fastapi import APIRouter from pydantic import BaseModel, Field -from agentpool_server.opencode_server.dependencies import StateDep -from agentpool_server.opencode_server.models.events import ( +from wolfharness_server.opencode_server.dependencies import StateDep +from wolfharness_server.opencode_server.models.events import ( TuiCommandExecuteEvent, TuiCommandExecuteProperties, TuiPromptAppendEvent, diff --git a/src/agentpool_server/opencode_server/server.py b/src/wolfharness_server/opencode_server/server.py similarity index 93% rename from src/agentpool_server/opencode_server/server.py rename to src/wolfharness_server/opencode_server/server.py index 293a0905c..3a25686e8 100644 --- a/src/agentpool_server/opencode_server/server.py +++ b/src/wolfharness_server/opencode_server/server.py @@ -18,8 +18,8 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse, RedirectResponse, Response -from agentpool import log -from agentpool_server.opencode_server.routes import ( +from wolfharness import log +from wolfharness_server.opencode_server.routes import ( agent_router, app_router, config_router, @@ -33,8 +33,8 @@ session_router, tui_router, ) -from agentpool_server.opencode_server.state import ServerState -from agentpool_server.opencode_server.todo_utils import build_opencode_todos +from wolfharness_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.todo_utils import build_opencode_todos if TYPE_CHECKING: @@ -42,9 +42,9 @@ from httpx import Headers - from agentpool.agents.base_agent import BaseAgent - from agentpool.storage.manager import SessionMetadataGeneratedEvent - from agentpool.utils.todos import TodoTracker + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.storage.manager import SessionMetadataGeneratedEvent + from wolfharness.utils.todos import TodoTracker VERSION = "0.1.0" @@ -65,7 +65,7 @@ def render(self, content: Any) -> bytes: return super().render(jsonable_encoder(content, exclude_none=True)) -async def check_pypi_version(package: str = "agentpool") -> str | None: +async def check_pypi_version(package: str = "wolfharness") -> str | None: """Check PyPI for the latest version of a package. Args: @@ -131,7 +131,7 @@ def create_app(*, agent: BaseAgent[Any, Any], working_dir: str | None = None) -> # Set up SessionPool integration for session-scoped event consumption if state.pool.session_pool is not None: - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -146,8 +146,8 @@ def create_app(*, agent: BaseAgent[Any, Any], working_dir: str | None = None) -> if state.pool.skills is not None: from slashed import CommandStore - from agentpool.skills.command import SkillCommand - from agentpool_server.opencode_server.skill_bridge import OpenCodeSkillBridge + from wolfharness.skills.command import SkillCommand + from wolfharness_server.opencode_server.skill_bridge import OpenCodeSkillBridge def _build_skill_commands() -> list[SkillCommand]: """Build SkillCommand list from current pool skills.""" @@ -206,7 +206,7 @@ def _sync_command_store( # kind="skills_changed" when capabilities emit change notifications. extension_registry = state.pool.extension_registry if extension_registry is not None: - from agentpool.capabilities.extension_registry import ( + from wolfharness.capabilities.extension_registry import ( Scope, ScopeLevel, ) @@ -242,7 +242,7 @@ async def _watch_skill_changes() -> None: # when an MCP server's tool list changes (via notifications/tools/list_changed). # This watcher converts that to McpToolsChangedEvent and broadcasts it # so connected OpenCode clients can refresh their tool lists. - from agentpool_server.opencode_server.event_processor import EventProcessor + from wolfharness_server.opencode_server.event_processor import EventProcessor processor = EventProcessor() @@ -271,7 +271,7 @@ async def _watch_mcp_tool_changes() -> None: # Set up todo change callback to broadcast events async def on_todo_change(tracker: TodoTracker) -> None: """Broadcast todo updates to all active sessions.""" - from agentpool_server.opencode_server.models.events import Todo, TodoUpdatedEvent + from wolfharness_server.opencode_server.models.events import Todo, TodoUpdatedEvent # Convert tracker entries to OpenCode Todo models. todos = build_opencode_todos(tracker, Todo) @@ -285,8 +285,8 @@ async def on_todo_change(tracker: TodoTracker) -> None: async def on_title_generated(event: SessionMetadataGeneratedEvent) -> None: """Update session when metadata is generated by StorageManager.""" - from agentpool_server.opencode_server.converters import opencode_to_session_data - from agentpool_server.opencode_server.models.events import SessionUpdatedEvent + from wolfharness_server.opencode_server.converters import opencode_to_session_data + from wolfharness_server.opencode_server.models.events import SessionUpdatedEvent logger.info("on_title_generated called", session_id=event.session_id, data=event.metadata) session_id = event.session_id @@ -322,8 +322,8 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: # noqa: PLR0915 from watchfiles import Change - from agentpool.utils.file_watcher import FileWatcher, GitBranchWatcher - from agentpool_server.opencode_server.models import ( + from wolfharness.utils.file_watcher import FileWatcher, GitBranchWatcher + from wolfharness_server.opencode_server.models import ( FileWatcherUpdatedEvent, VcsBranchUpdatedEvent, ) @@ -389,15 +389,15 @@ async def on_file_change(changes: AbstractSet[tuple[Change, str]]) -> None: # --- Version update check (triggered when first client connects) --- async def check_for_updates() -> None: """Check PyPI for updates and notify via toast.""" - from agentpool import __version__ as current_version - from agentpool_server.opencode_server.models.events import TuiToastShowEvent + from wolfharness import __version__ as current_version + from wolfharness_server.opencode_server.models.events import TuiToastShowEvent - latest = await check_pypi_version("agentpool") + latest = await check_pypi_version("wolfharness") if latest and compare_versions(current_version, latest): logger.info("Update available", current_version=current_version, latest=latest) event = TuiToastShowEvent.create( title="Update Available", - message=f"agentpool {latest} is available (current: {current_version})", + message=f"wolfharness {latest} is available (current: {current_version})", variant="info", duration=10000, ) @@ -639,7 +639,7 @@ def run_server( if __name__ == "__main__": import asyncio - from agentpool import AgentPool, config_resources + from wolfharness import AgentPool, config_resources async def main() -> None: pool = AgentPool(config_resources.ACP_ASSISTANT) diff --git a/src/agentpool_server/opencode_server/session_pool_integration.py b/src/wolfharness_server/opencode_server/session_pool_integration.py similarity index 84% rename from src/agentpool_server/opencode_server/session_pool_integration.py rename to src/wolfharness_server/opencode_server/session_pool_integration.py index 0df508493..b3d2e1ec8 100644 --- a/src/agentpool_server/opencode_server/session_pool_integration.py +++ b/src/wolfharness_server/opencode_server/session_pool_integration.py @@ -17,17 +17,17 @@ from typing import TYPE_CHECKING, Any -from agentpool_server.mixins import ProtocolEventConsumerMixin -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.mixins import ProtocolEventConsumerMixin +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) -from agentpool_server.opencode_server.opencode_message_bridge import ( +from wolfharness_server.opencode_server.opencode_message_bridge import ( OpenCodeMessageBridgeMixin, append_message_to_session, get_messages_for_session, set_messages_for_session, ) -from agentpool_server.opencode_server.opencode_session_routes import ( +from wolfharness_server.opencode_server.opencode_session_routes import ( OpenCodeSessionRoutesMixin, ensure_session, get_session_status, @@ -36,13 +36,13 @@ if TYPE_CHECKING: - from agentpool.agents.events.events import SpawnSessionStart - from agentpool.orchestrator.core import SessionPool - from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter - from agentpool_server.opencode_server.event_processor_context import ( + from wolfharness.agents.events.events import SpawnSessionStart + from wolfharness.orchestrator.core import SessionPool + from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter + from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState __all__ = [ diff --git a/src/agentpool_server/opencode_server/skill_bridge.py b/src/wolfharness_server/opencode_server/skill_bridge.py similarity index 98% rename from src/agentpool_server/opencode_server/skill_bridge.py rename to src/wolfharness_server/opencode_server/skill_bridge.py index cb75800d0..7e6e4a178 100644 --- a/src/agentpool_server/opencode_server/skill_bridge.py +++ b/src/wolfharness_server/opencode_server/skill_bridge.py @@ -9,7 +9,7 @@ import logfire from slashed import Command as SlashedCommand, CommandContext -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) @@ -17,7 +17,7 @@ if TYPE_CHECKING: from collections.abc import Callable - from agentpool.skills.command import SkillCommand + from wolfharness.skills.command import SkillCommand class SkillCommandWrapper: diff --git a/src/agentpool_server/opencode_server/state.py b/src/wolfharness_server/opencode_server/state.py similarity index 89% rename from src/agentpool_server/opencode_server/state.py rename to src/wolfharness_server/opencode_server/state.py index d175a75db..5676e965a 100644 --- a/src/agentpool_server/opencode_server/state.py +++ b/src/wolfharness_server/opencode_server/state.py @@ -9,14 +9,14 @@ import time from typing import TYPE_CHECKING, Any -from agentpool import log -from agentpool.diagnostics.lsp_manager import LSPManager -from agentpool_server.opencode_server.models import SessionStatus -from agentpool_server.opencode_server.provider_auth import ( +from wolfharness import log +from wolfharness.diagnostics.lsp_manager import LSPManager +from wolfharness_server.opencode_server.models import SessionStatus +from wolfharness_server.opencode_server.provider_auth import ( ProviderAuthService, create_default_auth_service, ) -from agentpool_storage.opencode_provider import helpers +from wolfharness_storage.opencode_provider import helpers logger = log.get_logger(__name__) @@ -25,21 +25,21 @@ from fsspec.asyn import AsyncFileSystem from slashed import CommandStore - from agentpool.agents.base_agent import BaseAgent - from agentpool.delegation import AgentPool - from agentpool.models.model_configs import AnyModelConfig - from agentpool.orchestrator.core import SessionController - from agentpool.storage import StorageManager - from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider - from agentpool_server.opencode_server.models import ( + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.delegation import AgentPool + from wolfharness.models.model_configs import AnyModelConfig + from wolfharness.orchestrator.core import SessionController + from wolfharness.storage import StorageManager + from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider + from wolfharness_server.opencode_server.models import ( Config, Event, MessageWithParts, QuestionInfo, Session, ) - from agentpool_server.opencode_server.models.question import QuestionToolInfo - from agentpool_server.opencode_server.routes.global_routes import GlobalEventFactory + from wolfharness_server.opencode_server.models.question import QuestionToolInfo + from wolfharness_server.opencode_server.routes.global_routes import GlobalEventFactory # Type alias for async callback OnFirstSubscriberCallback = Callable[[], Coroutine[Any, Any, None]] @@ -102,7 +102,7 @@ def parse_model_info(model_name: str | None) -> tuple[str, str]: Splits on the first colon (e.g. ``"openai:gpt-4o"`` → ``("gpt-4o", "openai")``). Falls back to ``("default", - "agentpool")`` when the model name is ``None`` or missing a + "wolfharness")`` when the model name is ``None`` or missing a provider prefix. Args: @@ -114,7 +114,7 @@ def parse_model_info(model_name: str | None) -> tuple[str, str]: if model_name and ":" in model_name: provider, model = model_name.split(":", 1) return model, provider - return "default", "agentpool" + return "default", "wolfharness" @property def model_variants(self) -> dict[str, AnyModelConfig]: @@ -134,7 +134,7 @@ def resolve_default_model_info(self) -> tuple[str, str]: name as model_id and configured provider as provider_id. 2. Parses ``self.agent.model_name`` (e.g. ``"openai:gpt-4o"``) by splitting on the first colon. - 3. Falls back to ``("default", "agentpool")``. + 3. Falls back to ``("default", "wolfharness")``. Returns: Tuple of ``(model_id, provider_id)``. @@ -144,7 +144,7 @@ def resolve_default_model_info(self) -> tuple[str, str]: if agent_model and self._pool is not None: manifest = self._pool.manifest if manifest and manifest.model_variants: - from agentpool_server.shared.model_utils import ( + from wolfharness_server.shared.model_utils import ( _extract_provider, _find_variant_name, ) @@ -191,7 +191,7 @@ def __post_init__(self) -> None: event_bus = getattr(session_pool, "event_bus", None) if event_bus is not None: - from agentpool_server.opencode_server.event_bridge import ( + from wolfharness_server.opencode_server.event_bridge import ( OpenCodeEventBridge, ) @@ -204,7 +204,7 @@ def get_event_factory(self) -> GlobalEventFactory: and computed project ID, then cached for the server's lifetime. Imports GlobalEventFactory locally to avoid circular imports. """ - from agentpool_server.opencode_server.routes.global_routes import GlobalEventFactory + from wolfharness_server.opencode_server.routes.global_routes import GlobalEventFactory if self._event_factory is None: directory = self.base_path @@ -307,7 +307,7 @@ def ensure_input_provider(self, session_id: str) -> OpenCodeInputProvider: Stores the provider on SessionState (via SessionController) when available. """ - from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider + from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider input_provider = None if self.session_controller is not None: @@ -386,8 +386,8 @@ async def broadcast_event(self, event: Event) -> None: async def mark_session_idle(self, session_id: str) -> None: """Mark a session idle and broadcast the matching status events.""" - from agentpool_server.opencode_server.models import SessionIdleEvent, SessionStatusEvent - from agentpool_server.opencode_server.session_pool_integration import set_session_status + from wolfharness_server.opencode_server.models import SessionIdleEvent, SessionStatusEvent + from wolfharness_server.opencode_server.session_pool_integration import set_session_status status = SessionStatus(type="idle") await set_session_status(self, session_id, status) @@ -396,6 +396,6 @@ async def mark_session_idle(self, session_id: str) -> None: async def emit_session_turn_complete(self, session_id: str) -> None: """Broadcast the per-turn completion signal without changing busy state.""" - from agentpool_server.opencode_server.models import SessionIdleEvent + from wolfharness_server.opencode_server.models import SessionIdleEvent await self.broadcast_event(SessionIdleEvent.create(session_id)) diff --git a/src/agentpool_server/opencode_server/stream_adapter.py b/src/wolfharness_server/opencode_server/stream_adapter.py similarity index 93% rename from src/agentpool_server/opencode_server/stream_adapter.py rename to src/wolfharness_server/opencode_server/stream_adapter.py index 323b58a53..96c71d574 100644 --- a/src/agentpool_server/opencode_server/stream_adapter.py +++ b/src/wolfharness_server/opencode_server/stream_adapter.py @@ -13,22 +13,22 @@ from pydantic_ai import RequestUsage -from agentpool.log import get_logger -from agentpool.tasks.exceptions import RunAbortedError -from agentpool.utils import identifiers as identifier -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.log import get_logger +from wolfharness.tasks.exceptions import RunAbortedError +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessageWithParts, PartUpdatedEvent, SessionErrorEvent, TokenCache, Tokens, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( StepFinishPart, TextPart, TimeStartEndOptional, @@ -38,10 +38,10 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator, Callable, Iterator - from agentpool.agents.events.events import RichAgentStreamEvent - from agentpool_server.opencode_server.models import MessageWithParts - from agentpool_server.opencode_server.models.events import Event - from agentpool_server.opencode_server.state import ServerState + from wolfharness.agents.events.events import RichAgentStreamEvent + from wolfharness_server.opencode_server.models import MessageWithParts + from wolfharness_server.opencode_server.models.events import Event + from wolfharness_server.opencode_server.state import ServerState logger = get_logger(__name__) diff --git a/src/agentpool_server/opencode_server/todo_utils.py b/src/wolfharness_server/opencode_server/todo_utils.py similarity index 91% rename from src/agentpool_server/opencode_server/todo_utils.py rename to src/wolfharness_server/opencode_server/todo_utils.py index 584136a25..6d04a9381 100644 --- a/src/agentpool_server/opencode_server/todo_utils.py +++ b/src/wolfharness_server/opencode_server/todo_utils.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: - from agentpool.utils.todos import TodoPriority, TodoStatus, TodoTracker + from wolfharness.utils.todos import TodoPriority, TodoStatus, TodoTracker class TodoModelFactory[TodoModelT](Protocol): diff --git a/src/agentpool_server/py.typed b/src/wolfharness_server/py.typed similarity index 100% rename from src/agentpool_server/py.typed rename to src/wolfharness_server/py.typed diff --git a/src/agentpool_server/shared/__init__.py b/src/wolfharness_server/shared/__init__.py similarity index 87% rename from src/agentpool_server/shared/__init__.py rename to src/wolfharness_server/shared/__init__.py index 0cd98ad33..dd24bf0cf 100644 --- a/src/agentpool_server/shared/__init__.py +++ b/src/wolfharness_server/shared/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from agentpool_server.shared.constants import ( +from wolfharness_server.shared.constants import ( DEFAULT_MODEL_CONTEXT_LIMIT, DEFAULT_MODEL_INPUT_COST, DEFAULT_MODEL_OUTPUT_COST, diff --git a/src/agentpool_server/shared/constants.py b/src/wolfharness_server/shared/constants.py similarity index 100% rename from src/agentpool_server/shared/constants.py rename to src/wolfharness_server/shared/constants.py diff --git a/src/agentpool_server/shared/model_utils.py b/src/wolfharness_server/shared/model_utils.py similarity index 95% rename from src/agentpool_server/shared/model_utils.py rename to src/wolfharness_server/shared/model_utils.py index 380db4c61..8a661000b 100644 --- a/src/agentpool_server/shared/model_utils.py +++ b/src/wolfharness_server/shared/model_utils.py @@ -9,8 +9,8 @@ from typing import TYPE_CHECKING, Any -from agentpool.log import get_logger -from agentpool.models.model_configs import ( +from wolfharness.log import get_logger +from wolfharness.models.model_configs import ( AnthropicModelConfig, AnyModelConfig, FallbackModelConfig, @@ -18,7 +18,7 @@ OpenAIModelConfig, StringModelConfig, ) -from agentpool_server.shared.constants import ( +from wolfharness_server.shared.constants import ( DEFAULT_MODEL_CONTEXT_LIMIT, DEFAULT_MODEL_INPUT_COST, DEFAULT_MODEL_OUTPUT_COST, @@ -30,9 +30,9 @@ from tokonomics.model_discovery.model_info import ModelInfo as TokoModelInfo from acp.schema import SessionModelState - from agentpool.agents.base_agent import BaseAgent - from agentpool_server.acp_server.provider_router import ProviderRouter - from agentpool_server.opencode_server.models import Provider + from wolfharness.agents.base_agent import BaseAgent + from wolfharness_server.acp_server.provider_router import ProviderRouter + from wolfharness_server.opencode_server.models import Provider logger = get_logger(__name__) @@ -169,7 +169,7 @@ def resolve_model_info_from_response( variant name, and returns the variant name + configured provider. When no variant matches (or inputs are ``None``), falls back to raw names - with ``"unknown"`` / ``"agentpool"`` defaults for ``None`` values. + with ``"unknown"`` / ``"wolfharness"`` defaults for ``None`` values. Args: model_name: Raw model name from ``result.response.model_name`` @@ -188,7 +188,7 @@ def resolve_model_info_from_response( config = model_variants[variant_name] return variant_name, _extract_provider(config) logger.debug("No variant match for %s", full_id) - return model_name or "unknown", provider_name or "agentpool" + return model_name or "unknown", provider_name or "wolfharness" def _build_providers_from_tokonomics(toko_models: list[TokoModelInfo]) -> list[Provider]: @@ -203,7 +203,7 @@ def _build_providers_from_tokonomics(toko_models: list[TokoModelInfo]) -> list[P Returns: List of Provider objects with models converted using Model.from_tokonomics(). """ - from agentpool_server.opencode_server.models import Model, Provider + from wolfharness_server.opencode_server.models import Model, Provider providers_by_name: dict[str, Provider] = {} @@ -246,7 +246,7 @@ def _apply_configured_variants( This function modifies the providers list in place. New providers are created if a configured variant references a non-existent provider. """ - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Model, ModelCost, ModelLimit, diff --git a/src/agentpool_storage/__init__.py b/src/wolfharness_storage/__init__.py similarity index 76% rename from src/agentpool_storage/__init__.py rename to src/wolfharness_storage/__init__.py index 9cfc526bb..9054e0788 100644 --- a/src/agentpool_storage/__init__.py +++ b/src/wolfharness_storage/__init__.py @@ -1,15 +1,15 @@ """Storage provider package.""" -from agentpool_storage.adapter import StorageProviderAdapter -from agentpool_storage.base import StorageProvider -from agentpool_storage.project_store import ( +from wolfharness_storage.adapter import StorageProviderAdapter +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.project_store import ( ProjectStore, detect_project_root, discover_config_path, generate_project_id, resolve_config, ) -from agentpool_storage.protocols import ( +from wolfharness_storage.protocols import ( CheckpointStore, CommandLog, MessagePersistence, diff --git a/src/agentpool_storage/acp_provider/__init__.py b/src/wolfharness_storage/acp_provider/__init__.py similarity index 59% rename from src/agentpool_storage/acp_provider/__init__.py rename to src/wolfharness_storage/acp_provider/__init__.py index 14c5460b8..2d2651d63 100644 --- a/src/agentpool_storage/acp_provider/__init__.py +++ b/src/wolfharness_storage/acp_provider/__init__.py @@ -1,5 +1,5 @@ """ACP storage provider - read-only session access via ACP protocol.""" -from agentpool_storage.acp_provider.provider import ACPStorageProvider +from wolfharness_storage.acp_provider.provider import ACPStorageProvider __all__ = ["ACPStorageProvider"] diff --git a/src/agentpool_storage/acp_provider/provider.py b/src/wolfharness_storage/acp_provider/provider.py similarity index 91% rename from src/agentpool_storage/acp_provider/provider.py rename to src/wolfharness_storage/acp_provider/provider.py index 981c5ea14..e3b2888ba 100644 --- a/src/agentpool_storage/acp_provider/provider.py +++ b/src/wolfharness_storage/acp_provider/provider.py @@ -4,18 +4,18 @@ from typing import TYPE_CHECKING -from agentpool.log import get_logger -from agentpool.utils.time_utils import get_now, parse_iso_timestamp -from agentpool_config.storage import ACPStorageConfig -from agentpool_storage.base import StorageProvider -from agentpool_storage.models import ConversationData +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import get_now, parse_iso_timestamp +from wolfharness_config.storage import ACPStorageConfig +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.models import ConversationData if TYPE_CHECKING: from datetime import datetime from acp.client.connection import ClientSideConnection - from agentpool_storage.models import QueryFilters + from wolfharness_storage.models import QueryFilters logger = get_logger(__name__) diff --git a/src/agentpool_storage/adapter.py b/src/wolfharness_storage/adapter.py similarity index 95% rename from src/agentpool_storage/adapter.py rename to src/wolfharness_storage/adapter.py index c6e015c9c..1ee4ebd6e 100644 --- a/src/agentpool_storage/adapter.py +++ b/src/wolfharness_storage/adapter.py @@ -2,7 +2,7 @@ ``StorageProviderAdapter`` delegates every method to the wrapped ``StorageProvider`` instance, making it pass ``isinstance`` checks for all -seven Protocols defined in ``agentpool_storage.protocols``. +seven Protocols defined in ``wolfharness_storage.protocols``. This enables gradual migration: consumers depend on the narrow Protocol they need, while the underlying storage remains a single ``StorageProvider`` @@ -18,12 +18,12 @@ from collections.abc import Sequence from datetime import datetime - from agentpool.common_types import JsonValue - from agentpool.messaging import ChatMessage, TokenCost - from agentpool.sessions.models import ProjectData, SessionData - from agentpool_config.session import SessionQuery - from agentpool_storage.base import StorageProvider - from agentpool_storage.models import ConversationData, QueryFilters, StatsFilters + from wolfharness.common_types import JsonValue + from wolfharness.messaging import ChatMessage, TokenCost + from wolfharness.sessions.models import ProjectData, SessionData + from wolfharness_config.session import SessionQuery + from wolfharness_storage.base import StorageProvider + from wolfharness_storage.models import ConversationData, QueryFilters, StatsFilters class StorageProviderAdapter: diff --git a/src/agentpool_storage/base.py b/src/wolfharness_storage/base.py similarity index 98% rename from src/agentpool_storage/base.py rename to src/wolfharness_storage/base.py index 4eaacd217..904617dba 100644 --- a/src/agentpool_storage/base.py +++ b/src/wolfharness_storage/base.py @@ -12,12 +12,12 @@ from datetime import datetime from types import TracebackType - from agentpool.common_types import JsonValue - from agentpool.messaging import ChatMessage, TokenCost - from agentpool.sessions.models import ProjectData, SessionData - from agentpool_config.session import SessionQuery - from agentpool_config.storage import BaseStorageProviderConfig - from agentpool_storage.models import ConversationData, QueryFilters, StatsFilters + from wolfharness.common_types import JsonValue + from wolfharness.messaging import ChatMessage, TokenCost + from wolfharness.sessions.models import ProjectData, SessionData + from wolfharness_config.session import SessionQuery + from wolfharness_config.storage import BaseStorageProviderConfig + from wolfharness_storage.models import ConversationData, QueryFilters, StatsFilters class StoredMessage: diff --git a/src/wolfharness_storage/file_provider/__init__.py b/src/wolfharness_storage/file_provider/__init__.py new file mode 100644 index 000000000..dd8af7754 --- /dev/null +++ b/src/wolfharness_storage/file_provider/__init__.py @@ -0,0 +1,7 @@ +"""File provider for wolfharness.""" + +from __future__ import annotations + +from wolfharness_storage.file_provider.provider import FileProvider + +__all__ = ["FileProvider"] diff --git a/src/agentpool_storage/file_provider/provider.py b/src/wolfharness_storage/file_provider/provider.py similarity index 96% rename from src/agentpool_storage/file_provider/provider.py rename to src/wolfharness_storage/file_provider/provider.py index 59a296532..267c4ad92 100644 --- a/src/agentpool_storage/file_provider/provider.py +++ b/src/wolfharness_storage/file_provider/provider.py @@ -9,14 +9,14 @@ from pydantic_ai.usage import RunUsage from upathtools import to_upath -from agentpool.common_types import JsonValue, MessageRole -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage, TokenCost -from agentpool.storage import deserialize_messages -from agentpool.utils.time_utils import get_now, parse_iso_timestamp -from agentpool_config.storage import FileStorageConfig -from agentpool_storage.base import StorageProvider -from agentpool_storage.models import TokenUsage +from wolfharness.common_types import JsonValue, MessageRole +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage, TokenCost +from wolfharness.storage import deserialize_messages +from wolfharness.utils.time_utils import get_now, parse_iso_timestamp +from wolfharness_config.storage import FileStorageConfig +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.models import TokenUsage if TYPE_CHECKING: @@ -24,8 +24,8 @@ from yamling import FormatType - from agentpool.sessions.models import ProjectData - from agentpool_config.session import SessionQuery + from wolfharness.sessions.models import ProjectData + from wolfharness_config.session import SessionQuery logger = get_logger(__name__) @@ -196,7 +196,7 @@ async def filter_messages(self, query: SessionQuery) -> list[ChatMessage[str]]: async def log_message(self, *, message: ChatMessage[Any]) -> None: """Log a new message.""" - from agentpool.storage.serialization import serialize_messages + from wolfharness.storage.serialization import serialize_messages cost_info = message.cost_info self._data["messages"].append({ @@ -526,7 +526,7 @@ async def delete_session_messages(self, session_id: str) -> int: def _to_project_data(self, data: ProjectDataDict) -> ProjectData: """Convert dict to ProjectData.""" - from agentpool.sessions.models import ProjectData + from wolfharness.sessions.models import ProjectData return ProjectData( project_id=data["project_id"], diff --git a/src/agentpool_storage/formatters.py b/src/wolfharness_storage/formatters.py similarity index 98% rename from src/agentpool_storage/formatters.py rename to src/wolfharness_storage/formatters.py index c4d3c91f8..8bcf12d9d 100644 --- a/src/agentpool_storage/formatters.py +++ b/src/wolfharness_storage/formatters.py @@ -9,7 +9,7 @@ if TYPE_CHECKING: - from agentpool_storage.models import ConversationData + from wolfharness_storage.models import ConversationData def is_conversation_data(data: Any) -> TypeGuard[ConversationData]: diff --git a/src/wolfharness_storage/memory_provider/__init__.py b/src/wolfharness_storage/memory_provider/__init__.py new file mode 100644 index 000000000..1d303687a --- /dev/null +++ b/src/wolfharness_storage/memory_provider/__init__.py @@ -0,0 +1,7 @@ +"""Memory provider for wolfharness.""" + +from __future__ import annotations + +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider + +__all__ = ["MemoryStorageProvider"] diff --git a/src/agentpool_storage/memory_provider/provider.py b/src/wolfharness_storage/memory_provider/provider.py similarity index 97% rename from src/agentpool_storage/memory_provider/provider.py rename to src/wolfharness_storage/memory_provider/provider.py index 0268a98c0..7d8364078 100644 --- a/src/agentpool_storage/memory_provider/provider.py +++ b/src/wolfharness_storage/memory_provider/provider.py @@ -4,21 +4,21 @@ from typing import TYPE_CHECKING, Any -from agentpool.utils.time_utils import get_now, parse_iso_timestamp -from agentpool_config.storage import MemoryStorageConfig -from agentpool_storage.base import StorageProvider -from agentpool_storage.models import ConversationData +from wolfharness.utils.time_utils import get_now, parse_iso_timestamp +from wolfharness_config.storage import MemoryStorageConfig +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.models import ConversationData if TYPE_CHECKING: from collections.abc import Sequence from datetime import datetime - from agentpool.common_types import JsonValue - from agentpool.messaging import ChatMessage - from agentpool.sessions.models import ProjectData, SessionData - from agentpool_config.session import SessionQuery - from agentpool_storage.models import QueryFilters, StatsFilters, TokenUsage + from wolfharness.common_types import JsonValue + from wolfharness.messaging import ChatMessage + from wolfharness.sessions.models import ProjectData, SessionData + from wolfharness_config.session import SessionQuery + from wolfharness_storage.models import QueryFilters, StatsFilters, TokenUsage class MemoryStorageProvider(StorageProvider): diff --git a/src/agentpool_storage/models.py b/src/wolfharness_storage/models.py similarity index 97% rename from src/agentpool_storage/models.py rename to src/wolfharness_storage/models.py index 0488507d5..a3f1869b5 100644 --- a/src/agentpool_storage/models.py +++ b/src/wolfharness_storage/models.py @@ -9,7 +9,7 @@ if TYPE_CHECKING: from datetime import datetime - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage GroupBy = Literal["agent", "model", "hour", "day"] diff --git a/src/agentpool_storage/opencode_provider/ARCHITECTURE.md b/src/wolfharness_storage/opencode_provider/ARCHITECTURE.md similarity index 98% rename from src/agentpool_storage/opencode_provider/ARCHITECTURE.md rename to src/wolfharness_storage/opencode_provider/ARCHITECTURE.md index 43fe4177b..1cbb5a7ae 100644 --- a/src/agentpool_storage/opencode_provider/ARCHITECTURE.md +++ b/src/wolfharness_storage/opencode_provider/ARCHITECTURE.md @@ -53,7 +53,7 @@ The random portion appears to be a timestamp-based identifier. Example: ```bash -echo -n "/home/phil65/dev/oss/agentpool" | sha1sum +echo -n "/home/phil65/dev/oss/wolfharness" | sha1sum # → 486ce75a8fddd4372018ab816ac62d8004dc52fd ``` @@ -74,7 +74,7 @@ echo -n "/home/phil65/dev/oss/agentpool" | sha1sum "id": "ses_4afbda00cffeVl5YERm4op7JEG", "version": "1.0.193", "projectID": "486ce75a8fddd4372018ab816ac62d8004dc52fd", - "directory": "/home/phil65/dev/oss/agentpool", + "directory": "/home/phil65/dev/oss/wolfharness", "title": "Claude capabilities overview", "time": { "created": 1766578085876, @@ -237,7 +237,7 @@ for msg_file in messages: - Retrieve messages in chronological order 4. **Format Conversion** - - OpenCode format → `ChatMessage` (for agentpool) + - OpenCode format → `ChatMessage` (for wolfharness) - `ChatMessage` → OpenCode format (for persistence) ### Challenges diff --git a/src/agentpool_storage/opencode_provider/__init__.py b/src/wolfharness_storage/opencode_provider/__init__.py similarity index 85% rename from src/agentpool_storage/opencode_provider/__init__.py rename to src/wolfharness_storage/opencode_provider/__init__.py index 162053da9..057f6d830 100644 --- a/src/agentpool_storage/opencode_provider/__init__.py +++ b/src/wolfharness_storage/opencode_provider/__init__.py @@ -13,7 +13,7 @@ from __future__ import annotations -from agentpool_storage.opencode_provider.provider import OpenCodeStorageProvider +from wolfharness_storage.opencode_provider.provider import OpenCodeStorageProvider __all__ = [ "OpenCodeStorageProvider", diff --git a/src/agentpool_storage/opencode_provider/helpers.py b/src/wolfharness_storage/opencode_provider/helpers.py similarity index 96% rename from src/agentpool_storage/opencode_provider/helpers.py rename to src/wolfharness_storage/opencode_provider/helpers.py index 0e18dcc8e..3e5ef04ee 100644 --- a/src/agentpool_storage/opencode_provider/helpers.py +++ b/src/wolfharness_storage/opencode_provider/helpers.py @@ -28,17 +28,17 @@ UserPromptPart, ) -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage, TokenCost -from agentpool.utils import identifiers as identifier -from agentpool.utils.pydantic_ai_helpers import to_user_content -from agentpool.utils.time_utils import ms_to_datetime -from agentpool_server.opencode_server.models.message import ( +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage, TokenCost +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.pydantic_ai_helpers import to_user_content +from wolfharness.utils.time_utils import ms_to_datetime +from wolfharness_server.opencode_server.models.message import ( AssistantMessage, MessageInfo, UserMessage, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( FilePart, Part, ReasoningPart, @@ -46,7 +46,7 @@ ToolPart, ToolStateCompleted, ) -from agentpool_server.opencode_server.models.session import Session +from wolfharness_server.opencode_server.models.session import Session if TYPE_CHECKING: @@ -75,7 +75,7 @@ def compute_project_id(directory: str) -> str: !!! note "Dual ID scheme" This is DIFFERENT from ``generate_project_id()`` in - ``agentpool_storage.project_store``, which hashes the canonical worktree + ``wolfharness_storage.project_store``, which hashes the canonical worktree path for AgentPool-internal ``ProjectData`` identification. The two IDs serve distinct purposes and are not interchangeable: - ``compute_project_id`` — git root commit SHA1 (OpenCode session layout) diff --git a/src/agentpool_storage/opencode_provider/provider.py b/src/wolfharness_storage/opencode_provider/provider.py similarity index 97% rename from src/agentpool_storage/opencode_provider/provider.py rename to src/wolfharness_storage/opencode_provider/provider.py index 11d44687c..cbc048e0e 100644 --- a/src/agentpool_storage/opencode_provider/provider.py +++ b/src/wolfharness_storage/opencode_provider/provider.py @@ -32,13 +32,18 @@ UserPromptPart, ) -from agentpool.log import get_logger -from agentpool.sessions.models import ProjectData, SessionData -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict -from agentpool.utils.thread_helpers import run_in_thread -from agentpool.utils.time_utils import datetime_to_ms, get_now, ms_to_datetime, parse_iso_timestamp -from agentpool_config.storage import OpenCodeStorageConfig -from agentpool_server.opencode_server.models import ( +from wolfharness.log import get_logger +from wolfharness.sessions.models import ProjectData, SessionData +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.utils.thread_helpers import run_in_thread +from wolfharness.utils.time_utils import ( + datetime_to_ms, + get_now, + ms_to_datetime, + parse_iso_timestamp, +) +from wolfharness_config.storage import OpenCodeStorageConfig +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageInfo as OpenCodeMessage, Part as OpenCodePart, @@ -46,15 +51,15 @@ SessionSummary, TimeCreatedUpdated, ) -from agentpool_storage.base import StorageProvider -from agentpool_storage.models import ConversationData as ConvData, TokenUsage -from agentpool_storage.opencode_provider import helpers +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.models import ConversationData as ConvData, TokenUsage +from wolfharness_storage.opencode_provider import helpers if TYPE_CHECKING: - from agentpool.messaging import ChatMessage, TokenCost - from agentpool_config.session import SessionQuery - from agentpool_storage.models import QueryFilters, StatsFilters + from wolfharness.messaging import ChatMessage, TokenCost + from wolfharness_config.session import SessionQuery + from wolfharness_storage.models import QueryFilters, StatsFilters logger = get_logger(__name__) @@ -306,7 +311,7 @@ async def _write_message( # noqa: PLR0915 finish_reason: Any | None = None, ) -> None: """Write a message in OpenCode format.""" - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -338,7 +343,7 @@ async def _write_message( # noqa: PLR0915 session_id=session_id, parent_id=parent_id or "", model_id=model or "", - provider_id=model.split(":")[0] if model else "agentpool", + provider_id=model.split(":")[0] if model else "wolfharness", path=MessagePath(cwd=str(Path.cwd()), root=str(Path.cwd())), time=MessageTime(created=now_ms), tokens=Tokens( @@ -1301,8 +1306,8 @@ async def get_filtered_conversations( compact: Only show first/last message of each conversation include_tokens: Include token usage statistics """ - from agentpool.utils.parse_time import parse_time_period - from agentpool.utils.time_utils import get_now + from wolfharness.utils.parse_time import parse_time_period + from wolfharness.utils.time_utils import get_now cutoff: datetime | None = None if period: @@ -1396,7 +1401,7 @@ async def list_session_ids( import asyncio import datetime as dt - from agentpool_storage.models import QueryFilters, StatsFilters + from wolfharness_storage.models import QueryFilters, StatsFilters async def main() -> None: provider = OpenCodeStorageProvider() diff --git a/src/agentpool_storage/project_store.py b/src/wolfharness_storage/project_store.py similarity index 93% rename from src/agentpool_storage/project_store.py rename to src/wolfharness_storage/project_store.py index e606b1178..c080e2af5 100644 --- a/src/agentpool_storage/project_store.py +++ b/src/wolfharness_storage/project_store.py @@ -6,17 +6,17 @@ from pathlib import Path from typing import TYPE_CHECKING -from agentpool.log import get_logger -from agentpool.sessions.models import ProjectData -from agentpool.utils.time_utils import get_now +from wolfharness.log import get_logger +from wolfharness.sessions.models import ProjectData +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: - from agentpool.storage.manager import StorageManager + from wolfharness.storage.manager import StorageManager logger = get_logger(__name__) -# Config file names to search for in .agentpool/ directory +# Config file names to search for in .wolfharness/ directory CONFIG_FILENAMES = ["config.yml", "config.yaml", "config.json", "config.toml"] @@ -25,7 +25,7 @@ def resolve_config(project: ProjectData | None = None, cwd: str | None = None) - Resolution order: 1. Project's explicit config_path (if set) - 2. .agentpool/config.yml in project worktree (auto-discovered) + 2. .wolfharness/config.yml in project worktree (auto-discovered) 3. Global default from ConfigStore (CLI fallback) 4. None (use built-in defaults) @@ -36,7 +36,7 @@ def resolve_config(project: ProjectData | None = None, cwd: str | None = None) - Returns: Path to config file, or None if no config found """ - from agentpool_cli.store import ConfigStore + from wolfharness_cli.store import ConfigStore # 1. Project-specific explicit config if project and project.config_path: @@ -96,7 +96,7 @@ def generate_project_id(worktree: str) -> str: !!! note "Dual ID scheme" This is DIFFERENT from ``compute_project_id()`` in - ``agentpool_storage.opencode_provider.helpers``, which derives the ID + ``wolfharness_storage.opencode_provider.helpers``, which derives the ID from the git root commit SHA1 for OpenCode session directory layout. The two IDs serve distinct purposes and are not interchangeable: - ``generate_project_id`` — path SHA1 (AgentPool project registry) @@ -113,7 +113,7 @@ def generate_project_id(worktree: str) -> str: def discover_config_path(worktree: str) -> str | None: - """Search for config file in .agentpool/ directory. + """Search for config file in .wolfharness/ directory. Args: worktree: Project root path @@ -121,7 +121,7 @@ def discover_config_path(worktree: str) -> str | None: Returns: Path to config file if found, None otherwise """ - config_dir = Path(worktree) / ".agentpool" + config_dir = Path(worktree) / ".wolfharness" if not config_dir.is_dir(): return None @@ -306,7 +306,7 @@ async def delete(self, project_id: str) -> bool: async def refresh_config(self, project_id: str) -> ProjectData | None: """Re-discover config file for a project. - Useful after user adds .agentpool/config.yml to their project. + Useful after user adds .wolfharness/config.yml to their project. Args: project_id: Project identifier diff --git a/src/agentpool_storage/protocols.py b/src/wolfharness_storage/protocols.py similarity index 96% rename from src/agentpool_storage/protocols.py rename to src/wolfharness_storage/protocols.py index 11f978de2..48b6d6aac 100644 --- a/src/agentpool_storage/protocols.py +++ b/src/wolfharness_storage/protocols.py @@ -26,11 +26,11 @@ from collections.abc import Sequence from datetime import datetime - from agentpool.common_types import JsonValue - from agentpool.messaging import ChatMessage, TokenCost - from agentpool.sessions.models import ProjectData, SessionData - from agentpool_config.session import SessionQuery - from agentpool_storage.models import ConversationData, QueryFilters, StatsFilters + from wolfharness.common_types import JsonValue + from wolfharness.messaging import ChatMessage, TokenCost + from wolfharness.sessions.models import ProjectData, SessionData + from wolfharness_config.session import SessionQuery + from wolfharness_storage.models import ConversationData, QueryFilters, StatsFilters # --------------------------------------------------------------------------- @@ -255,7 +255,7 @@ class ProjectStoreProtocol(Protocol): """Protocol for project CRUD operations. Named ``ProjectStoreProtocol`` to avoid clash with the existing - ``ProjectStore`` class in ``agentpool_storage.project_store``. + ``ProjectStore`` class in ``wolfharness_storage.project_store``. """ async def save_project(self, project: ProjectData) -> None: diff --git a/src/agentpool_storage/py.typed b/src/wolfharness_storage/py.typed similarity index 100% rename from src/agentpool_storage/py.typed rename to src/wolfharness_storage/py.typed diff --git a/src/agentpool_storage/sql_provider/__init__.py b/src/wolfharness_storage/sql_provider/__init__.py similarity index 71% rename from src/agentpool_storage/sql_provider/__init__.py rename to src/wolfharness_storage/sql_provider/__init__.py index 1f98a936f..e70f082ec 100644 --- a/src/agentpool_storage/sql_provider/__init__.py +++ b/src/wolfharness_storage/sql_provider/__init__.py @@ -2,8 +2,8 @@ from __future__ import annotations -from agentpool_storage.sql_provider.sql_provider import SQLModelProvider -from agentpool_storage.sql_provider.models import ( +from wolfharness_storage.sql_provider.sql_provider import SQLModelProvider +from wolfharness_storage.sql_provider.models import ( CommandHistory, Conversation, ConversationLog, diff --git a/src/agentpool_storage/sql_provider/cli.py b/src/wolfharness_storage/sql_provider/cli.py similarity index 94% rename from src/agentpool_storage/sql_provider/cli.py rename to src/wolfharness_storage/sql_provider/cli.py index 3c0d1407b..5533b6cff 100755 --- a/src/agentpool_storage/sql_provider/cli.py +++ b/src/wolfharness_storage/sql_provider/cli.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Database migration management script for agentpool. +"""Database migration management script for wolfharness. This script provides easy commands for managing database migrations using Alembic. """ @@ -17,8 +17,8 @@ app = typer.Typer( - name="agentpool-db", - help="Database migration management for agentpool", + name="wolfharness-db", + help="Database migration management for wolfharness", rich_markup_mode="rich", ) console = Console() @@ -39,7 +39,7 @@ def run_command(cmd: list[str], cwd: Path | None = None) -> int: def get_project_root() -> Path: """Get the project root directory.""" # Start from this file and go up to find the project root - # This file is in src/agentpool_storage/sql_provider/cli.py + # This file is in src/wolfharness_storage/sql_provider/cli.py return Path(__file__).parent.parent.parent.parent diff --git a/src/agentpool_storage/sql_provider/models.py b/src/wolfharness_storage/sql_provider/models.py similarity index 98% rename from src/agentpool_storage/sql_provider/models.py rename to src/wolfharness_storage/sql_provider/models.py index 54972ec0b..1cf021025 100644 --- a/src/agentpool_storage/sql_provider/models.py +++ b/src/wolfharness_storage/sql_provider/models.py @@ -1,4 +1,4 @@ -"""Database models for agentpool storage.""" +"""Database models for wolfharness storage.""" from __future__ import annotations @@ -14,8 +14,8 @@ from sqlmodel import JSON, Field, SQLModel from sqlmodel.main import SQLModelConfig # type: ignore[attr-defined] -from agentpool.common_types import JsonValue -from agentpool.utils.time_utils import get_now +from wolfharness.common_types import JsonValue +from wolfharness.utils.time_utils import get_now if TYPE_CHECKING: diff --git a/src/agentpool_storage/sql_provider/queries.py b/src/wolfharness_storage/sql_provider/queries.py similarity index 100% rename from src/agentpool_storage/sql_provider/queries.py rename to src/wolfharness_storage/sql_provider/queries.py diff --git a/src/agentpool_storage/sql_provider/sql_messages.py b/src/wolfharness_storage/sql_provider/sql_messages.py similarity index 96% rename from src/agentpool_storage/sql_provider/sql_messages.py rename to src/wolfharness_storage/sql_provider/sql_messages.py index 0f0b20161..edc00522b 100644 --- a/src/agentpool_storage/sql_provider/sql_messages.py +++ b/src/wolfharness_storage/sql_provider/sql_messages.py @@ -11,10 +11,10 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import desc, select -from agentpool.log import get_logger -from agentpool.utils.time_utils import get_now -from agentpool_storage.sql_provider.models import Message -from agentpool_storage.sql_provider.utils import ( +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import get_now +from wolfharness_storage.sql_provider.models import Message +from wolfharness_storage.sql_provider.utils import ( build_message_query, parse_model_info, to_chat_message, @@ -24,8 +24,8 @@ if TYPE_CHECKING: from sqlalchemy.ext.asyncio import AsyncEngine - from agentpool.messaging import ChatMessage - from agentpool_config.session import SessionQuery + from wolfharness.messaging import ChatMessage + from wolfharness_config.session import SessionQuery logger = get_logger(__name__) @@ -69,7 +69,7 @@ async def log_message(self, *, message: ChatMessage[Any]) -> None: message ID again with final content, tool results, token usage, and finish metadata. """ - from agentpool.storage.serialization import serialize_messages + from wolfharness.storage.serialization import serialize_messages provider, model_name = parse_model_info(message.model_name) cost_info = message.cost_info @@ -220,7 +220,7 @@ async def fork_conversation( Creates a new conversation record. The fork point message_id is returned so callers can set it as parent_id for new messages. """ - from agentpool_storage.sql_provider.models import Conversation + from wolfharness_storage.sql_provider.models import Conversation async with AsyncSession(self.engine) as session: # Get source conversation diff --git a/src/agentpool_storage/sql_provider/sql_projects.py b/src/wolfharness_storage/sql_provider/sql_projects.py similarity index 94% rename from src/agentpool_storage/sql_provider/sql_projects.py rename to src/wolfharness_storage/sql_provider/sql_projects.py index 86ba9c90b..a8d6059bd 100644 --- a/src/agentpool_storage/sql_provider/sql_projects.py +++ b/src/wolfharness_storage/sql_provider/sql_projects.py @@ -11,15 +11,15 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import desc, select -from agentpool.log import get_logger -from agentpool.utils.time_utils import get_now -from agentpool_storage.sql_provider.models import Project +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import get_now +from wolfharness_storage.sql_provider.models import Project if TYPE_CHECKING: from sqlalchemy.ext.asyncio import AsyncEngine - from agentpool.sessions.models import ProjectData + from wolfharness.sessions.models import ProjectData logger = get_logger(__name__) @@ -36,7 +36,7 @@ class SQLProjectsMixin: def _to_project_data(self, row: Project) -> ProjectData: """Convert database model to ProjectData.""" - from agentpool.sessions.models import ProjectData + from wolfharness.sessions.models import ProjectData return ProjectData( project_id=row.project_id, diff --git a/src/agentpool_storage/sql_provider/sql_provider.py b/src/wolfharness_storage/sql_provider/sql_provider.py similarity index 87% rename from src/agentpool_storage/sql_provider/sql_provider.py rename to src/wolfharness_storage/sql_provider/sql_provider.py index a364c709a..69c983678 100644 --- a/src/agentpool_storage/sql_provider/sql_provider.py +++ b/src/wolfharness_storage/sql_provider/sql_provider.py @@ -7,12 +7,12 @@ from sqlmodel import SQLModel -from agentpool.log import get_logger -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.base import StorageProvider -from agentpool_storage.sql_provider.sql_messages import SQLMessagesMixin -from agentpool_storage.sql_provider.sql_projects import SQLProjectsMixin -from agentpool_storage.sql_provider.sql_sessions import SQLSessionsMixin +from wolfharness.log import get_logger +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.sql_provider.sql_messages import SQLMessagesMixin +from wolfharness_storage.sql_provider.sql_projects import SQLProjectsMixin +from wolfharness_storage.sql_provider.sql_sessions import SQLSessionsMixin if TYPE_CHECKING: @@ -57,7 +57,7 @@ async def _init_database(self, auto_migrate: bool = True) -> None: Args: auto_migrate: Whether to automatically run Alembic migrations """ - from agentpool_storage.sql_provider.utils import run_alembic_migrations + from wolfharness_storage.sql_provider.utils import run_alembic_migrations if auto_migrate: # Run migrations first (handles schema changes for existing DBs) diff --git a/src/agentpool_storage/sql_provider/sql_sessions.py b/src/wolfharness_storage/sql_provider/sql_sessions.py similarity index 96% rename from src/agentpool_storage/sql_provider/sql_sessions.py rename to src/wolfharness_storage/sql_provider/sql_sessions.py index 4f5448295..110de53ca 100644 --- a/src/agentpool_storage/sql_provider/sql_sessions.py +++ b/src/wolfharness_storage/sql_provider/sql_sessions.py @@ -15,17 +15,17 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import SQLModel, desc, select -from agentpool.log import get_logger -from agentpool.messaging import TokenCost -from agentpool.utils.parse_time import parse_time_period -from agentpool.utils.time_utils import get_now -from agentpool_storage.models import QueryFilters -from agentpool_storage.sql_provider.models import ( +from wolfharness.log import get_logger +from wolfharness.messaging import TokenCost +from wolfharness.utils.parse_time import parse_time_period +from wolfharness.utils.time_utils import get_now +from wolfharness_storage.models import QueryFilters +from wolfharness_storage.sql_provider.models import ( CommandHistory, Conversation, Message, ) -from agentpool_storage.sql_provider.utils import ( +from wolfharness_storage.sql_provider.utils import ( format_conversation, to_chat_message, ) @@ -46,9 +46,9 @@ from sqlalchemy.ext.asyncio import AsyncEngine - from agentpool.common_types import JsonValue - from agentpool.sessions.models import SessionData - from agentpool_storage.models import ConversationData, StatsFilters + from wolfharness.common_types import JsonValue + from wolfharness.sessions.models import SessionData + from wolfharness_storage.models import ConversationData, StatsFilters logger = get_logger(__name__) @@ -115,7 +115,7 @@ async def log_session( Uses upsert semantics to handle duplicate session IDs gracefully. If the session already exists, it will be silently ignored. """ - from agentpool_storage.sql_provider.models import Conversation + from wolfharness_storage.sql_provider.models import Conversation async with AsyncSession(self.engine) as session: # Soft validation: check if parent exists (warn but don't crash) @@ -292,7 +292,7 @@ async def get_sessions(self, filters: QueryFilters) -> list[ConversationData]: async def get_session_stats(self, filters: StatsFilters) -> dict[str, dict[str, Any]]: """Get statistics using SQL aggregations.""" - from agentpool_storage.sql_provider.models import Conversation, Message + from wolfharness_storage.sql_provider.models import Conversation, Message async with AsyncSession(self.engine) as session: # Base query for stats @@ -339,7 +339,7 @@ async def reset(self, *, agent_name: str | None = None, hard: bool = False) -> t """Reset database storage.""" from sqlalchemy import text - from agentpool_storage.sql_provider.queries import ( + from wolfharness_storage.sql_provider.queries import ( DELETE_AGENT_CONVERSATIONS, DELETE_AGENT_MESSAGES, DELETE_ALL_CONVERSATIONS, @@ -375,7 +375,7 @@ async def reset(self, *, agent_name: str | None = None, hard: bool = False) -> t async def get_session_counts(self, *, agent_name: str | None = None) -> tuple[int, int]: """Get conversation and message counts.""" - from agentpool_storage.sql_provider import Conversation, Message + from wolfharness_storage.sql_provider import Conversation, Message if not self.session: msg = "Session not initialized. Use provider as async context manager." @@ -410,7 +410,7 @@ def _session_from_db(self, row: Conversation) -> SessionData: """ from pydantic import TypeAdapter - from agentpool.sessions.models import PendingDeferredCall, SessionData + from wolfharness.sessions.models import PendingDeferredCall, SessionData metadata = row.metadata_json or {} # Conversation.title is authoritative — always sync to metadata @@ -455,7 +455,7 @@ async def save_session(self, data: SessionData) -> None: """ from pydantic import TypeAdapter - from agentpool.sessions.models import PendingDeferredCall + from wolfharness.sessions.models import PendingDeferredCall # Serialize pending_deferred_calls into metadata_json metadata = dict(data.metadata) diff --git a/src/agentpool_storage/sql_provider/utils.py b/src/wolfharness_storage/sql_provider/utils.py similarity index 92% rename from src/agentpool_storage/sql_provider/utils.py rename to src/wolfharness_storage/sql_provider/utils.py index ead93202f..ad32336fd 100644 --- a/src/agentpool_storage/sql_provider/utils.py +++ b/src/wolfharness_storage/sql_provider/utils.py @@ -10,12 +10,12 @@ from sqlalchemy import Column, and_ from sqlmodel import select -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage, TokenCost -from agentpool.storage import deserialize_messages -from agentpool.utils.time_utils import parse_iso_timestamp -from agentpool_storage.models import ConversationData -from agentpool_storage.sql_provider.models import Conversation +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage, TokenCost +from wolfharness.storage import deserialize_messages +from wolfharness.utils.time_utils import parse_iso_timestamp +from wolfharness_storage.models import ConversationData +from wolfharness_storage.sql_provider.models import Conversation if TYPE_CHECKING: @@ -24,8 +24,8 @@ from sqlmodel.sql.expression import SelectOfScalar from tokonomics.toko_types import TokenUsage - from agentpool_config.session import SessionQuery - from agentpool_storage.sql_provider.models import Message + from wolfharness_config.session import SessionQuery + from wolfharness_storage.sql_provider.models import Message # Track migrated databases to skip redundant migration attempts @@ -37,7 +37,7 @@ def aggregate_token_usage( messages: Sequence[Message | ChatMessage[str]], ) -> TokenUsage: """Sum up tokens from a sequence of messages.""" - from agentpool_storage.sql_provider.models import Message + from wolfharness_storage.sql_provider.models import Message total = prompt = completion = 0 for msg in messages: @@ -166,7 +166,7 @@ def parse_model_info(model: str | None) -> tuple[str | None, str | None]: def build_message_query(query: SessionQuery) -> SelectOfScalar[Any]: """Build SQLModel query from SessionQuery.""" - from agentpool_storage.sql_provider.models import Message + from wolfharness_storage.sql_provider.models import Message stmt = select(Message).order_by(Message.timestamp, Message.id) # type: ignore diff --git a/src/agentpool_storage/zed_provider/__init__.py b/src/wolfharness_storage/zed_provider/__init__.py similarity index 84% rename from src/agentpool_storage/zed_provider/__init__.py rename to src/wolfharness_storage/zed_provider/__init__.py index 8752963c7..2753ab512 100644 --- a/src/agentpool_storage/zed_provider/__init__.py +++ b/src/wolfharness_storage/zed_provider/__init__.py @@ -9,7 +9,7 @@ from __future__ import annotations -from agentpool_storage.zed_provider.provider import ZedStorageProvider +from wolfharness_storage.zed_provider.provider import ZedStorageProvider __all__ = [ "ZedStorageProvider", diff --git a/src/agentpool_storage/zed_provider/helpers.py b/src/wolfharness_storage/zed_provider/helpers.py similarity index 96% rename from src/agentpool_storage/zed_provider/helpers.py rename to src/wolfharness_storage/zed_provider/helpers.py index 6ee23fad4..e58bc8ab8 100644 --- a/src/agentpool_storage/zed_provider/helpers.py +++ b/src/wolfharness_storage/zed_provider/helpers.py @@ -21,16 +21,16 @@ ) import zstandard -from agentpool.log import get_logger -from agentpool.messaging import ChatMessage -from agentpool.mime_utils import detect_image_media_type -from agentpool.utils.time_utils import parse_iso_timestamp +from wolfharness.log import get_logger +from wolfharness.messaging import ChatMessage +from wolfharness.mime_utils import detect_image_media_type +from wolfharness.utils.time_utils import parse_iso_timestamp if TYPE_CHECKING: from datetime import datetime - from agentpool_storage.zed_provider.models import ( + from wolfharness_storage.zed_provider.models import ( ZedFlatMessage, ZedNestedMessage, ZedThread, @@ -266,7 +266,7 @@ def thread_to_chat_messages(thread: ZedThread, thread_id: str) -> list[ChatMessa Returns: List of ChatMessage objects """ - from agentpool_storage.zed_provider.models import ZedFlatMessage, ZedNestedMessage + from wolfharness_storage.zed_provider.models import ZedFlatMessage, ZedNestedMessage messages: list[ChatMessage[str]] = [] updated_at = parse_iso_timestamp(thread.updated_at) diff --git a/src/agentpool_storage/zed_provider/models.py b/src/wolfharness_storage/zed_provider/models.py similarity index 100% rename from src/agentpool_storage/zed_provider/models.py rename to src/wolfharness_storage/zed_provider/models.py diff --git a/src/agentpool_storage/zed_provider/provider.py b/src/wolfharness_storage/zed_provider/provider.py similarity index 96% rename from src/agentpool_storage/zed_provider/provider.py rename to src/wolfharness_storage/zed_provider/provider.py index c26cf4e0f..d41de836c 100644 --- a/src/agentpool_storage/zed_provider/provider.py +++ b/src/wolfharness_storage/zed_provider/provider.py @@ -11,21 +11,21 @@ import anyenv -from agentpool.log import get_logger -from agentpool.utils.time_utils import get_now, parse_iso_timestamp -from agentpool_config.storage import ZedStorageConfig -from agentpool_storage.base import StorageProvider -from agentpool_storage.models import ConversationData, TokenUsage -from agentpool_storage.zed_provider import helpers -from agentpool_storage.zed_provider.models import ZedThread +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import get_now, parse_iso_timestamp +from wolfharness_config.storage import ZedStorageConfig +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.models import ConversationData, TokenUsage +from wolfharness_storage.zed_provider import helpers +from wolfharness_storage.zed_provider.models import ZedThread if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.messaging import ChatMessage - from agentpool_config.session import SessionQuery - from agentpool_storage.models import QueryFilters, StatsFilters + from wolfharness.messaging import ChatMessage + from wolfharness_config.session import SessionQuery + from wolfharness_storage.models import QueryFilters, StatsFilters logger = get_logger(__name__) @@ -411,7 +411,7 @@ async def fork_conversation( import asyncio import datetime as dt - from agentpool_storage.models import QueryFilters, StatsFilters + from wolfharness_storage.models import QueryFilters, StatsFilters async def main() -> None: provider = ZedStorageProvider() diff --git a/src/agentpool_sync/__init__.py b/src/wolfharness_sync/__init__.py similarity index 74% rename from src/agentpool_sync/__init__.py rename to src/wolfharness_sync/__init__.py index 645edc04f..12d328861 100644 --- a/src/agentpool_sync/__init__.py +++ b/src/wolfharness_sync/__init__.py @@ -11,30 +11,30 @@ from __future__ import annotations -from agentpool_sync.config import ( +from wolfharness_sync.config import ( FileSyncConfig, PackageSyncConfig, SyncConfig, UrlSyncConfig, ) -from agentpool_sync.git import GitError, GitRepo -from agentpool_sync.handlers import AgentReconciler, LoggingHandler -from agentpool_sync.manager import InitMode, SyncManager -from agentpool_sync.models import FileChange, SyncMetadata, UrlChange -from agentpool_sync.packages import ( +from wolfharness_sync.git import GitError, GitRepo +from wolfharness_sync.handlers import AgentReconciler, LoggingHandler +from wolfharness_sync.manager import InitMode, SyncManager +from wolfharness_sync.models import FileChange, SyncMetadata, UrlChange +from wolfharness_sync.packages import ( PackageChange, PackageRegistry, PackageState, is_significant_bump, ) -from agentpool_sync.parsers import ( +from wolfharness_sync.parsers import ( BUILTIN_PARSERS, MarkdownSyncParser, MetadataParser, PythonSyncParser, get_parser_for_file, ) -from agentpool_sync.resources import ResourceChange, ResourceRegistry, ResourceState +from wolfharness_sync.resources import ResourceChange, ResourceRegistry, ResourceState __all__ = [ # Parsers diff --git a/src/agentpool_sync/config.py b/src/wolfharness_sync/config.py similarity index 95% rename from src/agentpool_sync/config.py rename to src/wolfharness_sync/config.py index 413d56f48..eac4eae1f 100644 --- a/src/agentpool_sync/config.py +++ b/src/wolfharness_sync/config.py @@ -80,8 +80,8 @@ class SyncConfig(BaseModel): urls: list[UrlSyncConfig] = Field(default_factory=list) """URL resources to track centrally.""" - registry_path: str = ".agentpool/resources.yml" + registry_path: str = ".wolfharness/resources.yml" """Path to the resource registry file.""" - packages_registry_path: str = ".agentpool/packages.yml" + packages_registry_path: str = ".wolfharness/packages.yml" """Path to the packages registry file.""" diff --git a/src/agentpool_sync/git.py b/src/wolfharness_sync/git.py similarity index 100% rename from src/agentpool_sync/git.py rename to src/wolfharness_sync/git.py diff --git a/src/agentpool_sync/handlers.py b/src/wolfharness_sync/handlers.py similarity index 98% rename from src/agentpool_sync/handlers.py rename to src/wolfharness_sync/handlers.py index 4b05022a2..69f0d9262 100644 --- a/src/agentpool_sync/handlers.py +++ b/src/wolfharness_sync/handlers.py @@ -8,8 +8,8 @@ if TYPE_CHECKING: from collections.abc import Callable - from agentpool import MessageNode - from agentpool_sync.models import FileChange + from wolfharness import MessageNode + from wolfharness_sync.models import FileChange class AgentReconciler: diff --git a/src/agentpool_sync/manager.py b/src/wolfharness_sync/manager.py similarity index 96% rename from src/agentpool_sync/manager.py rename to src/wolfharness_sync/manager.py index 00753a036..7a56825c0 100644 --- a/src/agentpool_sync/manager.py +++ b/src/wolfharness_sync/manager.py @@ -9,19 +9,19 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from agentpool_sync.git import GitRepo -from agentpool_sync.models import FileChange -from agentpool_sync.packages import PackageRegistry -from agentpool_sync.parsers import BUILTIN_PARSERS -from agentpool_sync.resources import ResourceRegistry +from wolfharness_sync.git import GitRepo +from wolfharness_sync.models import FileChange +from wolfharness_sync.packages import PackageRegistry +from wolfharness_sync.parsers import BUILTIN_PARSERS +from wolfharness_sync.resources import ResourceRegistry if TYPE_CHECKING: from collections.abc import Awaitable, Callable - from agentpool_sync.models import SyncMetadata - from agentpool_sync.packages import PackageChange - from agentpool_sync.parsers import MetadataParser + from wolfharness_sync.models import SyncMetadata + from wolfharness_sync.packages import PackageChange + from wolfharness_sync.parsers import MetadataParser ChangeHandler = Callable[[FileChange], Awaitable[str | None]] ProjectChangeHandler = Callable[[list[PackageChange]], Awaitable[None]] @@ -55,19 +55,19 @@ def __init__( Args: root: Repository root path - registry_file: Path to resource registry (default: .agentpool/resources.yml) - packages_file: Path to packages registry (default: .agentpool/packages.yml) + registry_file: Path to resource registry (default: .wolfharness/resources.yml) + packages_file: Path to packages registry (default: .wolfharness/packages.yml) """ self.root = Path(root).resolve() self.git = GitRepo(self.root) registry_path = ( - Path(registry_file) if registry_file else self.root / ".agentpool" / "resources.yml" + Path(registry_file) if registry_file else self.root / ".wolfharness" / "resources.yml" ) self.resources = ResourceRegistry(registry_file=registry_path) packages_path = ( - Path(packages_file) if packages_file else self.root / ".agentpool" / "packages.yml" + Path(packages_file) if packages_file else self.root / ".wolfharness" / "packages.yml" ) self.packages = PackageRegistry(registry_file=packages_path) diff --git a/src/agentpool_sync/models.py b/src/wolfharness_sync/models.py similarity index 100% rename from src/agentpool_sync/models.py rename to src/wolfharness_sync/models.py diff --git a/src/agentpool_sync/packages.py b/src/wolfharness_sync/packages.py similarity index 100% rename from src/agentpool_sync/packages.py rename to src/wolfharness_sync/packages.py diff --git a/src/agentpool_sync/parsers/__init__.py b/src/wolfharness_sync/parsers/__init__.py similarity index 100% rename from src/agentpool_sync/parsers/__init__.py rename to src/wolfharness_sync/parsers/__init__.py diff --git a/src/agentpool_sync/parsers/helpers.py b/src/wolfharness_sync/parsers/helpers.py similarity index 100% rename from src/agentpool_sync/parsers/helpers.py rename to src/wolfharness_sync/parsers/helpers.py diff --git a/src/agentpool_sync/parsers/markdown_parser.py b/src/wolfharness_sync/parsers/markdown_parser.py similarity index 93% rename from src/agentpool_sync/parsers/markdown_parser.py rename to src/wolfharness_sync/parsers/markdown_parser.py index 3fd8e0191..01b9520f1 100644 --- a/src/agentpool_sync/parsers/markdown_parser.py +++ b/src/wolfharness_sync/parsers/markdown_parser.py @@ -6,11 +6,11 @@ import yaml -from agentpool_sync.parsers.helpers import FRONTMATTER_PATTERN +from wolfharness_sync.parsers.helpers import FRONTMATTER_PATTERN if TYPE_CHECKING: - from agentpool_sync.models import SyncMetadata + from wolfharness_sync.models import SyncMetadata class MarkdownSyncParser: @@ -34,7 +34,7 @@ class MarkdownSyncParser: def parse(self, content: str) -> SyncMetadata | None: import yamling - from agentpool_sync.models import SyncMetadata + from wolfharness_sync.models import SyncMetadata match = FRONTMATTER_PATTERN.match(content) if not match: diff --git a/src/agentpool_sync/parsers/protocol.py b/src/wolfharness_sync/parsers/protocol.py similarity index 91% rename from src/agentpool_sync/parsers/protocol.py rename to src/wolfharness_sync/parsers/protocol.py index a727b13c8..a5a713acd 100644 --- a/src/agentpool_sync/parsers/protocol.py +++ b/src/wolfharness_sync/parsers/protocol.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: - from agentpool_sync.models import SyncMetadata + from wolfharness_sync.models import SyncMetadata class MetadataParser(Protocol): diff --git a/src/agentpool_sync/parsers/python_parser.py b/src/wolfharness_sync/parsers/python_parser.py similarity index 96% rename from src/agentpool_sync/parsers/python_parser.py rename to src/wolfharness_sync/parsers/python_parser.py index 9ff6c4c33..fd0bd06e4 100644 --- a/src/agentpool_sync/parsers/python_parser.py +++ b/src/wolfharness_sync/parsers/python_parser.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: - from agentpool_sync.models import SyncMetadata + from wolfharness_sync.models import SyncMetadata _BLOCK_PATTERN = re.compile(r"^# /// sync\s*\n((?:^#[^\n]*\n)*?)^# ///$", re.MULTILINE) @@ -89,7 +89,7 @@ def _parse_toml_like(text: str) -> SyncMetadata: """Simple TOML-like parser for the metadata block.""" import anyenv - from agentpool_sync.models import SyncMetadata + from wolfharness_sync.models import SyncMetadata try: # TODO: need to check whether this needs to be BaseModel with extra fields allowed diff --git a/src/agentpool_sync/resources.py b/src/wolfharness_sync/resources.py similarity index 100% rename from src/agentpool_sync/resources.py rename to src/wolfharness_sync/resources.py diff --git a/src/wolfharness_toolsets/__init__.py b/src/wolfharness_toolsets/__init__.py new file mode 100644 index 000000000..515c994ca --- /dev/null +++ b/src/wolfharness_toolsets/__init__.py @@ -0,0 +1,13 @@ +"""Toolsets package.""" + +from wolfharness_toolsets.config_creation import ConfigCreationTools +from wolfharness_toolsets.fsspec_toolset import FSSpecTools +from wolfharness_toolsets.notifications import NotificationsTools +from wolfharness_toolsets.vfs_toolset import VFSTools + +__all__ = [ + "ConfigCreationTools", + "FSSpecTools", + "NotificationsTools", + "VFSTools", +] diff --git a/src/wolfharness_toolsets/builtin/__init__.py b/src/wolfharness_toolsets/builtin/__init__.py new file mode 100644 index 000000000..4330edc56 --- /dev/null +++ b/src/wolfharness_toolsets/builtin/__init__.py @@ -0,0 +1,23 @@ +"""Built-in toolsets for agent capabilities.""" + +from __future__ import annotations + + +# Import provider classes +from wolfharness_toolsets.builtin.code import CodeTools +from wolfharness_toolsets.builtin.debug import DebugTools +from wolfharness_toolsets.builtin.execution_environment import ProcessManagementTools +from wolfharness_toolsets.builtin.question_tools import QuestionTools +from wolfharness_toolsets.builtin.subagent_tools import SubagentTools +from wolfharness_toolsets.builtin.workers import WorkersTools + + +__all__ = [ + # Provider classes + "CodeTools", + "DebugTools", + "ProcessManagementTools", + "QuestionTools", + "SubagentTools", + "WorkersTools", +] diff --git a/src/agentpool_toolsets/builtin/code.py b/src/wolfharness_toolsets/builtin/code.py similarity index 97% rename from src/agentpool_toolsets/builtin/code.py rename to src/wolfharness_toolsets/builtin/code.py index 1e6a51abc..b9dfcab03 100644 --- a/src/agentpool_toolsets/builtin/code.py +++ b/src/wolfharness_toolsets/builtin/code.py @@ -9,10 +9,10 @@ from upathtools import is_directory -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger -from agentpool_toolsets.fsspec_toolset.diagnostics import ( +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger +from wolfharness_toolsets.fsspec_toolset.diagnostics import ( DiagnosticsManager, format_diagnostics_table, format_run_summary, @@ -25,8 +25,8 @@ from exxec.base import ExecutionEnvironment from fsspec.asyn import AsyncFileSystem - from agentpool.tools.base import Tool - from agentpool_toolsets.fsspec_toolset.diagnostics import DiagnosticsConfig + from wolfharness.tools.base import Tool + from wolfharness_toolsets.fsspec_toolset.diagnostics import DiagnosticsConfig logger = get_logger(__name__) diff --git a/src/agentpool_toolsets/builtin/debug.py b/src/wolfharness_toolsets/builtin/debug.py similarity index 95% rename from src/agentpool_toolsets/builtin/debug.py rename to src/wolfharness_toolsets/builtin/debug.py index a7760ca57..70ed0ec8b 100644 --- a/src/agentpool_toolsets/builtin/debug.py +++ b/src/wolfharness_toolsets/builtin/debug.py @@ -12,9 +12,9 @@ import anyenv from pydantic_ai import RunContext # noqa: TC002 -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger logger = get_logger(__name__) @@ -206,7 +206,7 @@ async def get_logs( async def get_platform_paths() -> str: - """Get platform-specific paths for agentpool. + """Get platform-specific paths for wolfharness. Returns: Dictionary of platform paths @@ -214,14 +214,14 @@ async def get_platform_paths() -> str: import platformdirs paths = { - "config": platformdirs.user_config_dir("agentpool"), - "data": platformdirs.user_data_dir("agentpool"), - "cache": platformdirs.user_cache_dir("agentpool"), - "logs": platformdirs.user_log_dir("agentpool"), - "state": platformdirs.user_state_dir("agentpool"), + "config": platformdirs.user_config_dir("wolfharness"), + "data": platformdirs.user_data_dir("wolfharness"), + "cache": platformdirs.user_cache_dir("wolfharness"), + "logs": platformdirs.user_log_dir("wolfharness"), + "state": platformdirs.user_state_dir("wolfharness"), } - lines = ["Platform paths for agentpool:", ""] + lines = ["Platform paths for wolfharness:", ""] for name, path in paths.items(): lines.append(f" {name}: {path}") diff --git a/src/agentpool_toolsets/builtin/execution_environment.py b/src/wolfharness_toolsets/builtin/execution_environment.py similarity index 96% rename from src/agentpool_toolsets/builtin/execution_environment.py rename to src/wolfharness_toolsets/builtin/execution_environment.py index c456d5cb5..8029cb1a9 100644 --- a/src/agentpool_toolsets/builtin/execution_environment.py +++ b/src/wolfharness_toolsets/builtin/execution_environment.py @@ -5,9 +5,9 @@ import re from typing import TYPE_CHECKING -from agentpool import log -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness import log +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability if TYPE_CHECKING: @@ -15,7 +15,7 @@ from exxec import ExecutionEnvironment - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool logger = log.get_logger(__name__) @@ -37,8 +37,8 @@ class ProcessManagementTools(FunctionToolsetCapability): Uses any ExecutionEnvironment backend and emits events via AgentContext. For code execution and bash commands, use the standalone tools: - - agentpool.tool_impls.bash.BashTool - - agentpool.tool_impls.execute_code.ExecuteCodeTool + - wolfharness.tool_impls.bash.BashTool + - wolfharness.tool_impls.execute_code.ExecuteCodeTool """ def __init__(self, env: ExecutionEnvironment | None = None, name: str = "process") -> None: diff --git a/src/agentpool_toolsets/builtin/file_edit/__init__.py b/src/wolfharness_toolsets/builtin/file_edit/__init__.py similarity index 100% rename from src/agentpool_toolsets/builtin/file_edit/__init__.py rename to src/wolfharness_toolsets/builtin/file_edit/__init__.py diff --git a/src/agentpool_toolsets/builtin/file_edit/file_edit.py b/src/wolfharness_toolsets/builtin/file_edit/file_edit.py similarity index 95% rename from src/agentpool_toolsets/builtin/file_edit/file_edit.py rename to src/wolfharness_toolsets/builtin/file_edit/file_edit.py index 429c58e81..0355c2c67 100644 --- a/src/agentpool_toolsets/builtin/file_edit/file_edit.py +++ b/src/wolfharness_toolsets/builtin/file_edit/file_edit.py @@ -9,12 +9,12 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from agentpool.tools.base import Tool -from agentpool.utils.diffs import compute_unified_diff, count_changed_lines +from wolfharness.tools.base import Tool +from wolfharness.utils.diffs import compute_unified_diff, count_changed_lines if TYPE_CHECKING: - from agentpool.agents import AgentContext + from wolfharness.agents import AgentContext async def edit_file_tool( diff --git a/src/agentpool_toolsets/builtin/question_tools.py b/src/wolfharness_toolsets/builtin/question_tools.py similarity index 97% rename from src/agentpool_toolsets/builtin/question_tools.py rename to src/wolfharness_toolsets/builtin/question_tools.py index d2e173219..1b76e73be 100644 --- a/src/agentpool_toolsets/builtin/question_tools.py +++ b/src/wolfharness_toolsets/builtin/question_tools.py @@ -14,10 +14,10 @@ from pydantic_ai import ModelRetry from pydantic_xml import BaseXmlModel, attr, element -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.tasks.exceptions import RunAbortedError -from agentpool.tools.base import ToolResult +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.tasks.exceptions import RunAbortedError +from wolfharness.tools.base import ToolResult class Suggest(BaseXmlModel, tag="suggest"): diff --git a/src/agentpool_toolsets/builtin/skills.py b/src/wolfharness_toolsets/builtin/skills.py similarity index 95% rename from src/agentpool_toolsets/builtin/skills.py rename to src/wolfharness_toolsets/builtin/skills.py index add9d622b..7e0c5074c 100644 --- a/src/agentpool_toolsets/builtin/skills.py +++ b/src/wolfharness_toolsets/builtin/skills.py @@ -1,7 +1,7 @@ """Skills tools — backward-compatible re-exports. The standalone ``SkillsTools`` toolset has been consolidated into -:class:`~agentpool.capabilities.skill_manager_cap.SkillManagerCap` +:class:`~wolfharness.capabilities.skill_manager_cap.SkillManagerCap` (unify-skill-loading change). The production ``load_skill`` / ``list_skills`` tools are owned by ``SkillManagerCap``. @@ -16,8 +16,8 @@ from typing import Any -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.skill_manager_cap import ( +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.skill_manager_cap import ( SkillManagerCap, _load_reference_content, _substitute_arguments, diff --git a/src/agentpool_toolsets/builtin/subagent_tools.py b/src/wolfharness_toolsets/builtin/subagent_tools.py similarity index 95% rename from src/agentpool_toolsets/builtin/subagent_tools.py rename to src/wolfharness_toolsets/builtin/subagent_tools.py index f8d63ac2a..d3827ffad 100644 --- a/src/agentpool_toolsets/builtin/subagent_tools.py +++ b/src/wolfharness_toolsets/builtin/subagent_tools.py @@ -17,12 +17,12 @@ import logfire from pydantic_ai import ModelRetry -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.agents.events import StreamCompleteEvent -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger -from agentpool.tools.exceptions import ToolError +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger +from wolfharness.tools.exceptions import ToolError logger = get_logger(__name__) @@ -99,7 +99,7 @@ async def task( # noqa: D417 Returns: Structured output containing result and metadata """ - from agentpool.common_types import SupportsRunStream + from wolfharness.common_types import SupportsRunStream _ = description # Used for logging/tracking in future @@ -234,7 +234,7 @@ def _extract_model_id(agent_cfg: Any) -> str | None: """Extract model_id from agent config if it's a NativeAgentConfig.""" if agent_cfg is None: return None - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig if isinstance(agent_cfg, NativeAgentConfig): raw_model = agent_cfg.model @@ -264,7 +264,7 @@ async def _run_sync( is_team_node: bool, ) -> str: """Run task synchronously and return final content.""" - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory if is_team_node: result = await node.run(prompt, message_history=MessageHistory()) diff --git a/src/agentpool_toolsets/builtin/workers.py b/src/wolfharness_toolsets/builtin/workers.py similarity index 89% rename from src/agentpool_toolsets/builtin/workers.py rename to src/wolfharness_toolsets/builtin/workers.py index 8485af7c7..b723a0901 100644 --- a/src/agentpool_toolsets/builtin/workers.py +++ b/src/wolfharness_toolsets/builtin/workers.py @@ -10,22 +10,22 @@ from typing import TYPE_CHECKING, Any, Literal -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.agents.events import ( +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.agents.events import ( StreamCompleteEvent, SubAgentEvent, ) -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger -from agentpool.tools.exceptions import ToolError +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger +from wolfharness.tools.exceptions import ToolError if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.tools.base import Tool - from agentpool_config.workers import WorkerConfig + from wolfharness.tools.base import Tool + from wolfharness_config.workers import WorkerConfig logger = get_logger(__name__) @@ -60,7 +60,7 @@ async def get_tools(self) -> Sequence[Tool]: def _create_worker_tool(self, worker_config: WorkerConfig) -> Tool: """Create a tool for a single worker configuration.""" - from agentpool_config.workers import AgentWorkerConfig + from wolfharness_config.workers import AgentWorkerConfig worker_name = worker_config.name # Regular agents get history management @@ -83,7 +83,7 @@ def _create_agent_tool( """Create tool for a regular agent worker with history management.""" async def run(ctx: AgentContext, prompt: str) -> Any: - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent session_pool = _require_session_pool(ctx) worker = await _resolve_worker(ctx, session_pool, agent_name) @@ -127,7 +127,7 @@ def _create_node_tool(self, node_name: str) -> Tool: async def run(ctx: AgentContext, prompt: str) -> str: session_pool = _require_session_pool(ctx) worker = await _resolve_worker(ctx, session_pool, node_name) - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent is_team_node = not isinstance(worker, BaseAgent) _check_delegation_depth(ctx) @@ -174,7 +174,7 @@ async def _resolve_worker(ctx: AgentContext, session_pool: Any, node_name: str) agent_cfg = ctx.pool.manifest.agents[node_name] if session_pool.sessions is not None: session_pool.sessions.runtime_registry.register(node_name, agent_cfg) - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id temp_id = generate_session_id() worker = await session_pool.sessions.get_or_create_session_agent( @@ -206,7 +206,7 @@ async def _manage_history( worker: Any, ctx: AgentContext, pass_message_history: bool, reset_history_on_run: bool ) -> Any: """Manage conversation history for agent workers. Returns old history if saved.""" - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent if not isinstance(worker, BaseAgent): return None @@ -227,9 +227,9 @@ async def _create_child_session( is_team_node: bool, ) -> str: """Create child session for worker execution.""" - from agentpool.agents.base_agent import BaseAgent - from agentpool.common_types import SupportsRunStream - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.common_types import SupportsRunStream + from wolfharness.delegation.base_team import BaseTeam parent_session_id = getattr(ctx.node, "session_id", None) or ( ctx.run_ctx.session_id if ctx.run_ctx else "" @@ -275,10 +275,10 @@ async def _execute_worker( is_team_node: bool, ) -> str: """Execute worker and return result content.""" - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent if is_team_node: - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory result = await worker.run(prompt, message_history=MessageHistory()) return str(result.content) if result.content else "" diff --git a/src/agentpool_toolsets/composio_toolset.py b/src/wolfharness_toolsets/composio_toolset.py similarity index 93% rename from src/agentpool_toolsets/composio_toolset.py rename to src/wolfharness_toolsets/composio_toolset.py index 7fed8c543..bf3684b82 100644 --- a/src/agentpool_toolsets/composio_toolset.py +++ b/src/wolfharness_toolsets/composio_toolset.py @@ -5,14 +5,14 @@ import os from typing import TYPE_CHECKING, Any -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import Callable, Sequence - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool logger = get_logger(__name__) @@ -83,7 +83,7 @@ async def get_tools(self) -> Sequence[Tool]: import anyio async def main() -> None: - from agentpool import Agent + from wolfharness import Agent tools = ComposioTools("user@example.com", toolsets=["github"]) agent = Agent(model="gpt-5-nano") diff --git a/src/agentpool_toolsets/config_creation.py b/src/wolfharness_toolsets/config_creation.py similarity index 98% rename from src/agentpool_toolsets/config_creation.py rename to src/wolfharness_toolsets/config_creation.py index 676b737a2..316e81a9c 100644 --- a/src/agentpool_toolsets/config_creation.py +++ b/src/wolfharness_toolsets/config_creation.py @@ -9,7 +9,7 @@ from upathtools import is_directory_sync, to_upath from upathtools.filesystems.file_filesystems import JsonSchemaFileSystem -from agentpool.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability if TYPE_CHECKING: diff --git a/src/agentpool_toolsets/cron.py b/src/wolfharness_toolsets/cron.py similarity index 92% rename from src/agentpool_toolsets/cron.py rename to src/wolfharness_toolsets/cron.py index 14ec81c91..f529d052b 100644 --- a/src/agentpool_toolsets/cron.py +++ b/src/wolfharness_toolsets/cron.py @@ -5,18 +5,18 @@ from datetime import datetime from typing import TYPE_CHECKING, Self -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger -from agentpool.utils.time_utils import datetime_to_ms +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger +from wolfharness.utils.time_utils import datetime_to_ms if TYPE_CHECKING: from collections.abc import Sequence from types import TracebackType - from agentpool.tools.base import Tool - from agentpool_bot.cron.cron_types import CronSchedule - from agentpool_bot.cron.service import CronService + from wolfharness.tools.base import Tool + from wolfharness_bot.cron.cron_types import CronSchedule + from wolfharness_bot.cron.service import CronService logger = get_logger(__name__) @@ -93,7 +93,7 @@ async def cron_add( Returns: Confirmation with the job id. """ - from agentpool_bot.cron.cron_types import CronSchedule + from wolfharness_bot.cron.cron_types import CronSchedule if tz and not cron_expr: return "Error: tz can only be used with cron_expr" diff --git a/src/agentpool_toolsets/entry_points.py b/src/wolfharness_toolsets/entry_points.py similarity index 87% rename from src/agentpool_toolsets/entry_points.py rename to src/wolfharness_toolsets/entry_points.py index 99019ba88..ee60b2036 100644 --- a/src/agentpool_toolsets/entry_points.py +++ b/src/wolfharness_toolsets/entry_points.py @@ -7,14 +7,14 @@ from epregistry import EntryPointRegistry -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool logger = get_logger(__name__) @@ -26,7 +26,7 @@ class EntryPointTools(FunctionToolsetCapability): def __init__(self, module: str) -> None: super().__init__(name=module) self.module = module - self.registry = EntryPointRegistry[Callable[..., Any]]("agentpool") + self.registry = EntryPointRegistry[Callable[..., Any]]("wolfharness") async def get_tools(self) -> Sequence[Tool]: """Get tools from entry points.""" diff --git a/src/agentpool_toolsets/fsspec_toolset/__init__.py b/src/wolfharness_toolsets/fsspec_toolset/__init__.py similarity index 55% rename from src/agentpool_toolsets/fsspec_toolset/__init__.py rename to src/wolfharness_toolsets/fsspec_toolset/__init__.py index 5b725e23b..0476ed7a6 100644 --- a/src/agentpool_toolsets/fsspec_toolset/__init__.py +++ b/src/wolfharness_toolsets/fsspec_toolset/__init__.py @@ -2,13 +2,13 @@ from __future__ import annotations -from agentpool_toolsets.fsspec_toolset.diagnostics import ( +from wolfharness_toolsets.fsspec_toolset.diagnostics import ( DiagnosticsConfig, DiagnosticsManager, DiagnosticsResult, ) -from agentpool_toolsets.fsspec_toolset.image_utils import resize_image_if_needed -from agentpool_toolsets.fsspec_toolset.toolset import FSSpecTools +from wolfharness_toolsets.fsspec_toolset.image_utils import resize_image_if_needed +from wolfharness_toolsets.fsspec_toolset.toolset import FSSpecTools __all__ = [ "DiagnosticsConfig", diff --git a/src/agentpool_toolsets/fsspec_toolset/diagnostics.py b/src/wolfharness_toolsets/fsspec_toolset/diagnostics.py similarity index 99% rename from src/agentpool_toolsets/fsspec_toolset/diagnostics.py rename to src/wolfharness_toolsets/fsspec_toolset/diagnostics.py index 014e7f105..dbb8b11b5 100644 --- a/src/agentpool_toolsets/fsspec_toolset/diagnostics.py +++ b/src/wolfharness_toolsets/fsspec_toolset/diagnostics.py @@ -18,7 +18,7 @@ import anyenv -from agentpool.log import get_logger +from wolfharness.log import get_logger if TYPE_CHECKING: diff --git a/src/agentpool_toolsets/fsspec_toolset/grep.py b/src/wolfharness_toolsets/fsspec_toolset/grep.py similarity index 99% rename from src/agentpool_toolsets/fsspec_toolset/grep.py rename to src/wolfharness_toolsets/fsspec_toolset/grep.py index a6aef6799..96b175b9e 100644 --- a/src/agentpool_toolsets/fsspec_toolset/grep.py +++ b/src/wolfharness_toolsets/fsspec_toolset/grep.py @@ -7,8 +7,8 @@ import anyenv -from agentpool.log import get_logger -from agentpool_toolsets.fsspec_toolset.helpers import DEFAULT_MAX_SIZE +from wolfharness.log import get_logger +from wolfharness_toolsets.fsspec_toolset.helpers import DEFAULT_MAX_SIZE if TYPE_CHECKING: diff --git a/src/agentpool_toolsets/fsspec_toolset/helpers.py b/src/wolfharness_toolsets/fsspec_toolset/helpers.py similarity index 99% rename from src/agentpool_toolsets/fsspec_toolset/helpers.py rename to src/wolfharness_toolsets/fsspec_toolset/helpers.py index 0c0c8a072..13acfc312 100644 --- a/src/agentpool_toolsets/fsspec_toolset/helpers.py +++ b/src/wolfharness_toolsets/fsspec_toolset/helpers.py @@ -8,7 +8,7 @@ from pydantic_ai import ModelRetry -from agentpool.log import get_logger +from wolfharness.log import get_logger logger = get_logger(__name__) diff --git a/src/agentpool_toolsets/fsspec_toolset/image_utils.py b/src/wolfharness_toolsets/fsspec_toolset/image_utils.py similarity index 100% rename from src/agentpool_toolsets/fsspec_toolset/image_utils.py rename to src/wolfharness_toolsets/fsspec_toolset/image_utils.py diff --git a/src/agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py b/src/wolfharness_toolsets/fsspec_toolset/streaming_diff_parser.py similarity index 100% rename from src/agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py rename to src/wolfharness_toolsets/fsspec_toolset/streaming_diff_parser.py diff --git a/src/agentpool_toolsets/fsspec_toolset/toolset.py b/src/wolfharness_toolsets/fsspec_toolset/toolset.py similarity index 96% rename from src/agentpool_toolsets/fsspec_toolset/toolset.py rename to src/wolfharness_toolsets/fsspec_toolset/toolset.py index 8bdd77a9e..b52e078da 100644 --- a/src/agentpool_toolsets/fsspec_toolset/toolset.py +++ b/src/wolfharness_toolsets/fsspec_toolset/toolset.py @@ -26,27 +26,27 @@ from sublime_search import replace_content from upathtools import is_directory -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger -from agentpool.mime_utils import guess_type, is_binary_content, is_binary_mime -from agentpool.tool_impls.delete_path import create_delete_path_tool -from agentpool.tool_impls.download_file import create_download_file_tool -from agentpool.tool_impls.grep import create_grep_tool -from agentpool.tool_impls.list_directory import create_list_directory_tool -from agentpool.tool_impls.read import create_read_tool -from agentpool.tools.base import ToolResult # noqa: TC001 -from agentpool.utils.diffs import get_changed_line_numbers -from agentpool_toolsets.fsspec_toolset.diagnostics import ( +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger +from wolfharness.mime_utils import guess_type, is_binary_content, is_binary_mime +from wolfharness.tool_impls.delete_path import create_delete_path_tool +from wolfharness.tool_impls.download_file import create_download_file_tool +from wolfharness.tool_impls.grep import create_grep_tool +from wolfharness.tool_impls.list_directory import create_list_directory_tool +from wolfharness.tool_impls.read import create_read_tool +from wolfharness.tools.base import ToolResult # noqa: TC001 +from wolfharness.utils.diffs import get_changed_line_numbers +from wolfharness_toolsets.fsspec_toolset.diagnostics import ( DiagnosticsConfig, DiagnosticsManager, format_diagnostics_table, ) -from agentpool_toolsets.fsspec_toolset.helpers import ( +from wolfharness_toolsets.fsspec_toolset.helpers import ( format_directory_listing, truncate_lines, ) -from agentpool_toolsets.fsspec_toolset.streaming_diff_parser import ( +from wolfharness_toolsets.fsspec_toolset.streaming_diff_parser import ( NewTextChunk, OldTextChunk, StreamingDiffParser, @@ -60,12 +60,12 @@ from fsspec.asyn import AsyncFileSystem from pydantic_ai import ModelRequest - from agentpool.agents.base_agent import BaseAgent - from agentpool.common_types import ModelType - from agentpool.messaging import MessageHistory - from agentpool.prompts.conversion_manager import ConversionManager - from agentpool.tools.base import Tool - from agentpool_toolsets.fsspec_toolset.grep import GrepBackend + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.common_types import ModelType + from wolfharness.messaging import MessageHistory + from wolfharness.prompts.conversion_manager import ConversionManager + from wolfharness.tools.base import Tool + from wolfharness_toolsets.fsspec_toolset.grep import GrepBackend logger = get_logger(__name__) @@ -241,7 +241,7 @@ async def _get_file_map( Returns: Structure map string or None if language not supported """ - from agentpool.repomap import generate_file_outline + from wolfharness.repomap import generate_file_outline # Use centralized outline generation fs = self._get_fs(agent_ctx) @@ -300,7 +300,7 @@ async def list_directory( # noqa: D417 Returns: Markdown-formatted directory listing """ - from agentpool.agents.events import TextContentItem + from wolfharness.agents.events import TextContentItem path = self._resolve_path(path, agent_ctx) msg = f"Listing directory: {path}" @@ -390,9 +390,9 @@ async def read( # noqa: D417 Text content for text files, BinaryContent for binary files (with optional dimension note as list when image was resized), or dict with error """ - from agentpool.agents.events import FileContentItem, LocationContentItem - from agentpool.repomap import truncate_with_notice - from agentpool_toolsets.fsspec_toolset.image_utils import resize_image_if_needed + from wolfharness.agents.events import FileContentItem, LocationContentItem + from wolfharness.repomap import truncate_with_notice + from wolfharness_toolsets.fsspec_toolset.image_utils import resize_image_if_needed path = self._resolve_path(path, agent_ctx) msg = f"Reading file: {path}" @@ -487,7 +487,7 @@ async def read_as_markdown(self, agent_ctx: AgentContext, path: str) -> str | di Returns: File content converted to markdown """ - from agentpool.agents.events import TextContentItem + from wolfharness.agents.events import TextContentItem assert self.converter is not None, "Converter required for read_as_markdown" path = self._resolve_path(path, agent_ctx) @@ -527,8 +527,8 @@ async def write( # noqa: D417 Returns: Success message or ToolResult with metadata """ - from agentpool.agents.events import DiffContentItem - from agentpool.tools.base import ToolResult + from wolfharness.agents.events import DiffContentItem + from wolfharness.tools.base import ToolResult path = self._resolve_path(path, agent_ctx) msg = f"Writing file: {path}" @@ -728,7 +728,7 @@ async def edit_batch( # noqa: D417 ("old_name()", "new_name()"), # Update call sites ] """ - from agentpool.tools.base import ToolResult + from wolfharness.tools.base import ToolResult path = self._resolve_path(path, agent_ctx) msg = f"Editing file: {path} ({description})" @@ -985,8 +985,8 @@ async def grep( # noqa: D417 Returns: Grep results as formatted text """ - from agentpool.agents.events import TextContentItem - from agentpool_toolsets.fsspec_toolset.grep import ( + from wolfharness.agents.events import TextContentItem + from wolfharness_toolsets.fsspec_toolset.grep import ( DEFAULT_EXCLUDE_PATTERNS, detect_grep_backend, grep_with_fsspec, @@ -1187,7 +1187,7 @@ async def agentic_edit( # noqa: D417 Returns: Success message with edit summary """ - from agentpool.messaging import ChatMessage, MessageHistory + from wolfharness.messaging import ChatMessage, MessageHistory path = self._resolve_path(path, agent_ctx) title = f"AI editing file: {path}" @@ -1554,7 +1554,7 @@ async def _stream_raw_content( def to_opencode_edit_metadata(original_content: str, new_content: str, path: str) -> dict[str, Any]: - from agentpool.utils.diffs import compute_unified_diff + from wolfharness.utils.diffs import compute_unified_diff name = Path(path).name diff = compute_unified_diff( @@ -1653,7 +1653,7 @@ async def main() -> None: from pydantic_ai.models.test import TestModel from upathtools import core - from agentpool import Agent, AgentPool + from wolfharness import Agent, AgentPool fs = core.filesystem("file") tools = FSSpecTools(fs, name="local_fs") @@ -1663,7 +1663,7 @@ async def main() -> None: result = await tools.agentic_edit( PyAiContext(deps=None, model=TestModel(), usage=RunUsage()), agent_ctx, - path="/home/phil65/dev/oss/agentpool/src/agentpool_toolsets/fsspec_toolset/toolset.py", + path="/home/phil65/dev/oss/wolfharness/src/wolfharness_toolsets/fsspec_toolset/toolset.py", display_description="Append a poem", ) print(result) diff --git a/src/agentpool_toolsets/mcp_discovery/__init__.py b/src/wolfharness_toolsets/mcp_discovery/__init__.py similarity index 60% rename from src/agentpool_toolsets/mcp_discovery/__init__.py rename to src/wolfharness_toolsets/mcp_discovery/__init__.py index 57ecd9a77..801548115 100644 --- a/src/agentpool_toolsets/mcp_discovery/__init__.py +++ b/src/wolfharness_toolsets/mcp_discovery/__init__.py @@ -1,5 +1,5 @@ """MCP Discovery Toolset - dynamic MCP server exploration and tool execution.""" -from agentpool_toolsets.mcp_discovery.toolset import MCPDiscoveryToolset +from wolfharness_toolsets.mcp_discovery.toolset import MCPDiscoveryToolset __all__ = ["MCPDiscoveryToolset"] diff --git a/src/agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet b/src/wolfharness_toolsets/mcp_discovery/data/mcp_servers.parquet similarity index 100% rename from src/agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet rename to src/wolfharness_toolsets/mcp_discovery/data/mcp_servers.parquet diff --git a/src/agentpool_toolsets/mcp_discovery/toolset.py b/src/wolfharness_toolsets/mcp_discovery/toolset.py similarity index 96% rename from src/agentpool_toolsets/mcp_discovery/toolset.py rename to src/wolfharness_toolsets/mcp_discovery/toolset.py index 772d6c5de..122c9fa11 100644 --- a/src/agentpool_toolsets/mcp_discovery/toolset.py +++ b/src/wolfharness_toolsets/mcp_discovery/toolset.py @@ -19,11 +19,11 @@ from pydantic import HttpUrl from pydantic_ai import RunContext # noqa: TC002 -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger -from agentpool.mcp_server.client import MCPClient -from agentpool.mcp_server.registries.official_registry_client import ( +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger +from wolfharness.mcp_server.client import MCPClient +from wolfharness.mcp_server.registries.official_registry_client import ( MCPRegistryClient, MCPRegistryError, ) @@ -34,9 +34,9 @@ from fastmcp.client.sampling import SamplingHandler - from agentpool.mcp_server.registries.official_registry_client import RegistryServer - from agentpool.tools.base import Tool - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness.mcp_server.registries.official_registry_client import RegistryServer + from wolfharness.tools.base import Tool + from wolfharness_config.mcp_server import MCPServerConfig logger = get_logger(__name__) @@ -154,7 +154,7 @@ def _is_server_allowed(self, server_name: str) -> bool: async def _get_server_config(self, server_name: str) -> MCPServerConfig: """Get connection config for a server from the registry.""" - from agentpool_config.mcp_server import ( + from wolfharness_config.mcp_server import ( SSEMCPServerConfig, StreamableHTTPMCPServerConfig, ) @@ -462,7 +462,7 @@ async def cleanup(self) -> None: if __name__ == "__main__": import asyncio - from agentpool.agents.native_agent import Agent + from wolfharness.agents.native_agent import Agent async def main() -> None: """End-to-end example: Add MCP discovery toolset to an agent and call a tool.""" diff --git a/src/agentpool_toolsets/mcp_run_toolset.py b/src/wolfharness_toolsets/mcp_run_toolset.py similarity index 96% rename from src/agentpool_toolsets/mcp_run_toolset.py rename to src/wolfharness_toolsets/mcp_run_toolset.py index d7664c170..3814fb793 100644 --- a/src/agentpool_toolsets/mcp_run_toolset.py +++ b/src/wolfharness_toolsets/mcp_run_toolset.py @@ -7,8 +7,8 @@ from schemez import OpenAIFunctionDefinition -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -19,7 +19,7 @@ from mcp import ClientSession from mcp.types import CallToolResult - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool logger = get_logger(__name__) diff --git a/src/agentpool_toolsets/notifications.py b/src/wolfharness_toolsets/notifications.py similarity index 96% rename from src/agentpool_toolsets/notifications.py rename to src/wolfharness_toolsets/notifications.py index 7809acaec..f1deec16b 100644 --- a/src/agentpool_toolsets/notifications.py +++ b/src/wolfharness_toolsets/notifications.py @@ -6,14 +6,14 @@ from schemez.functionschema.typedefs import OpenAIFunctionDefinition -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: from collections.abc import Mapping, Sequence - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool logger = get_logger(__name__) diff --git a/src/agentpool_toolsets/openapi.py b/src/wolfharness_toolsets/openapi.py similarity index 96% rename from src/agentpool_toolsets/openapi.py rename to src/wolfharness_toolsets/openapi.py index 586accc89..9df0fb524 100644 --- a/src/agentpool_toolsets/openapi.py +++ b/src/wolfharness_toolsets/openapi.py @@ -8,8 +8,8 @@ from schemez.openapi.loader import load_openapi_spec, parse_operations from upathtools import read_path, to_upath -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -18,7 +18,7 @@ import httpx from upathtools import JoinablePathLike - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool logger = get_logger(__name__) diff --git a/src/agentpool_toolsets/py.typed b/src/wolfharness_toolsets/py.typed similarity index 100% rename from src/agentpool_toolsets/py.typed rename to src/wolfharness_toolsets/py.typed diff --git a/src/agentpool_toolsets/search_toolset.py b/src/wolfharness_toolsets/search_toolset.py similarity index 94% rename from src/agentpool_toolsets/search_toolset.py rename to src/wolfharness_toolsets/search_toolset.py index 746543995..8ceb6e261 100644 --- a/src/agentpool_toolsets/search_toolset.py +++ b/src/wolfharness_toolsets/search_toolset.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Any -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.log import get_logger +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.log import get_logger if TYPE_CHECKING: @@ -20,8 +20,8 @@ WebSearchResponse, ) - from agentpool.agents.context import AgentContext - from agentpool.tools.base import Tool + from wolfharness.agents.context import AgentContext + from wolfharness.tools.base import Tool logger = get_logger(__name__) @@ -102,7 +102,7 @@ async def web_search( # noqa: D417 Returns: Formatted markdown search results. """ - from agentpool.agents.events import TextContentItem + from wolfharness.agents.events import TextContentItem assert self._web_provider is not None await agent_ctx.events.tool_call_progress( @@ -151,7 +151,7 @@ async def news_search( # noqa: D417 Returns: Formatted markdown news results. """ - from agentpool.agents.events import TextContentItem + from wolfharness.agents.events import TextContentItem assert self._news_provider is not None diff --git a/src/agentpool_toolsets/streaming_tools.py b/src/wolfharness_toolsets/streaming_tools.py similarity index 97% rename from src/agentpool_toolsets/streaming_tools.py rename to src/wolfharness_toolsets/streaming_tools.py index c3d811130..bca5a52fa 100644 --- a/src/agentpool_toolsets/streaming_tools.py +++ b/src/wolfharness_toolsets/streaming_tools.py @@ -52,9 +52,9 @@ async def edit_file( from pydantic_ai.messages import ModelRequest, ModelResponse - from agentpool.agents.base_agent import BaseAgent - from agentpool.agents.context import AgentContext - from agentpool.messaging import MessageHistory + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.agents.context import AgentContext + from wolfharness.messaging import MessageHistory @dataclass @@ -185,7 +185,7 @@ def _create_forked_history(agent: BaseAgent) -> MessageHistory: The fork preserves full context for cache benefits while isolating new messages from the main conversation. """ - from agentpool.messaging import ChatMessage, MessageHistory + from wolfharness.messaging import ChatMessage, MessageHistory if current_history := agent.conversation.get_history(): # Get pydantic-ai messages and inject cache point diff --git a/src/agentpool_toolsets/vfs_toolset.py b/src/wolfharness_toolsets/vfs_toolset.py similarity index 97% rename from src/agentpool_toolsets/vfs_toolset.py rename to src/wolfharness_toolsets/vfs_toolset.py index 52fcd8793..eed5d1161 100644 --- a/src/agentpool_toolsets/vfs_toolset.py +++ b/src/wolfharness_toolsets/vfs_toolset.py @@ -4,8 +4,8 @@ from fnmatch import fnmatch -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability async def vfs_list( # noqa: D417 diff --git a/test_graph_config_loading.py b/test_graph_config_loading.py index 720cd95b0..a041cf7e3 100644 --- a/test_graph_config_loading.py +++ b/test_graph_config_loading.py @@ -12,8 +12,8 @@ import pytest -from agentpool import AgentPool -from agentpool_config.graph_translation import GraphConfig +from wolfharness import AgentPool +from wolfharness_config.graph_translation import GraphConfig if TYPE_CHECKING: @@ -228,8 +228,8 @@ async def test_empty_config_loads(tmp_path: Path) -> None: @pytest.mark.asyncio async def test_programmatic_manifest_with_graph() -> None: """Programmatic manifests with graph in model_extra should work.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest( agents={ diff --git a/test_pool_skills.py b/test_pool_skills.py index 7b22e8ec1..9f06c44b9 100644 --- a/test_pool_skills.py +++ b/test_pool_skills.py @@ -1,6 +1,6 @@ """Test skill resolution with actual AgentPool setup mimicking serve-acp. -Usage (from packages/agentpool): +Usage (from packages/wolfharness): uv run python test_pool_skills.py """ @@ -10,9 +10,9 @@ import sys -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/agentpool/src") +sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") -from agentpool import AgentPool +from wolfharness import AgentPool async def main(): # noqa: PLR0915 diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 4b091fae2..fb394e215 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -24,7 +24,7 @@ AgentPool uses a 4-layer conceptual taxonomy (L1-L4) for discussing test types. | **L1** | Unit | `@pytest.mark.unit` | `TestModel`, `FunctionModel`, mocks | Fast (<100ms) | None | ✅ Always | Logic branches, boundary conditions, data transforms | | **L2** | Integration | `@pytest.mark.integration` | Real `AgentPool` + `TestModel`, FastAPI `TestClient` | Fast (<500ms) | None | ✅ Always | Component wiring, capability registration, event conversion, protocol handlers (mocked deps) | | **L3** | VCR | `@pytest.mark.vcr` | `pytest-recording` + `vcrpy` + cassettes | Fast-Medium (<5s) | None (replay) | ✅ Always | Real model API response format, streaming event sequences, tool call structures, protocol transport | -| **L4** | Subprocess E2E | `@pytest.mark.e2e` | Real `agentpool serve-*` process + protocol client | Slow (5-30s) | Optional | L4a: ✅ PR smoke, L4b: ❌ Nightly | Server startup, stdio transport, process lifecycle, real I/O timing | +| **L4** | Subprocess E2E | `@pytest.mark.e2e` | Real `wolfharness serve-*` process + protocol client | Slow (5-30s) | Optional | L4a: ✅ PR smoke, L4b: ❌ Nightly | Server startup, stdio transport, process lifecycle, real I/O timing | Tests carry exactly ONE primary layer marker. L3 VCR tests that also exercise protocol integration MAY additionally carry `@pytest.mark.integration` as a secondary marker for filtering. The primary marker determines CI stage assignment. @@ -99,7 +99,7 @@ a skipped test is never executed. The test intent MUST be documented in the docs so the test clearly communicates what behavior is expected once the feature is implemented. ```python -@pytest.mark.skip(reason="mcp/connect client-side not implemented in agentpool ACP server yet") +@pytest.mark.skip(reason="mcp/connect client-side not implemented in wolfharness ACP server yet") async def test_mcp_connect_client_side(...): """Send mcp/connect request with a valid MCP server config. Expect result.connected = true and result.server_info containing @@ -434,14 +434,14 @@ Never. Fix the test or fix the code. If a test is legitimately obsolete, mark it | `vcr_config` | module | VCR configuration (header filtering, body decoding) | | `acp_client` | function | ACP client connected to in-process ACP server | | `opencode_client` | function | FastAPI `TestClient` for OpenCode server | -| `subprocess_server` | function | Spawned `agentpool serve-*` process (cached by serve_command; use `--no-server-cache` to bypass) | +| `subprocess_server` | function | Spawned `wolfharness serve-*` process (cached by serve_command; use `--no-server-cache` to bypass) | | `minimal_config` | session | Minimal YAML config with single test agent | | `session_e2e_config` | session | Session-scoped e2e config with in-memory storage (for cached servers) | | `session_e2e_config_with_tool` | session | Session-scoped e2e config with bash tool + in-memory storage | ## Subprocess Server Cache -The `subprocess_server` fixture uses a **session-scoped cache** to reuse `agentpool serve-*` processes across tests with the same `serve_command`. This reduces ~100+ subprocess spawns to ~4-5, cutting L4a e2e time from ~14min to ~4-5min. +The `subprocess_server` fixture uses a **session-scoped cache** to reuse `wolfharness serve-*` processes across tests with the same `serve_command`. This reduces ~100+ subprocess spawns to ~4-5, cutting L4a e2e time from ~14min to ~4-5min. ### How It Works diff --git a/tests/_controller_helpers.py b/tests/_controller_helpers.py index 23a03a7a4..ccaddfbc3 100644 --- a/tests/_controller_helpers.py +++ b/tests/_controller_helpers.py @@ -10,11 +10,11 @@ import time from typing import TYPE_CHECKING, Any -from agentpool.lifecycle.types import DeliveryMode +from wolfharness.lifecycle.types import DeliveryMode if TYPE_CHECKING: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController async def send_via_controller( diff --git a/tests/acp/test_client_handler_session_update.py b/tests/acp/test_client_handler_session_update.py index 91e982f4b..a472982a8 100644 --- a/tests/acp/test_client_handler_session_update.py +++ b/tests/acp/test_client_handler_session_update.py @@ -25,9 +25,9 @@ ToolCallStart, UserMessageChunk, ) -from agentpool.agents.acp_agent import ACPAgent -from agentpool.agents.acp_agent.client_handler import ACPClientHandler -from agentpool.agents.acp_agent.session_state import ACPSessionState +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.agents.acp_agent.client_handler import ACPClientHandler +from wolfharness.agents.acp_agent.session_state import ACPSessionState def _mock_agent() -> MagicMock: @@ -235,7 +235,7 @@ async def test_current_mode_update_emits_signal( handler: ACPClientHandler, mock_agent: MagicMock ) -> None: """CurrentModeUpdate should emit state_updated signal with ModeInfo.""" - from agentpool.agents.modes import ModeInfo + from wolfharness.agents.modes import ModeInfo session_state = ACPSessionState(session_id="test-session") session_state.modes = SessionModeState( diff --git a/tests/acp/test_event_converter_errors.py b/tests/acp/test_event_converter_errors.py index 538acc0dd..5384bb658 100644 --- a/tests/acp/test_event_converter_errors.py +++ b/tests/acp/test_event_converter_errors.py @@ -8,14 +8,14 @@ from pydantic_ai import RequestUsage import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunErrorEvent, RunFailedEvent, StreamCompleteEvent, ToolCallStartEvent, ) -from agentpool.messaging.messages import ChatMessage -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.messaging.messages import ChatMessage +from wolfharness_server.acp_server.event_converter import ACPEventConverter @pytest.fixture diff --git a/tests/acp/test_event_converter_snapshots.py b/tests/acp/test_event_converter_snapshots.py index a7d17034d..5d70528ff 100644 --- a/tests/acp/test_event_converter_snapshots.py +++ b/tests/acp/test_event_converter_snapshots.py @@ -33,13 +33,13 @@ from pydantic_ai.usage import RequestUsage -from agentpool.agents.events import StreamCompleteEvent -from agentpool.messaging.messages import ChatMessage -from agentpool_server.acp_server.event_converter import ACPEventConverter from tests.fixtures.subagent_events import ( TEST_EVENT_SEQUENCES, zed_full_lifecycle_events, ) +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.messaging.messages import ChatMessage +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = pytest.mark.integration @@ -398,7 +398,7 @@ async def test_full_lifecycle( # tool_call_id for snapshot comparison fixed_uuid = UUID("00000000-0000-0000-0000-000000000001") with patch( - "agentpool_server.acp_server.event_converter.uuid.uuid4", + "wolfharness_server.acp_server.event_converter.uuid.uuid4", return_value=fixed_uuid, ): events = zed_full_lifecycle_events() @@ -486,7 +486,7 @@ async def test_zed_mode_tool_call_start_has_kind_subagent(self): Then: The yielded ToolCallStart has kind="other" (subagent context is conveyed via field_meta, not the kind field). """ - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart converter = ACPEventConverter(subagent_display_mode="zed") converter._current_message_id = "test-msg-id" @@ -519,7 +519,7 @@ async def test_zed_mode_build_subagent_completed_has_meta_and_tool_name(self): Then: The yielded ToolCallProgress has field_meta with subagent_session_info and tool_name keys. """ - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart converter = ACPEventConverter(subagent_display_mode="zed") converter._current_message_id = "test-msg-id" diff --git a/tests/acp/test_meta_guardrails.py b/tests/acp/test_meta_guardrails.py index 87b9584e8..c7b279b74 100644 --- a/tests/acp/test_meta_guardrails.py +++ b/tests/acp/test_meta_guardrails.py @@ -14,8 +14,8 @@ import pytest -from agentpool.agents.events import SpawnSessionStart -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events import SpawnSessionStart +from wolfharness_server.acp_server.event_converter import ACPEventConverter @pytest.fixture diff --git a/tests/acp/test_qwen_display_mode.py b/tests/acp/test_qwen_display_mode.py index 70a26e507..781142e68 100644 --- a/tests/acp/test_qwen_display_mode.py +++ b/tests/acp/test_qwen_display_mode.py @@ -15,8 +15,8 @@ import pytest from acp.schema import AgentMessageChunk, ToolCallStart -from agentpool.agents.events import SpawnSessionStart -from agentpool_server.acp_server.event_converter import ( +from wolfharness.agents.events import SpawnSessionStart +from wolfharness_server.acp_server.event_converter import ( ACPEventConverter, SubagentContext, ) diff --git a/tests/acp/test_subagent_display_mode_coerce.py b/tests/acp/test_subagent_display_mode_coerce.py index feb17cfd6..630d8b0e6 100644 --- a/tests/acp/test_subagent_display_mode_coerce.py +++ b/tests/acp/test_subagent_display_mode_coerce.py @@ -10,7 +10,7 @@ import pytest -from agentpool_server.acp_server.server import _coerce_subagent_display_mode +from wolfharness_server.acp_server.server import _coerce_subagent_display_mode # --------------------------------------------------------------------------- @@ -99,7 +99,7 @@ def test_coerce_qwen(caplog: pytest.LogCaptureFixture): @pytest.mark.unit def test_pool_server_config_accepts_qwen(): """ACPPoolServerConfig accepts 'qwen' without ValidationError.""" - from agentpool_config.pool_server import ACPPoolServerConfig + from wolfharness_config.pool_server import ACPPoolServerConfig config = ACPPoolServerConfig(subagent_display_mode="qwen") assert config.subagent_display_mode == "qwen" diff --git a/tests/acp/test_toast_notification.py b/tests/acp/test_toast_notification.py index 82af9c01b..a4158d48e 100644 --- a/tests/acp/test_toast_notification.py +++ b/tests/acp/test_toast_notification.py @@ -6,7 +6,7 @@ import pytest -from agentpool.agents.events import ToastInfo +from wolfharness.agents.events import ToastInfo pytestmark = pytest.mark.unit @@ -27,7 +27,7 @@ def mock_session(self): async def test_send_toast_error(self, mock_session): """Test sending error toast notification.""" - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession # Patch the method onto the mock with patch.object(mock_session, "notifications") as mock_notifs: @@ -37,7 +37,7 @@ async def test_send_toast_error(self, mock_session): level="error", ) mock_notifs.send_ext_notification.assert_called_once_with( - method="_agentpool/toast", + method="_wolfharness/toast", params={ "message": "Test error", "level": "error", @@ -48,7 +48,7 @@ async def test_send_toast_error(self, mock_session): async def test_send_toast_warning(self, mock_session): """Test sending warning toast notification.""" - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession with patch.object(mock_session, "notifications") as mock_notifs: await ACPSession._send_toast( @@ -58,7 +58,7 @@ async def test_send_toast_warning(self, mock_session): duration=5000, ) mock_notifs.send_ext_notification.assert_called_once_with( - method="_agentpool/toast", + method="_wolfharness/toast", params={ "message": "Warning message", "level": "warning", @@ -69,7 +69,7 @@ async def test_send_toast_warning(self, mock_session): async def test_send_toast_with_action(self, mock_session): """Test sending toast with action button.""" - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession with patch.object(mock_session, "notifications") as mock_notifs: await ACPSession._send_toast( @@ -79,7 +79,7 @@ async def test_send_toast_with_action(self, mock_session): action={"label": "Retry", "command": "/retry"}, ) mock_notifs.send_ext_notification.assert_called_once_with( - method="_agentpool/toast", + method="_wolfharness/toast", params={ "message": "Click to retry", "level": "info", @@ -90,7 +90,7 @@ async def test_send_toast_with_action(self, mock_session): async def test_send_toast_cancelled(self, mock_session): """Test that toast is not sent when session is cancelled.""" - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession mock_session._cancelled = True with patch.object(mock_session, "notifications") as mock_notifs: @@ -103,7 +103,7 @@ async def test_send_toast_cancelled(self, mock_session): async def test_send_toast_exception_handled(self, mock_session): """Test that exceptions in send_ext_notification are handled.""" - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession with patch.object(mock_session, "notifications") as mock_notifs: mock_notifs.send_ext_notification.side_effect = RuntimeError("Network error") @@ -129,8 +129,8 @@ def mock_handler(self): return handler async def test_handle_toast_notification(self, mock_handler): - """Test handling _agentpool/toast ext notification.""" - from agentpool.agents.acp_agent.client_handler import ACPClientHandler + """Test handling _wolfharness/toast ext notification.""" + from wolfharness.agents.acp_agent.client_handler import ACPClientHandler params = { "message": "Error occurred", @@ -139,7 +139,7 @@ async def test_handle_toast_notification(self, mock_handler): "action": {"label": "Retry", "command": "/retry"}, } - await ACPClientHandler.ext_notification(mock_handler, "_agentpool/toast", params) + await ACPClientHandler.ext_notification(mock_handler, "_wolfharness/toast", params) # Verify state_updated was emitted with ToastInfo mock_handler._agent.state_updated.emit.assert_called_once() @@ -152,11 +152,11 @@ async def test_handle_toast_notification(self, mock_handler): async def test_handle_toast_notification_defaults(self, mock_handler): """Test handling toast with default values.""" - from agentpool.agents.acp_agent.client_handler import ACPClientHandler + from wolfharness.agents.acp_agent.client_handler import ACPClientHandler params = {"message": "Info message"} - await ACPClientHandler.ext_notification(mock_handler, "_agentpool/toast", params) + await ACPClientHandler.ext_notification(mock_handler, "_wolfharness/toast", params) toast = mock_handler._agent.state_updated.emit.call_args[0][0] assert isinstance(toast, ToastInfo) @@ -167,7 +167,7 @@ async def test_handle_toast_notification_defaults(self, mock_handler): async def test_handle_unknown_ext_notification(self, mock_handler): """Test that unknown ext notifications are ignored gracefully.""" - from agentpool.agents.acp_agent.client_handler import ACPClientHandler + from wolfharness.agents.acp_agent.client_handler import ACPClientHandler await ACPClientHandler.ext_notification(mock_handler, "_unknown/method", {"foo": "bar"}) @@ -191,12 +191,12 @@ async def test_send_ext_notification(self, mock_notifications): await ACPNotifications.send_ext_notification( mock_notifications, - method="_agentpool/toast", + method="_wolfharness/toast", params={"message": "Hello", "level": "info"}, ) mock_notifications.client.ext_notification.assert_called_once_with( - "_agentpool/toast", + "_wolfharness/toast", {"message": "Hello", "level": "info"}, ) @@ -243,7 +243,7 @@ def test_toast_info_full(self): def test_toast_info_in_state_update(self): """Test that ToastInfo can be used as a state update.""" - from agentpool.agents.events import ToastInfo + from wolfharness.agents.events import ToastInfo toast = ToastInfo(message="Test", level="warning") assert isinstance(toast, ToastInfo) diff --git a/tests/acp/test_zed_none_child.py b/tests/acp/test_zed_none_child.py index 1445f2b40..e90c9834a 100644 --- a/tests/acp/test_zed_none_child.py +++ b/tests/acp/test_zed_none_child.py @@ -4,7 +4,7 @@ unknown/missing values) in zed mode does not raise ``KeyError`` or crash, matching the plan requirement in ``tasks.md 6.8``. -Ref: ``src/agentpool_server/acp_server/event_converter.py:690-693`` +Ref: ``src/wolfharness_server/acp_server/event_converter.py:690-693`` """ from __future__ import annotations @@ -13,15 +13,15 @@ from pydantic_ai.usage import RequestUsage import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent, RunErrorEvent, SpawnSessionStart, StreamCompleteEvent, SubAgentEvent, ) -from agentpool.messaging.messages import ChatMessage -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.messaging.messages import ChatMessage +from wolfharness_server.acp_server.event_converter import ACPEventConverter # --------------------------------------------------------------------------- diff --git a/tests/acp/test_zed_subagent_spawn.py b/tests/acp/test_zed_subagent_spawn.py index f11b079d6..ffc0103a4 100644 --- a/tests/acp/test_zed_subagent_spawn.py +++ b/tests/acp/test_zed_subagent_spawn.py @@ -15,16 +15,16 @@ import pytest from acp.schema import ToolCallStart -from agentpool import Agent -from agentpool.agents.context import ( +from wolfharness import Agent +from wolfharness.agents.context import ( MAX_SUBAGENT_DEPTH, AgentContext, AgentRunContext, SubagentDepthError, ) -from agentpool.agents.events import SpawnSessionStart -from agentpool.orchestrator.core import EventBus -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events import SpawnSessionStart +from wolfharness.orchestrator.core import EventBus +from wolfharness_server.acp_server.event_converter import ACPEventConverter # --------------------------------------------------------------------------- @@ -291,7 +291,7 @@ def test_team_py_uses_yield_spawn_pattern() -> None: """ import inspect - from agentpool.delegation import base_team + from wolfharness.delegation import base_team source = inspect.getsource(base_team) diff --git a/tests/acp_server/test_acp_skill_commands.py b/tests/acp_server/test_acp_skill_commands.py index 13028e8e6..f71009ec1 100644 --- a/tests/acp_server/test_acp_skill_commands.py +++ b/tests/acp_server/test_acp_skill_commands.py @@ -11,9 +11,9 @@ import pytest from slashed import Command as SlashedCommand, CommandStore -from agentpool.skills.command import SkillCommand -from agentpool.skills.skill import Skill -from agentpool_server.acp_server.commands.skill_commands import ACPSkillBridge +from wolfharness.skills.command import SkillCommand +from wolfharness.skills.skill import Skill +from wolfharness_server.acp_server.commands.skill_commands import ACPSkillBridge # --------------------------------------------------------------------------- @@ -191,7 +191,7 @@ def _make_minimal_session( def _call_register_skill_commands(session: MagicMock) -> None: """Invoke the real _register_skill_commands method on a mock session.""" # We need to call the unbound method from ACPSession on our mock - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession ACPSession._register_skill_commands(session) # type: ignore[arg-type] diff --git a/tests/acp_server/test_acp_skill_e2e.py b/tests/acp_server/test_acp_skill_e2e.py index f8c364a5a..706293773 100644 --- a/tests/acp_server/test_acp_skill_e2e.py +++ b/tests/acp_server/test_acp_skill_e2e.py @@ -19,10 +19,10 @@ import pytest from slashed import Command as SlashedCommand, CommandStore -from agentpool import Agent, AgentPool -from agentpool.capabilities.change_event import ChangeEvent -from agentpool.skills.skill import Skill -from agentpool_server.acp_server.session import ACPSession +from wolfharness import Agent, AgentPool +from wolfharness.capabilities.change_event import ChangeEvent +from wolfharness.skills.skill import Skill +from wolfharness_server.acp_server.session import ACPSession if TYPE_CHECKING: @@ -53,8 +53,8 @@ def _make_skill( def _make_pool_and_agent() -> tuple[AgentPool, Agent]: """Create a simple pool with one agent.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) pool = AgentPool(manifest) @@ -395,7 +395,7 @@ async def test_regression_manifest_commands_still_work( _mock_skills_on_pool(pool, [_make_skill(name="my-skill")]) # Mock manifest to return a command config - from agentpool_config.commands import StaticCommandConfig + from wolfharness_config.commands import StaticCommandConfig manifest_cmd = StaticCommandConfig( type="static", @@ -463,7 +463,7 @@ async def test_regression_send_update_includes_both_manifest_and_skill_commands( skill = _make_skill(name="skill-cmd", description="A skill command") _mock_skills_on_pool(pool, [skill]) - from agentpool_config.commands import StaticCommandConfig + from wolfharness_config.commands import StaticCommandConfig manifest_cmd = StaticCommandConfig( type="static", @@ -503,7 +503,7 @@ async def test_regression_command_name_collision_last_registered_wins( skill = _make_skill(name=shared_name, description="Skill version of shared command") _mock_skills_on_pool(pool, [skill]) - from agentpool_config.commands import StaticCommandConfig + from wolfharness_config.commands import StaticCommandConfig manifest_cmd = StaticCommandConfig( type="static", diff --git a/tests/acp_server/test_acp_skill_lifecycle.py b/tests/acp_server/test_acp_skill_lifecycle.py index 452424409..719c23587 100644 --- a/tests/acp_server/test_acp_skill_lifecycle.py +++ b/tests/acp_server/test_acp_skill_lifecycle.py @@ -14,9 +14,9 @@ import pytest -from agentpool.capabilities.change_event import ChangeEvent -from agentpool.skills.command import SkillCommand -from agentpool.skills.skill import Skill +from wolfharness.capabilities.change_event import ChangeEvent +from wolfharness.skills.command import SkillCommand +from wolfharness.skills.skill import Skill # --------------------------------------------------------------------------- @@ -71,7 +71,7 @@ def _make_mock_session( """ from slashed import CommandStore - from agentpool_server.acp_server.commands.skill_commands import ACPSkillBridge + from wolfharness_server.acp_server.commands.skill_commands import ACPSkillBridge session = MagicMock() @@ -113,7 +113,7 @@ def _make_mock_session( session.notifications.send_agent_text = AsyncMock() # Bind real methods - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession session._register_skill_commands = ACPSession._register_skill_commands.__get__( session, ACPSession @@ -369,7 +369,7 @@ async def test_skill_executor_loads_instructions_and_injects_into_staged_content Injects into staged_content. """ - from agentpool_server.opencode_server.skill_bridge import create_skill_command + from wolfharness_server.opencode_server.skill_bridge import create_skill_command skill = _make_skill( name="inject-skill", @@ -404,7 +404,7 @@ async def test_skill_executor_with_load_instructions_raising_value_error() -> No Sends 'no instructions' message, no injection. """ - from agentpool_server.opencode_server.skill_bridge import create_skill_command + from wolfharness_server.opencode_server.skill_bridge import create_skill_command # Create a virtual skill (PurePosixPath) with instructions=None # load_instructions() will raise ValueError for virtual skills without pre-set instructions @@ -463,7 +463,7 @@ async def test_injected_prompt_format_uses_correct_xml_tags() -> None: uses + XML tags. """ - from agentpool_server.opencode_server.skill_bridge import create_skill_command + from wolfharness_server.opencode_server.skill_bridge import create_skill_command skill = _make_skill( name="format-skill", diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_end_to_end.py b/tests/agentpool_server/acp_server/test_acp_mcp_end_to_end.py index b4018ee97..5f47ca164 100644 --- a/tests/agentpool_server/acp_server/test_acp_mcp_end_to_end.py +++ b/tests/agentpool_server/acp_server/test_acp_mcp_end_to_end.py @@ -11,11 +11,11 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool_server.acp_server.acp_mcp_manager import ( +from wolfharness_server.acp_server.acp_mcp_manager import ( AcpMcpConnection, AcpMcpConnectionManager, ) -from agentpool_server.acp_server.acp_mcp_transport import AcpMcpTransport +from wolfharness_server.acp_server.acp_mcp_transport import AcpMcpTransport pytestmark = [pytest.mark.unit, pytest.mark.anyio] diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_fastmcp_integration.py b/tests/agentpool_server/acp_server/test_acp_mcp_fastmcp_integration.py index a2fc78ca4..bb5dd5645 100644 --- a/tests/agentpool_server/acp_server/test_acp_mcp_fastmcp_integration.py +++ b/tests/agentpool_server/acp_server/test_acp_mcp_fastmcp_integration.py @@ -38,11 +38,11 @@ from acp.exceptions import RequestError from acp.schema.mcp import AcpMcpServer -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnection -from agentpool_server.acp_server.acp_mcp_transport import AcpMcpTransport +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnection +from wolfharness_server.acp_server.acp_mcp_transport import AcpMcpTransport if TYPE_CHECKING: @@ -66,8 +66,8 @@ def mock_connection(): @pytest.fixture def default_test_agent() -> Agent: """Create a simple test agent with a pool.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest def simple_callback(message: str) -> str: return f"Test response: {message}" diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_manager.py b/tests/agentpool_server/acp_server/test_acp_mcp_manager.py index a1d9f42bd..ca8fb72b2 100644 --- a/tests/agentpool_server/acp_server/test_acp_mcp_manager.py +++ b/tests/agentpool_server/acp_server/test_acp_mcp_manager.py @@ -8,7 +8,7 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool_server.acp_server.acp_mcp_manager import ( +from wolfharness_server.acp_server.acp_mcp_manager import ( AcpMcpConnection, AcpMcpConnectionManager, ) diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_red_flags.py b/tests/agentpool_server/acp_server/test_acp_mcp_red_flags.py index 55c26bd4f..643117840 100644 --- a/tests/agentpool_server/acp_server/test_acp_mcp_red_flags.py +++ b/tests/agentpool_server/acp_server/test_acp_mcp_red_flags.py @@ -13,12 +13,12 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnection +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnection pytestmark = [pytest.mark.unit, pytest.mark.anyio] diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_session_cleanup.py b/tests/agentpool_server/acp_server/test_acp_mcp_session_cleanup.py index eaa367e82..2f5b5c416 100644 --- a/tests/agentpool_server/acp_server/test_acp_mcp_session_cleanup.py +++ b/tests/agentpool_server/acp_server/test_acp_mcp_session_cleanup.py @@ -12,12 +12,12 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.acp_mcp_manager import ( +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.acp_mcp_manager import ( AcpMcpConnection, AcpMcpConnectionManager, ) diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_transport.py b/tests/agentpool_server/acp_server/test_acp_mcp_transport.py index a888c1c21..36cb132a3 100644 --- a/tests/agentpool_server/acp_server/test_acp_mcp_transport.py +++ b/tests/agentpool_server/acp_server/test_acp_mcp_transport.py @@ -3,8 +3,8 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnection -from agentpool_server.acp_server.acp_mcp_transport import AcpMcpTransport +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnection +from wolfharness_server.acp_server.acp_mcp_transport import AcpMcpTransport pytestmark = pytest.mark.integration diff --git a/tests/agentpool_server/acp_server/test_e2e_session_lifecycle.py b/tests/agentpool_server/acp_server/test_e2e_session_lifecycle.py index 4850f657f..0d937d324 100644 --- a/tests/agentpool_server/acp_server/test_e2e_session_lifecycle.py +++ b/tests/agentpool_server/acp_server/test_e2e_session_lifecycle.py @@ -30,9 +30,9 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool.mcp_server.config_snapshot import McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness.mcp_server.config_snapshot import McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager class _FakeTransport: diff --git a/tests/agentpool_server/acp_server/test_resume_active_run.py b/tests/agentpool_server/acp_server/test_resume_active_run.py index e9ec6e179..69b417d6b 100644 --- a/tests/agentpool_server/acp_server/test_resume_active_run.py +++ b/tests/agentpool_server/acp_server/test_resume_active_run.py @@ -15,9 +15,9 @@ import pytest -from agentpool.lifecycle import RunState -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.session_controller import SessionController, SessionState +from wolfharness.lifecycle import RunState +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.session_controller import SessionController, SessionState @pytest.mark.integration diff --git a/tests/agentpool_server/acp_server/test_resume_reconnect.py b/tests/agentpool_server/acp_server/test_resume_reconnect.py index 4e283f26c..ae2cce7e3 100644 --- a/tests/agentpool_server/acp_server/test_resume_reconnect.py +++ b/tests/agentpool_server/acp_server/test_resume_reconnect.py @@ -25,8 +25,8 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool.mcp_server.manager import MCPManager -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager class _FakeTransport: diff --git a/tests/agentpool_server/acp_server/test_resume_session_lifecycle.py b/tests/agentpool_server/acp_server/test_resume_session_lifecycle.py index 77ab61461..129428b74 100644 --- a/tests/agentpool_server/acp_server/test_resume_session_lifecycle.py +++ b/tests/agentpool_server/acp_server/test_resume_session_lifecycle.py @@ -12,9 +12,9 @@ import pytest -from agentpool.mcp_server.manager import MCPManager -from agentpool.sessions import SessionData -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.sessions import SessionData +from wolfharness_server.acp_server.session_manager import ACPSessionManager @pytest.mark.integration @@ -97,7 +97,7 @@ async def mock_old_close() -> None: try: with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", return_value=mock_new_session, ): result = await acp_manager.resume_session( diff --git a/tests/agentpool_server/acp_server/test_websocket_disconnect.py b/tests/agentpool_server/acp_server/test_websocket_disconnect.py index 63e3facab..3303cf1c6 100644 --- a/tests/agentpool_server/acp_server/test_websocket_disconnect.py +++ b/tests/agentpool_server/acp_server/test_websocket_disconnect.py @@ -11,7 +11,7 @@ import pytest -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness_server.acp_server.session_manager import ACPSessionManager def _make_mock_session(session_id: str) -> AsyncMock: diff --git a/tests/agentpool_server/acp_server/test_websocket_disconnect_during_run.py b/tests/agentpool_server/acp_server/test_websocket_disconnect_during_run.py index 8fec052c6..e2ba2ea06 100644 --- a/tests/agentpool_server/acp_server/test_websocket_disconnect_during_run.py +++ b/tests/agentpool_server/acp_server/test_websocket_disconnect_during_run.py @@ -21,10 +21,10 @@ import pytest -from agentpool.lifecycle import RunState -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.session_controller import SessionController, SessionState -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.lifecycle import RunState +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.session_controller import SessionController, SessionState +from wolfharness_server.acp_server.session_manager import ACPSessionManager @pytest.mark.integration diff --git a/tests/agentpool_server/shared/test_model_utils.py b/tests/agentpool_server/shared/test_model_utils.py index 667d291f3..aa9ec905b 100644 --- a/tests/agentpool_server/shared/test_model_utils.py +++ b/tests/agentpool_server/shared/test_model_utils.py @@ -8,8 +8,8 @@ import pytest from tokonomics.model_discovery.model_info import ModelInfo, ModelPricing -from agentpool_server.opencode_server.models import ModelCost, ModelLimit -from agentpool_server.shared.model_utils import ( +from wolfharness_server.opencode_server.models import ModelCost, ModelLimit +from wolfharness_server.shared.model_utils import ( _apply_configured_variants, _build_providers_from_tokonomics, _extract_provider, @@ -72,7 +72,7 @@ class TestExtractProvider: def test_string_config_openai(self) -> None: """Extract provider from StringModelConfig with openai identifier.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig config = StringModelConfig(identifier="openai:gpt-4o") result = _extract_provider(config) @@ -80,7 +80,7 @@ def test_string_config_openai(self) -> None: def test_string_config_anthropic(self) -> None: """Extract provider from StringModelConfig with anthropic identifier.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig config = StringModelConfig(identifier="anthropic:claude-sonnet-4") result = _extract_provider(config) @@ -88,7 +88,7 @@ def test_string_config_anthropic(self) -> None: def test_string_config_no_provider(self) -> None: """Return unknown for StringModelConfig without provider prefix.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig config = StringModelConfig(identifier="gpt-4o") result = _extract_provider(config) @@ -96,7 +96,7 @@ def test_string_config_no_provider(self) -> None: def test_anthropic_config(self) -> None: """Return anthropic for AnthropicModelConfig.""" - from agentpool.models.model_configs import AnthropicModelConfig + from wolfharness.models.model_configs import AnthropicModelConfig config = AnthropicModelConfig(identifier="claude-opus-4-5") result = _extract_provider(config) @@ -104,7 +104,7 @@ def test_anthropic_config(self) -> None: def test_openai_config(self) -> None: """Return openai for OpenAIModelConfig.""" - from agentpool.models.model_configs import OpenAIModelConfig + from wolfharness.models.model_configs import OpenAIModelConfig config = OpenAIModelConfig(identifier="gpt-5.1-chat-latest") result = _extract_provider(config) @@ -112,7 +112,7 @@ def test_openai_config(self) -> None: def test_gemini_config(self) -> None: """Return google for GeminiModelConfig.""" - from agentpool.models.model_configs import GeminiModelConfig + from wolfharness.models.model_configs import GeminiModelConfig config = GeminiModelConfig(identifier="gemini-2.0-flash") result = _extract_provider(config) @@ -120,7 +120,7 @@ def test_gemini_config(self) -> None: def test_fallback_config_first_string(self) -> None: """Extract provider from first model in FallbackModelConfig.""" - from agentpool.models.model_configs import FallbackModelConfig, StringModelConfig + from wolfharness.models.model_configs import FallbackModelConfig, StringModelConfig config = FallbackModelConfig(models=[StringModelConfig(identifier="openai:gpt-4o")]) result = _extract_provider(config) @@ -128,7 +128,7 @@ def test_fallback_config_first_string(self) -> None: def test_fallback_config_first_anthropic(self) -> None: """Extract anthropic when first model is AnthropicModelConfig.""" - from agentpool.models.model_configs import AnthropicModelConfig, FallbackModelConfig + from wolfharness.models.model_configs import AnthropicModelConfig, FallbackModelConfig config = FallbackModelConfig(models=[AnthropicModelConfig(identifier="claude-opus-4-5")]) result = _extract_provider(config) @@ -137,7 +137,7 @@ def test_fallback_config_first_anthropic(self) -> None: def test_fallback_config_empty_models(self) -> None: """Return unknown for FallbackModelConfig with empty models list - minimum 1 required.""" # Note: FallbackModelConfig requires at least 1 model, so we test with String instead - from agentpool.models.model_configs import FallbackModelConfig + from wolfharness.models.model_configs import FallbackModelConfig # Single model fallback with unknown provider string config = FallbackModelConfig(models=["unknown-model-name"]) @@ -146,7 +146,7 @@ def test_fallback_config_empty_models(self) -> None: def test_fallback_config_nested_fallback(self) -> None: """Handle nested FallbackModelConfig.""" - from agentpool.models.model_configs import ( + from wolfharness.models.model_configs import ( AnthropicModelConfig, FallbackModelConfig, ) @@ -247,7 +247,7 @@ class TestApplyConfiguredVariants: @pytest.fixture def sample_provider(self) -> Any: """Create a sample Provider for testing.""" - from agentpool_server.opencode_server.models import Model, Provider + from wolfharness_server.opencode_server.models import Model, Provider model = Model( id="gpt-4o", @@ -387,7 +387,7 @@ class TestResolveModelInfoFromResponse: def test_variant_match(self) -> None: """Resolve raw API names to variant name and configured provider.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig # get_model() canonicalizes "openai-chat" → "openai", so # _resolve_variant_identifier() returns "openai:svc/kimi-k2". @@ -399,7 +399,7 @@ def test_variant_match(self) -> None: def test_variant_match_with_explicit_provider(self) -> None: """Resolve to variant name with explicit provider field in config.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig variants = { "kimi-k2": StringModelConfig( @@ -412,7 +412,7 @@ def test_variant_match_with_explicit_provider(self) -> None: def test_no_match_fallback(self) -> None: """Fall back to raw names when no variant matches.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig variants = {"kimi-k2": StringModelConfig(identifier="openai-chat:svc/kimi-k2")} result = resolve_model_info_from_response("gpt-4o", "openai", variants) @@ -425,25 +425,25 @@ def test_empty_variants(self) -> None: def test_none_model_name(self) -> None: """Return ('unknown', provider_name) when model_name is None.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig variants = {"kimi-k2": StringModelConfig(identifier="openai-chat:svc/kimi-k2")} result = resolve_model_info_from_response(None, "openai-chat", variants) assert result == ("unknown", "openai-chat") def test_none_provider_name(self) -> None: - """Return (model_name, 'agentpool') when provider_name is None.""" + """Return (model_name, 'wolfharness') when provider_name is None.""" result = resolve_model_info_from_response("gpt-4o", None, {}) - assert result == ("gpt-4o", "agentpool") + assert result == ("gpt-4o", "wolfharness") def test_both_none(self) -> None: - """Return ('unknown', 'agentpool') when both inputs are None.""" + """Return ('unknown', 'wolfharness') when both inputs are None.""" result = resolve_model_info_from_response(None, None, {}) - assert result == ("unknown", "agentpool") + assert result == ("unknown", "wolfharness") def test_non_string_config_variant(self) -> None: """Test matching with OpenAIModelConfig (non-StringModelConfig).""" - from agentpool.models.model_configs import OpenAIModelConfig + from wolfharness.models.model_configs import OpenAIModelConfig # OpenAIModelConfig resolves to variant_name as-is per _resolve_variant_identifier variants = {"gpt-5": OpenAIModelConfig(identifier="gpt-5.1-chat-latest")} @@ -457,7 +457,7 @@ def test_non_string_config_variant(self) -> None: def test_multiple_variants_same_identifier_first_wins(self) -> None: """First variant in dict order wins when multiple match the same identifier.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig config = StringModelConfig(identifier="openai-chat:svc/kimi-k2") variants = {"first": config, "second": config} diff --git a/tests/agents/acp_agent/test_acp_agent_load_session.py b/tests/agents/acp_agent/test_acp_agent_load_session.py index 8ab523429..56f6c33a2 100644 --- a/tests/agents/acp_agent/test_acp_agent_load_session.py +++ b/tests/agents/acp_agent/test_acp_agent_load_session.py @@ -14,9 +14,9 @@ SessionModeState, UserMessageChunk, ) -from agentpool.agents.acp_agent import ACPAgent -from agentpool.agents.acp_agent.session_state import ACPSessionState -from agentpool.sessions.models import SessionData +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.agents.acp_agent.session_state import ACPSessionState +from wolfharness.sessions.models import SessionData @pytest.fixture diff --git a/tests/agents/acp_agent/test_acp_converters.py b/tests/agents/acp_agent/test_acp_converters.py index 7384c88fc..b536528fd 100644 --- a/tests/agents/acp_agent/test_acp_converters.py +++ b/tests/agents/acp_agent/test_acp_converters.py @@ -20,7 +20,7 @@ ToolCallStart, UserMessageChunk, ) -from agentpool.agents.acp_agent.acp_converters import ( +from wolfharness.agents.acp_agent.acp_converters import ( ACPMessageAccumulator, acp_notifications_to_messages, ) diff --git a/tests/agents/acp_agent/test_acp_turn_hooks.py b/tests/agents/acp_agent/test_acp_turn_hooks.py index eb9768f84..64aba9f31 100644 --- a/tests/agents/acp_agent/test_acp_turn_hooks.py +++ b/tests/agents/acp_agent/test_acp_turn_hooks.py @@ -19,12 +19,12 @@ ToolCallStart, TurnCompleteUpdate, ) -from agentpool.agents.acp_agent.turn import ACPTurn -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.acp_agent.turn import ACPTurn +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( StreamCompleteEvent, ) -from agentpool.hooks import AgentHooks, CallableHook, HookResult +from wolfharness.hooks import AgentHooks, CallableHook, HookResult # --------------------------------------------------------------------------- diff --git a/tests/agents/acp_agent/test_create_turn.py b/tests/agents/acp_agent/test_create_turn.py index b28af0f41..c37bc1247 100644 --- a/tests/agents/acp_agent/test_create_turn.py +++ b/tests/agents/acp_agent/test_create_turn.py @@ -8,11 +8,11 @@ from acp import InitializeRequest from acp.agent.acp_agent_api import ACPAgentAPI -from agentpool.agents.acp_agent import ACPAgent -from agentpool.agents.acp_agent.client_handler import TimeoutableEvent -from agentpool.agents.acp_agent.session_state import ACPSessionState -from agentpool.agents.acp_agent.turn import ACPClientProtocol, ACPTurn -from agentpool.agents.context import AgentRunContext +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.agents.acp_agent.client_handler import TimeoutableEvent +from wolfharness.agents.acp_agent.session_state import ACPSessionState +from wolfharness.agents.acp_agent.turn import ACPClientProtocol, ACPTurn +from wolfharness.agents.context import AgentRunContext @pytest.mark.unit diff --git a/tests/agents/acp_agent/test_filter_servers.py b/tests/agents/acp_agent/test_filter_servers.py index d5006f8bd..28485a18c 100644 --- a/tests/agents/acp_agent/test_filter_servers.py +++ b/tests/agents/acp_agent/test_filter_servers.py @@ -7,7 +7,7 @@ from acp.schema import AgentCapabilities from acp.schema.mcp import AcpMcpServer, HttpMcpServer, StdioMcpServer -from agentpool.agents.acp_agent.helpers import filter_servers_by_capabilities +from wolfharness.agents.acp_agent.helpers import filter_servers_by_capabilities pytestmark = pytest.mark.unit diff --git a/tests/agents/acp_agent/test_load_session_manual.py b/tests/agents/acp_agent/test_load_session_manual.py index 7080bebf1..2b616bc4c 100644 --- a/tests/agents/acp_agent/test_load_session_manual.py +++ b/tests/agents/acp_agent/test_load_session_manual.py @@ -3,10 +3,10 @@ Run with: uv run python tests/agents/acp_agent/test_load_session_manual.py This tests the full flow: -1. Client connects to agentpool ACP server (wrapping Claude Code) +1. Client connects to wolfharness ACP server (wrapping Claude Code) 2. Lists sessions from Claude Code 3. Loads a session - server delegates to Claude Code's load_session -4. Claude Code replays notifications -> agentpool forwards -> client receives +4. Claude Code replays notifications -> wolfharness forwards -> client receives 5. Client's conversation.chat_messages gets populated Logs are written to /tmp/load_session_test.log for inspection. @@ -21,7 +21,7 @@ import pytest -from agentpool.agents.acp_agent import ACPAgent +from wolfharness.agents.acp_agent import ACPAgent pytestmark = pytest.mark.unit @@ -37,8 +37,8 @@ logging.StreamHandler(), # Also print to console ], ) -logging.getLogger("agentpool").setLevel(logging.DEBUG) -logging.getLogger("agentpool_server").setLevel(logging.DEBUG) +logging.getLogger("wolfharness").setLevel(logging.DEBUG) +logging.getLogger("wolfharness_server").setLevel(logging.DEBUG) logging.getLogger("acp").setLevel(logging.DEBUG) print(f"Logs will be written to: {LOG_FILE}") @@ -46,13 +46,13 @@ async def main() -> None: """Test load_session flow.""" - # Connect to agentpool ACP server running Claude Code agent + # Connect to wolfharness ACP server running Claude Code agent print("Starting ACPAgent...") async with ACPAgent( command="uv", - args=["run", "agentpool", "serve-acp", "tests/agents/acp_agent/claude_code_config.yml"], + args=["run", "wolfharness", "serve-acp", "tests/agents/acp_agent/claude_code_config.yml"], name="test_client", - cwd="/home/phil65/dev/oss/agentpool", + cwd="/home/phil65/dev/oss/wolfharness", ) as agent: print(f"Connected to ACP server, session_id: {agent._sdk_session_id}") diff --git a/tests/agents/acp_agent/test_step_usage.py b/tests/agents/acp_agent/test_step_usage.py index 5b4f4e41d..a93f800c4 100644 --- a/tests/agents/acp_agent/test_step_usage.py +++ b/tests/agents/acp_agent/test_step_usage.py @@ -24,10 +24,10 @@ ToolCallStart, UsageUpdate, ) -from agentpool.agents.acp_agent.acp_converters import acp_to_native_event -from agentpool.agents.acp_agent.turn import ACPTurn -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import StepUsageEvent +from wolfharness.agents.acp_agent.acp_converters import acp_to_native_event +from wolfharness.agents.acp_agent.turn import ACPTurn +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import StepUsageEvent if TYPE_CHECKING: @@ -168,7 +168,7 @@ def test_mixed_stream_with_step_index_progression() -> None: class _MockACPClient: """Mock ACP client that yields a predetermined sequence of session updates. - Implements the :class:`~agentpool.agents.acp_agent.turn.ACPClientProtocol` + Implements the :class:`~wolfharness.agents.acp_agent.turn.ACPClientProtocol` with a fixed list of ``SessionUpdate`` objects for ``stream_events()`` and an empty list for ``get_messages()``. """ diff --git a/tests/agents/acp_agent/test_turn.py b/tests/agents/acp_agent/test_turn.py index 32e972c4a..b7aeb0d57 100644 --- a/tests/agents/acp_agent/test_turn.py +++ b/tests/agents/acp_agent/test_turn.py @@ -12,9 +12,9 @@ AgentMessageChunk, TextContentBlock, ) -from agentpool.agents.acp_agent.turn import ACPTurn -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.acp_agent.turn import ACPTurn +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( PartDeltaEvent, RunErrorEvent, StreamCompleteEvent, diff --git a/tests/agents/acp_agent/test_turn_integration.py b/tests/agents/acp_agent/test_turn_integration.py index 564570a75..e9c202bf9 100644 --- a/tests/agents/acp_agent/test_turn_integration.py +++ b/tests/agents/acp_agent/test_turn_integration.py @@ -14,17 +14,17 @@ from acp import InitializeRequest from acp.schema import AgentMessageChunk, TextContentBlock -from agentpool.agents.acp_agent import ACPAgent -from agentpool.agents.acp_agent.turn import ACPTurn -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.agents.acp_agent.turn import ACPTurn +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( PartDeltaEvent, StreamCompleteEvent, ) -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.turn import Turn +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.turn import Turn if TYPE_CHECKING: diff --git a/tests/agents/events/test_deferred_events.py b/tests/agents/events/test_deferred_events.py index 9ad776ac9..c24d77392 100644 --- a/tests/agents/events/test_deferred_events.py +++ b/tests/agents/events/test_deferred_events.py @@ -7,7 +7,7 @@ import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( RichAgentStreamEvent, SessionResumeEvent, ToolCallDeferredEvent, diff --git a/tests/agents/events/test_event_bus_scopes.py b/tests/agents/events/test_event_bus_scopes.py index 924c42757..051d73c3f 100644 --- a/tests/agents/events/test_event_bus_scopes.py +++ b/tests/agents/events/test_event_bus_scopes.py @@ -12,10 +12,10 @@ import pytest -from agentpool import Agent -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events import RunStartedEvent, StreamEventEmitter -from agentpool.orchestrator.core import EventBus +from wolfharness import Agent +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events import RunStartedEvent, StreamEventEmitter +from wolfharness.orchestrator.core import EventBus def _stream_empty(queue: asyncio.Queue[Any]) -> bool: @@ -143,11 +143,11 @@ async def test_descendant_scope_child_does_not_receive_parent() -> None: @pytest.mark.anyio async def test_descendant_scope_with_session_controller() -> None: """Descendant scope works when using a SessionController for hierarchy queries.""" - from agentpool import AgentPool, AgentsManifest, NativeAgentConfig + from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig manifest = AgentsManifest(agents={"agent1": NativeAgentConfig(name="agent1", model="test")}) async with AgentPool(manifest) as pool: - from agentpool.orchestrator.core import SessionController + from wolfharness.orchestrator.core import SessionController controller = SessionController(pool) event_bus = EventBus(max_queue_size=10, session_controller=controller) diff --git a/tests/agents/events/test_file_ops_message_id.py b/tests/agents/events/test_file_ops_message_id.py index 741450182..5d7323953 100644 --- a/tests/agents/events/test_file_ops_message_id.py +++ b/tests/agents/events/test_file_ops_message_id.py @@ -13,10 +13,10 @@ import pytest -from agentpool import Agent -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events import DiffContentItem, StreamEventEmitter -from agentpool.utils.streams import FileOpsTracker +from wolfharness import Agent +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events import DiffContentItem, StreamEventEmitter +from wolfharness.utils.streams import FileOpsTracker @pytest.mark.unit diff --git a/tests/agents/events/test_user_message_inserted_event.py b/tests/agents/events/test_user_message_inserted_event.py index e2b0e8894..85e7a5c12 100644 --- a/tests/agents/events/test_user_message_inserted_event.py +++ b/tests/agents/events/test_user_message_inserted_event.py @@ -7,7 +7,7 @@ import pytest -from agentpool.agents.events.events import UserMessageInsertedEvent +from wolfharness.agents.events.events import UserMessageInsertedEvent pytestmark = [pytest.mark.unit] diff --git a/tests/agents/native_agent/test_agent_instructions.py b/tests/agents/native_agent/test_agent_instructions.py index 774b0742b..ad67b1fb6 100644 --- a/tests/agents/native_agent/test_agent_instructions.py +++ b/tests/agents/native_agent/test_agent_instructions.py @@ -7,8 +7,8 @@ from pydantic_ai import Agent as PydanticAgent import pytest -from agentpool.agents.native_agent import Agent -from agentpool.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.agents.native_agent import Agent +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability pytestmark = pytest.mark.unit @@ -175,7 +175,7 @@ def get_instructions(self) -> str | None: async def test_from_config_with_provider_instruction_ref(self): """Test from_config with ProviderInstructionConfig using ref.""" - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig class SimpleRefProvider(FunctionToolsetCapability): def __init__(self) -> None: diff --git a/tests/agents/native_agent/test_approval_bridge.py b/tests/agents/native_agent/test_approval_bridge.py index 726c1d0a1..dbc0a2bb6 100644 --- a/tests/agents/native_agent/test_approval_bridge.py +++ b/tests/agents/native_agent/test_approval_bridge.py @@ -20,9 +20,9 @@ ) import pytest -from agentpool import Agent -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.native_agent.approval_bridge import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.native_agent.approval_bridge import ( _map_confirmation_result, _resolve_deferred_approvals, create_approval_bridge_capability, @@ -315,7 +315,7 @@ async def test_get_agentlet_includes_approval_bridge_capability( """get_agentlet() includes the approval bridge capability.""" from pydantic_ai.capabilities import HandleDeferredToolCalls - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) diff --git a/tests/agents/native_agent/test_capability_chain.py b/tests/agents/native_agent/test_capability_chain.py index 6566736ad..fe374b56b 100644 --- a/tests/agents/native_agent/test_capability_chain.py +++ b/tests/agents/native_agent/test_capability_chain.py @@ -13,7 +13,7 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit @@ -41,16 +41,16 @@ async def test_deferred_bridge_before_approval_bridge( # uses local imports. with ( patch( - "agentpool.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", + "wolfharness.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", ) as mock_create_deferred, patch( - "agentpool.agents.native_agent.elicitation_bridge.create_elicitation_bridge_capability", + "wolfharness.agents.native_agent.elicitation_bridge.create_elicitation_bridge_capability", ) as mock_create_elicitation, patch( - "agentpool.agents.native_agent.approval_bridge.create_approval_bridge_capability", + "wolfharness.agents.native_agent.approval_bridge.create_approval_bridge_capability", ) as mock_create_approval, patch( - "agentpool.agents.native_agent.agent.PydanticAgent", + "wolfharness.agents.native_agent.agent.PydanticAgent", ) as mock_pydantic_agent, ): # Create distinguishable mock capabilities @@ -121,13 +121,13 @@ async def test_deferred_bridge_receives_deferred_tools_mapping( with ( patch( - "agentpool.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", + "wolfharness.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", ) as mock_create_deferred, patch( - "agentpool.agents.native_agent.approval_bridge.create_approval_bridge_capability", + "wolfharness.agents.native_agent.approval_bridge.create_approval_bridge_capability", ) as mock_create_approval, patch( - "agentpool.agents.native_agent.agent.PydanticAgent", + "wolfharness.agents.native_agent.agent.PydanticAgent", ) as mock_pydantic_agent, ): deferred_cap = MagicMock(spec=HandleDeferredToolCalls) @@ -176,13 +176,13 @@ async def test_deferred_bridge_empty_when_no_deferred_tools( with ( patch( - "agentpool.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", + "wolfharness.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", ) as mock_create_deferred, patch( - "agentpool.agents.native_agent.approval_bridge.create_approval_bridge_capability", + "wolfharness.agents.native_agent.approval_bridge.create_approval_bridge_capability", ) as mock_create_approval, patch( - "agentpool.agents.native_agent.agent.PydanticAgent", + "wolfharness.agents.native_agent.agent.PydanticAgent", ) as mock_pydantic_agent, ): deferred_cap = MagicMock(spec=HandleDeferredToolCalls) @@ -215,13 +215,13 @@ async def test_deferred_bridge_handles_get_tools_failure( with ( patch( - "agentpool.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", + "wolfharness.agents.native_agent.deferred_bridge.create_deferred_bridge_capability", ) as mock_create_deferred, patch( - "agentpool.agents.native_agent.approval_bridge.create_approval_bridge_capability", + "wolfharness.agents.native_agent.approval_bridge.create_approval_bridge_capability", ) as mock_create_approval, patch( - "agentpool.agents.native_agent.agent.PydanticAgent", + "wolfharness.agents.native_agent.agent.PydanticAgent", ) as mock_pydantic_agent, ): deferred_cap = MagicMock(spec=HandleDeferredToolCalls) diff --git a/tests/agents/native_agent/test_checkpoint.py b/tests/agents/native_agent/test_checkpoint.py index 7a81442f4..b9a4b5266 100644 --- a/tests/agents/native_agent/test_checkpoint.py +++ b/tests/agents/native_agent/test_checkpoint.py @@ -20,20 +20,20 @@ ) import pytest -from agentpool.agents.native_agent.checkpoint import ( +from wolfharness.agents.native_agent.checkpoint import ( CheckpointData, CheckpointManager, MidStreamCheckpointError, ) -from agentpool.sessions.models import PendingDeferredCall -from agentpool.storage.manager import StorageManager +from wolfharness.sessions.models import PendingDeferredCall +from wolfharness.storage.manager import StorageManager pytestmark = pytest.mark.unit if TYPE_CHECKING: - from agentpool.agents.events.events import ToolCallDeferredEvent + from wolfharness.agents.events.events import ToolCallDeferredEvent class TestCheckpointData: @@ -68,7 +68,7 @@ def test_create_with_data(self) -> None: @pytest.fixture def storage_manager() -> StorageManager: """Create a StorageManager with no providers (for unit testing).""" - from agentpool_config.storage import StorageConfig + from wolfharness_config.storage import StorageConfig config = StorageConfig(providers=[]) return StorageManager(config=config) @@ -443,7 +443,7 @@ def test_serialize_messages_roundtrip( json_str = CheckpointManager._serialize_messages(sample_messages) assert json_str is not None - from agentpool.storage.serialization import deserialize_messages + from wolfharness.storage.serialization import deserialize_messages restored = deserialize_messages(json_str) assert len(restored) == len(sample_messages) @@ -612,7 +612,7 @@ async def test_success_logs_saved( @pytest.mark.anyio async def test_storage_manager_returns_success_flag(self) -> None: """StorageManager.save_checkpoint() returns True when any provider succeeds.""" - from agentpool_config.storage import StorageConfig + from wolfharness_config.storage import StorageConfig config = StorageConfig(providers=[]) mgr = StorageManager(config=config) @@ -628,7 +628,7 @@ async def test_storage_manager_returns_success_flag(self) -> None: @pytest.mark.anyio async def test_storage_manager_returns_failure_flag(self) -> None: """StorageManager.save_checkpoint() returns False when all providers fail.""" - from agentpool_config.storage import StorageConfig + from wolfharness_config.storage import StorageConfig config = StorageConfig(providers=[]) mgr = StorageManager(config=config) @@ -644,7 +644,7 @@ async def test_storage_manager_returns_failure_flag(self) -> None: @pytest.mark.anyio async def test_storage_manager_returns_false_no_providers(self) -> None: """StorageManager.save_checkpoint() returns False when no providers configured.""" - from agentpool_config.storage import StorageConfig + from wolfharness_config.storage import StorageConfig config = StorageConfig(providers=[]) mgr = StorageManager(config=config) diff --git a/tests/agents/native_agent/test_confirmation.py b/tests/agents/native_agent/test_confirmation.py index 18a8b4bcd..b35132d47 100644 --- a/tests/agents/native_agent/test_confirmation.py +++ b/tests/agents/native_agent/test_confirmation.py @@ -20,9 +20,9 @@ from pydantic_graph import End import pytest -from agentpool import Agent -from agentpool.agents.context import AgentContext -from agentpool.tools.base import Tool +from wolfharness import Agent +from wolfharness.agents.context import AgentContext +from wolfharness.tools.base import Tool # ============================================================================ @@ -388,7 +388,7 @@ async def test_multiple_confirmation_tools_same_run( mock_agent._builtin_provider.register_tool(confirmation_tool_1) mock_agent._builtin_provider.register_tool(confirmation_tool_2) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: def side_effect(**kwargs: Any) -> MagicMock: capabilities = kwargs.get("capabilities", []) or [] @@ -417,7 +417,7 @@ async def test_mixed_approval_denial_same_run( mock_agent._builtin_provider.register_tool(confirmation_tool_1) mock_agent._builtin_provider.register_tool(confirmation_tool_2) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: def side_effect(**kwargs: Any) -> MagicMock: capabilities = kwargs.get("capabilities", []) or [] @@ -448,7 +448,7 @@ async def test_never_mode_auto_approves_all_tools( mock_agent._builtin_provider.register_tool(confirmation_tool_1) mock_agent._builtin_provider.register_tool(confirmation_tool_2) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: def side_effect(**kwargs: Any) -> MagicMock: capabilities = kwargs.get("capabilities", []) or [] @@ -474,7 +474,7 @@ async def test_abort_run_stops_subsequent_confirmations( mock_agent._builtin_provider.register_tool(confirmation_tool_1) mock_agent._builtin_provider.register_tool(confirmation_tool_2) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: def side_effect(**kwargs: Any) -> MagicMock: capabilities = kwargs.get("capabilities", []) or [] diff --git a/tests/agents/native_agent/test_deferred_bridge.py b/tests/agents/native_agent/test_deferred_bridge.py index 542bc2863..037279806 100644 --- a/tests/agents/native_agent/test_deferred_bridge.py +++ b/tests/agents/native_agent/test_deferred_bridge.py @@ -19,8 +19,8 @@ ) import pytest -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events.events import ToolCallDeferredEvent +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events.events import ToolCallDeferredEvent pytestmark = pytest.mark.unit @@ -103,7 +103,7 @@ class TestCreateDeferredBridgeCapability: def test_returns_handle_deferred_tool_calls(self) -> None: """Factory returns a HandleDeferredToolCalls capability.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -112,7 +112,7 @@ def test_returns_handle_deferred_tool_calls(self) -> None: def test_handler_is_callable(self) -> None: """Handler function is properly set on the capability.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -126,7 +126,7 @@ async def test_empty_deferred_tools_returns_none( block_tool_call: ToolCallPart, ) -> None: """When deferred_tools is empty, all calls pass through (return None).""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -153,7 +153,7 @@ async def test_block_tool_emits_deferred_event( block_tool_call: ToolCallPart, ) -> None: """Block-strategy tool emits ToolCallDeferredEvent.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -163,7 +163,7 @@ async def test_block_tool_emits_deferred_event( # We need to check that the event is emitted via the event_bus. # Patch the _emit_deferred_event to verify it's called correctly. with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ) as mock_emit: await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -188,7 +188,7 @@ async def test_block_tool_excluded_from_results( block_tool_call: ToolCallPart, ) -> None: """Block-strategy tool is EXCLUDED from returned DeferredToolResults.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -196,7 +196,7 @@ async def test_block_tool_excluded_from_results( cap = create_deferred_bridge_capability(deferred_tools=deferred_tools) with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ): result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -216,7 +216,7 @@ async def test_block_tool_remaining_in_requests( block_tool_call: ToolCallPart, ) -> None: """After results applied, block tool remains in remaining requests.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -224,7 +224,7 @@ async def test_block_tool_remaining_in_requests( cap = create_deferred_bridge_capability(deferred_tools=deferred_tools) with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ): result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -243,7 +243,7 @@ async def test_multiple_block_tools_all_excluded( deferred_tools: dict[str, str], ) -> None: """All block-strategy tools are excluded from results.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -255,7 +255,7 @@ async def test_multiple_block_tools_all_excluded( cap = create_deferred_bridge_capability(deferred_tools=deferred_tools) with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ) as mock_emit: result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -285,7 +285,7 @@ async def test_continue_tool_resolved_with_placeholder( continue_tool_call: ToolCallPart, ) -> None: """Continue-strategy tool is resolved with a ToolReturn placeholder.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -308,7 +308,7 @@ async def test_continue_tool_not_remaining( continue_tool_call: ToolCallPart, ) -> None: """Continue-strategy tool is fully resolved — no remaining requests.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -329,7 +329,7 @@ async def test_continue_tool_does_not_emit_deferred_event( continue_tool_call: ToolCallPart, ) -> None: """Continue-strategy tools do NOT emit ToolCallDeferredEvent.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -337,7 +337,7 @@ async def test_continue_tool_does_not_emit_deferred_event( cap = create_deferred_bridge_capability(deferred_tools=deferred_tools) with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ) as mock_emit: await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -362,7 +362,7 @@ async def test_mixed_block_and_continue( continue_tool_call: ToolCallPart, ) -> None: """Block excluded, continue resolved with placeholder.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -370,7 +370,7 @@ async def test_mixed_block_and_continue( cap = create_deferred_bridge_capability(deferred_tools=deferred_tools) with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ) as mock_emit: result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -403,7 +403,7 @@ async def test_mixed_deferred_and_non_deferred( non_deferred_tool_call: ToolCallPart, ) -> None: """Non-deferred tools excluded from results → pass through to next capability.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -411,7 +411,7 @@ async def test_mixed_deferred_and_non_deferred( cap = create_deferred_bridge_capability(deferred_tools=deferred_tools) with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ): result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -445,7 +445,7 @@ async def test_non_deferred_tool_returns_none( non_deferred_tool_call: ToolCallPart, ) -> None: """Non-deferred tools cause the bridge to return None.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -462,7 +462,7 @@ async def test_empty_requests_returns_none( deferred_tools: dict[str, str], ) -> None: """Empty requests return None.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -480,7 +480,7 @@ async def test_only_approvals_no_deferred_tools_returns_none( non_deferred_tool_call: ToolCallPart, ) -> None: """Approval-only requests (not in deferred_tools) return None.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -509,7 +509,7 @@ async def test_unapproved_block_tool_in_approvals_passes_through( run_ctx: RunContext[Any], ) -> None: """Deferred unapproved tool with block strategy in approvals → pass through.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -526,7 +526,7 @@ async def test_unapproved_block_tool_in_approvals_passes_through( # Approvals are NOT handled by deferred bridge — pass through to approval_bridge with patch( - "agentpool.agents.native_agent.deferred_bridge._emit_deferred_event", + "wolfharness.agents.native_agent.deferred_bridge._emit_deferred_event", new_callable=AsyncMock, ) as mock_emit: result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -541,7 +541,7 @@ async def test_continue_unapproved_tool_in_approvals_passes_through( run_ctx: RunContext[Any], ) -> None: """Deferred unapproved tool with continue strategy in approvals → pass through.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( create_deferred_bridge_capability, ) @@ -576,7 +576,7 @@ async def test_emit_uses_event_bus_when_available( block_tool_call: ToolCallPart, ) -> None: """Event is published via event_bus when available.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( _emit_deferred_event, ) @@ -602,7 +602,7 @@ async def test_emit_graceful_when_no_event_bus( run_ctx: RunContext[Any], ) -> None: """No crash when event_bus is unavailable.""" - from agentpool.agents.native_agent.deferred_bridge import ( + from wolfharness.agents.native_agent.deferred_bridge import ( _emit_deferred_event, ) diff --git a/tests/agents/native_agent/test_deferred_wrap_tool.py b/tests/agents/native_agent/test_deferred_wrap_tool.py index 7225e84ae..f918ee7b4 100644 --- a/tests/agents/native_agent/test_deferred_wrap_tool.py +++ b/tests/agents/native_agent/test_deferred_wrap_tool.py @@ -14,8 +14,8 @@ from pydantic_ai.tools import RunContext import pytest -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.tools import ApprovalRequired, CallDeferred, Tool +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.tools import ApprovalRequired, CallDeferred, Tool if TYPE_CHECKING: @@ -88,7 +88,7 @@ def _create_deferred_tool( @pytest.mark.unit async def test_non_deferred_tool_normal_return(agent_ctx: AgentContext) -> None: """deferred=False tool returns normally — identical to current behavior.""" - from agentpool.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool def normal_tool(text: str) -> str: return f"result: {text}" @@ -105,7 +105,7 @@ async def test_non_deferred_tool_with_context( agent_ctx: AgentContext, run_ctx: RunContext[Any] ) -> None: """deferred=False tool with RunContext returns normally.""" - from agentpool.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool def ctx_tool(ctx: RunContext[Any], text: str) -> str: assert ctx.deps is not None @@ -121,8 +121,8 @@ def ctx_tool(ctx: RunContext[Any], text: str) -> str: @pytest.mark.unit async def test_non_deferred_tool_tool_result_conversion(agent_ctx: AgentContext) -> None: """deferred=False tool that returns ToolResult converts to ToolReturn.""" - from agentpool.agents.native_agent.tool_wrapping import wrap_tool - from agentpool.tools.base import ToolResult + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.tools.base import ToolResult def result_tool(text: str) -> ToolResult: return ToolResult(content=f"content: {text}", metadata={"key": "val"}) @@ -146,7 +146,7 @@ async def test_deferred_tool_catches_call_deferred_on_resume( agent_ctx: AgentContext, ) -> None: """Tool body raises CallDeferred → wrap_tool() catches and routes to bridge.""" - from agentpool.agents.native_agent import tool_wrapping as tw_mod + from wolfharness.agents.native_agent import tool_wrapping as tw_mod def deferred_body(text: str) -> str: raise CallDeferred(metadata={"call_id": "tc-1"}) @@ -173,7 +173,7 @@ async def test_deferred_tool_catches_call_deferred_with_context( run_ctx: RunContext[Any], ) -> None: """Tool body with RunContext raises CallDeferred → caught properly.""" - from agentpool.agents.native_agent import tool_wrapping as tw_mod + from wolfharness.agents.native_agent import tool_wrapping as tw_mod def deferred_ctx_body(ctx: RunContext[Any], text: str) -> str: raise CallDeferred(metadata={"from": "ctx_tool"}) @@ -196,7 +196,7 @@ async def test_deferred_tool_catches_approval_required_on_resume( agent_ctx: AgentContext, ) -> None: """Tool body raises ApprovalRequired → wrap_tool() catches and routes to bridge.""" - from agentpool.agents.native_agent import tool_wrapping as tw_mod + from wolfharness.agents.native_agent import tool_wrapping as tw_mod def approval_body(text: str) -> str: raise ApprovalRequired(metadata={"needs": "human"}) @@ -226,7 +226,7 @@ async def test_deferred_tool_normal_return_on_resume_passes_through( agent_ctx: AgentContext, ) -> None: """Tool body returns normally on resume — no deferred lifecycle triggered.""" - from agentpool.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool def normal_body(text: str) -> str: return f"done: {text}" @@ -243,8 +243,8 @@ async def test_deferred_tool_normal_return_with_tool_result_passes_through( agent_ctx: AgentContext, ) -> None: """Tool body returns ToolResult on resume — converts to ToolReturn normally.""" - from agentpool.agents.native_agent.tool_wrapping import wrap_tool - from agentpool.tools.base import ToolResult + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.tools.base import ToolResult def result_body(text: str) -> ToolResult: return ToolResult(content=f"content: {text}") @@ -267,7 +267,7 @@ async def test_handle_deferred_exception_re_raises_call_deferred( agent_ctx: AgentContext, ) -> None: """_handle_deferred_exception re-raises CallDeferred (bridge not yet integrated).""" - from agentpool.agents.native_agent.tool_wrapping import _handle_deferred_exception + from wolfharness.agents.native_agent.tool_wrapping import _handle_deferred_exception exc = CallDeferred(metadata={"key": "value"}) tool = _create_deferred_tool(lambda: "unused", deferred=True) @@ -283,7 +283,7 @@ async def test_handle_deferred_exception_re_raises_approval_required( agent_ctx: AgentContext, ) -> None: """_handle_deferred_exception re-raises ApprovalRequired (bridge not yet integrated).""" - from agentpool.agents.native_agent.tool_wrapping import _handle_deferred_exception + from wolfharness.agents.native_agent.tool_wrapping import _handle_deferred_exception exc = ApprovalRequired(metadata={"reason": "confirm"}) tool = _create_deferred_tool( @@ -304,7 +304,7 @@ async def test_non_deferred_tool_does_not_catch_call_deferred( agent_ctx: AgentContext, ) -> None: """deferred=False tool: CallDeferred propagates as normal exception.""" - from agentpool.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool def raises_deferred(text: str) -> str: raise CallDeferred(metadata={"bad": "not deferred"}) @@ -321,7 +321,7 @@ async def test_non_deferred_tool_does_not_catch_approval_required( agent_ctx: AgentContext, ) -> None: """deferred=False tool: ApprovalRequired propagates as normal exception.""" - from agentpool.agents.native_agent.tool_wrapping import wrap_tool + from wolfharness.agents.native_agent.tool_wrapping import wrap_tool def raises_approval(text: str) -> str: raise ApprovalRequired(metadata={"bad": "not deferred"}) @@ -343,7 +343,7 @@ async def test_deferred_no_context_function_catches_call_deferred( agent_ctx: AgentContext, ) -> None: """Tool without RunContext/AgentContext raising CallDeferred → caught.""" - from agentpool.agents.native_agent import tool_wrapping as tw_mod + from wolfharness.agents.native_agent import tool_wrapping as tw_mod def no_ctx_body(text: str) -> str: raise CallDeferred(metadata={"no_context": True}) diff --git a/tests/agents/native_agent/test_get_agentlet_capabilities.py b/tests/agents/native_agent/test_get_agentlet_capabilities.py index d92a940d1..d887e974e 100644 --- a/tests/agents/native_agent/test_get_agentlet_capabilities.py +++ b/tests/agents/native_agent/test_get_agentlet_capabilities.py @@ -13,9 +13,9 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.orchestrator.core import EventBus +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.orchestrator.core import EventBus pytestmark = pytest.mark.unit @@ -111,7 +111,7 @@ async def test_get_agentlet_collects_tool_provider_capabilities( mock_provider_no_capability, ] - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -137,9 +137,9 @@ async def test_get_agentlet_creates_hooks_capability( hooks_cap = MagicMock() with ( - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, patch( - "agentpool.agents.native_agent.tool_intercept.ToolInterceptCapability", + "wolfharness.agents.native_agent.tool_intercept.ToolInterceptCapability", return_value=hooks_cap, ) as mock_ti_class, ): @@ -177,9 +177,9 @@ async def test_get_agentlet_uses_hook_manager_capability_directly( run_ctx = AgentRunContext(session_id="test-session", event_bus=event_bus) with ( - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, patch( - "agentpool.agents.native_agent.tool_intercept.ToolInterceptCapability", + "wolfharness.agents.native_agent.tool_intercept.ToolInterceptCapability", return_value=hooks_cap, ) as mock_ti_class, ): @@ -210,7 +210,7 @@ async def test_get_agentlet_collects_mcp_capabilities( """MCP capabilities collected from mcp.get_capabilities().""" mock_agent.mcp = mock_mcp_manager - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -245,7 +245,7 @@ async def test_get_agentlet_wraps_history_processors( "_resolve_history_processors", return_value=[mock_history_processor], ), - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, ): mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -275,7 +275,7 @@ async def test_get_agentlet_wraps_builtin_tools( builtin_tool_2 = MagicMock() mock_agent._builtin_tools = [builtin_tool_1, builtin_tool_2] - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -316,9 +316,9 @@ async def test_get_agentlet_passes_capabilities_to_pydantic_agent( "_resolve_history_processors", return_value=[mock_history_processor], ), - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, patch( - "agentpool.agents.native_agent.tool_intercept.ToolInterceptCapability", + "wolfharness.agents.native_agent.tool_intercept.ToolInterceptCapability", return_value=hooks_cap, ), ): @@ -360,7 +360,7 @@ async def test_get_agentlet_collects_instructions( "to_pydantic_ai_instructions", new=AsyncMock(return_value=[system_instruction]), ), - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, ): mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -391,7 +391,7 @@ async def test_get_agentlet_no_duplicate_history_resolution( "_resolve_history_processors", return_value=[], ) as mock_resolve, - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, ): mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -409,7 +409,7 @@ async def test_get_agentlet_no_wrap_tool_usage( mock_agent: Agent[Any], ) -> None: """No manual tool wrapping via wrap_tool.""" - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() # Also patch wrap_tool if it were imported - but it's not @@ -445,7 +445,7 @@ async def test_get_agentlet_default_providers_contribute_capabilities( "_resolve_history_processors", return_value=[], ), - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, ): mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -482,7 +482,7 @@ async def test_get_agentlet_handles_failing_provider_instructions( "to_pydantic_ai_instructions", new=AsyncMock(return_value=["system prompt"]), ), - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, ): mock_pydantic_agent.return_value = MagicMock() # Should not raise despite provider failing @@ -514,7 +514,7 @@ async def test_get_agentlet_resolves_string_model( "_resolve_model_string", return_value=(mock_model, None), ), - patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, + patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent, ): mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet("custom:model", None, None) @@ -537,7 +537,7 @@ async def test_python_api_capability_passthrough(mock_agent: Agent[Any]) -> None mock_agent.config = MagicMock() mock_agent.config.capabilities = [cap] - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -554,7 +554,7 @@ async def test_python_api_capability_passthrough(mock_agent: Agent[Any]) -> None @pytest.mark.anyio async def test_yaml_config_capability_passthrough(mock_agent: Agent[Any]) -> None: """YAML-loaded GenericCapabilityConfig is built and included in capabilities.""" - from agentpool_config.capabilities import GenericCapabilityConfig + from wolfharness_config.capabilities import GenericCapabilityConfig cap_config = GenericCapabilityConfig( type="pydantic_ai.capabilities.Instrumentation", @@ -563,7 +563,7 @@ async def test_yaml_config_capability_passthrough(mock_agent: Agent[Any]) -> Non mock_agent.config = MagicMock() mock_agent.config.capabilities = [cap_config] - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -587,7 +587,7 @@ async def test_user_capability_takes_precedence(mock_agent: Agent[Any]) -> None: mock_agent.config = MagicMock() mock_agent.config.capabilities = [user_cap] - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -604,7 +604,7 @@ async def test_user_capability_takes_precedence(mock_agent: Agent[Any]) -> None: @pytest.mark.anyio async def test_capability_config_build_called(mock_agent: Agent[Any]) -> None: """GenericCapabilityConfig.build() is called during get_agentlet().""" - from agentpool_config.capabilities import GenericCapabilityConfig + from wolfharness_config.capabilities import GenericCapabilityConfig cap_config = GenericCapabilityConfig( type="pydantic_ai.capabilities.Instrumentation", @@ -615,7 +615,7 @@ async def test_capability_config_build_called(mock_agent: Agent[Any]) -> None: with patch.object(GenericCapabilityConfig, "build") as mock_build: mock_build.return_value = MagicMock() - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await mock_agent.get_agentlet(None, None, None) @@ -642,9 +642,9 @@ async def test_from_config_capabilities_not_duplicated() -> None: """ from pydantic_ai.capabilities import Instrumentation - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.model_configs import TestModelConfig - from agentpool_config.capabilities import GenericCapabilityConfig + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.model_configs import TestModelConfig + from wolfharness_config.capabilities import GenericCapabilityConfig cap_config = GenericCapabilityConfig( type="pydantic_ai.capabilities.Instrumentation", @@ -659,7 +659,7 @@ async def test_from_config_capabilities_not_duplicated() -> None: agent = Agent.from_config(config) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await agent.get_agentlet(None, None, None) @@ -693,9 +693,9 @@ async def test_config_capabilities_visible_before_get_agentlet() -> None: """ from pydantic_ai.capabilities import Instrumentation - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.model_configs import TestModelConfig - from agentpool_config.capabilities import GenericCapabilityConfig + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.model_configs import TestModelConfig + from wolfharness_config.capabilities import GenericCapabilityConfig cap_config = GenericCapabilityConfig( type="pydantic_ai.capabilities.Instrumentation", @@ -732,9 +732,9 @@ async def test_no_duplicate_external_capabilities_after_get_agentlet() -> None: """ from pydantic_ai.capabilities import Instrumentation - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.model_configs import TestModelConfig - from agentpool_config.capabilities import GenericCapabilityConfig + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.model_configs import TestModelConfig + from wolfharness_config.capabilities import GenericCapabilityConfig cap_config = GenericCapabilityConfig( type="pydantic_ai.capabilities.Instrumentation", @@ -756,7 +756,7 @@ async def test_no_duplicate_external_capabilities_after_get_agentlet() -> None: f"found {count_before}." ) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await agent.get_agentlet(None, None, None) @@ -779,9 +779,9 @@ async def test_no_external_capability_growth_across_multiple_get_agentlet_calls( """ from pydantic_ai.capabilities import Instrumentation - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.model_configs import TestModelConfig - from agentpool_config.capabilities import GenericCapabilityConfig + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.model_configs import TestModelConfig + from wolfharness_config.capabilities import GenericCapabilityConfig cap_config = GenericCapabilityConfig( type="pydantic_ai.capabilities.Instrumentation", @@ -796,7 +796,7 @@ async def test_no_external_capability_growth_across_multiple_get_agentlet_calls( agent = Agent.from_config(config) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() # Call get_agentlet() three times (simulating three turns) @@ -816,12 +816,12 @@ async def test_no_external_capability_growth_across_multiple_get_agentlet_calls( # Also verify the overall _external_capabilities list didn't grow # (it should contain the config capability + any session/worker providers) # The key invariant: len after N calls == len after 1 call - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await agent.get_agentlet(None, None, None) len_after_4th = len(agent._external_capabilities) - with patch("agentpool.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: + with patch("wolfharness.agents.native_agent.agent.PydanticAgent") as mock_pydantic_agent: mock_pydantic_agent.return_value = MagicMock() await agent.get_agentlet(None, None, None) len_after_5th = len(agent._external_capabilities) diff --git a/tests/agents/native_agent/test_inject_prompt_cross_task.py b/tests/agents/native_agent/test_inject_prompt_cross_task.py index f4071626c..9ba1bcfd4 100644 --- a/tests/agents/native_agent/test_inject_prompt_cross_task.py +++ b/tests/agents/native_agent/test_inject_prompt_cross_task.py @@ -18,14 +18,14 @@ from pydantic_ai.models.test import TestModel, TestStreamedResponse import pytest -from agentpool import Agent -from agentpool.agents.base_agent import _current_run_ctx_var -from agentpool.agents.events import StreamCompleteEvent -from agentpool.orchestrator.core import SessionState +from wolfharness import Agent +from wolfharness.agents.base_agent import _current_run_ctx_var +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.orchestrator.core import SessionState if TYPE_CHECKING: - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.context import AgentRunContext # --------------------------------------------------------------------------- @@ -107,7 +107,7 @@ def _mock_session_pool(agent: Agent, run_ctx: AgentRunContext) -> None: """Mock agent_pool.session_pool so get_active_run_context() returns run_ctx.""" from unittest.mock import AsyncMock - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle session_state = SessionState(session_id="test-session", agent_name="test") session_state.current_run_id = run_ctx.run_id @@ -388,7 +388,7 @@ async def test_hook_manager_consumes_cross_task_injection_with_session_pool( when SessionPool fallback is available. """ - from agentpool.agents.native_agent.hook_manager import NativeAgentHookManager + from wolfharness.agents.native_agent.hook_manager import NativeAgentHookManager stream_started = asyncio.Event() captured_run_ctx: list[AgentRunContext] = [] diff --git a/tests/agents/native_agent/test_instructions_format.py b/tests/agents/native_agent/test_instructions_format.py index 3fc6d51a9..ecf3a9590 100644 --- a/tests/agents/native_agent/test_instructions_format.py +++ b/tests/agents/native_agent/test_instructions_format.py @@ -12,15 +12,15 @@ from pydantic_ai import Agent as PydanticAgent, RunContext import pytest -from agentpool.agents.context import AgentContext -from agentpool.agents.native_agent import Agent -from agentpool.agents.sys_prompts import SystemPrompts -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.prompts.instructions import ( +from wolfharness.agents.context import AgentContext +from wolfharness.agents.native_agent import Agent +from wolfharness.agents.sys_prompts import SystemPrompts +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.prompts.instructions import ( InstructionFunc, PydanticAIInstruction, ) -from agentpool.utils.context_wrapping import wrap_instruction +from wolfharness.utils.context_wrapping import wrap_instruction pytestmark = pytest.mark.unit diff --git a/tests/agents/native_agent/test_interrupt.py b/tests/agents/native_agent/test_interrupt.py index 1595f331c..beaaa4f41 100644 --- a/tests/agents/native_agent/test_interrupt.py +++ b/tests/agents/native_agent/test_interrupt.py @@ -25,9 +25,9 @@ from pydantic_ai.models.test import TestModel, TestStreamedResponse import pytest -from agentpool import Agent -from agentpool.agents.events import StreamCompleteEvent -from agentpool.orchestrator.core import SessionState +from wolfharness import Agent +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.orchestrator.core import SessionState if TYPE_CHECKING: @@ -107,7 +107,7 @@ async def fast_agent() -> Agent[None]: def _mock_session_pool(agent: Agent, run_ctx: Any) -> None: """Mock agent_pool.session_pool so _get_session_run_ctx() returns run_ctx.""" - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle session_state = SessionState(session_id="test-session", agent_name="test") session_state.current_run_id = run_ctx.run_id @@ -153,7 +153,7 @@ async def test_interrupt_without_run_ctx_sets_cancelled_flag(slow_agent: Agent[N After removing _active_run_ctx, cross-task access requires SessionPool fallback. """ - from agentpool.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.base_agent import _current_run_ctx_var stream_started = asyncio.Event() captured_run_ctx: list[Any] = [] @@ -208,7 +208,7 @@ async def test_interrupt_without_run_ctx_cancels_stream_task(slow_agent: Agent[N The stream loop checks run_ctx.cancelled and breaks, so the task completes normally (not via CancelledError). """ - from agentpool.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.base_agent import _current_run_ctx_var stream_started = asyncio.Event() captured_run_ctx: list[Any] = [] @@ -268,7 +268,7 @@ async def test_interrupt_with_run_ctx_still_works(fast_agent: Agent[None]) -> No The fix must not break the existing code path where run_ctx is provided. """ - from agentpool.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.base_agent import _current_run_ctx_var stream_started = asyncio.Event() captured_run_ctx = None @@ -312,7 +312,7 @@ async def test_interrupt_cancels_iteration_task(slow_agent: Agent[None]) -> None After the fix: iteration_task is stored as self._iteration_task and directly cancelled by _interrupt(). """ - from agentpool.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.base_agent import _current_run_ctx_var stream_started = asyncio.Event() interrupt_done = asyncio.Event() @@ -424,7 +424,7 @@ async def test_opencode_abort_flow_stops_agent(slow_agent: Agent[None]) -> None: Cross-task access requires SessionPool fallback (since _active_run_ctx was removed). """ - from agentpool.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.base_agent import _current_run_ctx_var stream_started = asyncio.Event() events_received: list[Any] = [] diff --git a/tests/agents/native_agent/test_native_turn_hooks.py b/tests/agents/native_agent/test_native_turn_hooks.py index 9e2814faa..bd2045b1d 100644 --- a/tests/agents/native_agent/test_native_turn_hooks.py +++ b/tests/agents/native_agent/test_native_turn_hooks.py @@ -11,11 +11,11 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events.events import StreamCompleteEvent -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.hooks import AgentHooks, CallableHook, HookResult +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events.events import StreamCompleteEvent +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.hooks import AgentHooks, CallableHook, HookResult # --------------------------------------------------------------------------- diff --git a/tests/agents/native_agent/test_process_history_capability.py b/tests/agents/native_agent/test_process_history_capability.py index 0ff836eee..cd130fe9b 100644 --- a/tests/agents/native_agent/test_process_history_capability.py +++ b/tests/agents/native_agent/test_process_history_capability.py @@ -11,7 +11,7 @@ from pydantic_ai.tools import RunContext import pytest -from agentpool.agents.native_agent.process_history_capability import ( +from wolfharness.agents.native_agent.process_history_capability import ( ProcessHistoryAdapter, _is_run_context_annotation, ) diff --git a/tests/agents/native_agent/test_step_error_metadata.py b/tests/agents/native_agent/test_step_error_metadata.py index 09b42cd8d..b22d5ba1b 100644 --- a/tests/agents/native_agent/test_step_error_metadata.py +++ b/tests/agents/native_agent/test_step_error_metadata.py @@ -4,7 +4,7 @@ import pytest -from agentpool.agents.events.events import RunErrorEvent, StepErrorMetadata +from wolfharness.agents.events.events import RunErrorEvent, StepErrorMetadata @pytest.mark.unit diff --git a/tests/agents/native_agent/test_step_usage.py b/tests/agents/native_agent/test_step_usage.py index cdbd4eeed..6889e7514 100644 --- a/tests/agents/native_agent/test_step_usage.py +++ b/tests/agents/native_agent/test_step_usage.py @@ -12,14 +12,14 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events.events import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events.events import ( StepUsageEvent, StreamCompleteEvent, ToolCallCompleteEvent, ) -from agentpool.agents.native_agent.turn import NativeTurn +from wolfharness.agents.native_agent.turn import NativeTurn # --------------------------------------------------------------------------- diff --git a/tests/agents/native_agent/test_terminal_events.py b/tests/agents/native_agent/test_terminal_events.py index 3856da34e..5ba781b02 100644 --- a/tests/agents/native_agent/test_terminal_events.py +++ b/tests/agents/native_agent/test_terminal_events.py @@ -19,14 +19,14 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events.events import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events.events import ( RunErrorEvent, StepErrorMetadata, StreamCompleteEvent, ) -from agentpool.agents.native_agent.turn import NativeTurn +from wolfharness.agents.native_agent.turn import NativeTurn def _make_mock_agentlet_raising(exc: BaseException) -> MagicMock: diff --git a/tests/agents/native_agent/test_tool_intercept_capability.py b/tests/agents/native_agent/test_tool_intercept_capability.py index e1167c3c2..63570f689 100644 --- a/tests/agents/native_agent/test_tool_intercept_capability.py +++ b/tests/agents/native_agent/test_tool_intercept_capability.py @@ -26,10 +26,10 @@ from pydantic_ai.usage import RunUsage import pytest -from agentpool import Agent -from agentpool.agents.native_agent.tool_intercept import ToolInterceptCapability -from agentpool.hooks import AgentHooks, CallableHook -from agentpool.hooks.base import HookResult +from wolfharness import Agent +from wolfharness.agents.native_agent.tool_intercept import ToolInterceptCapability +from wolfharness.hooks import AgentHooks, CallableHook +from wolfharness.hooks.base import HookResult pytestmark = pytest.mark.unit @@ -230,7 +230,7 @@ async def test_wrap_tool_execute_reraises_run_aborted_error( run. If swallowed here, the LLM receives it as a tool error and continues executing. """ - from agentpool.tasks.exceptions import RunAbortedError + from wolfharness.tasks.exceptions import RunAbortedError cap = make_capability(mock_hook_manager) ctx = make_run_context() @@ -293,7 +293,7 @@ async def test_wrap_tool_execute_reraises_tool_skipped_error( MCP tool_bridge). It signals the tool was skipped, not failed — swallowing it converts a skip signal into a tool error response. """ - from agentpool.tasks.exceptions import ToolSkippedError + from wolfharness.tasks.exceptions import ToolSkippedError cap = make_capability(mock_hook_manager) ctx = make_run_context() diff --git a/tests/agents/native_agent/test_turn.py b/tests/agents/native_agent/test_turn.py index c685a7aeb..12270fcc7 100644 --- a/tests/agents/native_agent/test_turn.py +++ b/tests/agents/native_agent/test_turn.py @@ -18,19 +18,19 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events.events import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events.events import ( RunErrorEvent, StreamCompleteEvent, ToolCallCompleteEvent, ) -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.tasks.exceptions import RunAbortedError +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.tasks.exceptions import RunAbortedError if TYPE_CHECKING: - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool def _make_mock_agentlet_raising(exc: BaseException) -> MagicMock: @@ -108,7 +108,7 @@ def fake_is_terminal(tool: Tool[Any]) -> bool: events: list[Any] = [] with patch( - "agentpool.agents.native_agent.turn.is_terminal_tool", + "wolfharness.agents.native_agent.turn.is_terminal_tool", side_effect=fake_is_terminal, ): events.extend([event async for event in turn.execute()]) diff --git a/tests/agents/run_concurrent_tests.py b/tests/agents/run_concurrent_tests.py index c9883589f..869ef4c97 100644 --- a/tests/agents/run_concurrent_tests.py +++ b/tests/agents/run_concurrent_tests.py @@ -13,11 +13,11 @@ import time -# Add agentpool to path +# Add wolfharness to path sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) -from agentpool import Agent -from agentpool.agents.events import StreamCompleteEvent +from wolfharness import Agent +from wolfharness.agents.events import StreamCompleteEvent async def run_baseline_test(): # noqa: PLR0915 diff --git a/tests/agents/test_agent_basics.py b/tests/agents/test_agent_basics.py index ef7ecbf25..5b64c417c 100644 --- a/tests/agents/test_agent_basics.py +++ b/tests/agents/test_agent_basics.py @@ -9,8 +9,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, ChatMessage -from agentpool.agents.events import StreamCompleteEvent +from wolfharness import Agent, ChatMessage +from wolfharness.agents.events import StreamCompleteEvent SIMPLE_PROMPT = "Hello, how are you?" diff --git a/tests/agents/test_base_agent_api.py b/tests/agents/test_base_agent_api.py index bbcc226af..407e5277a 100644 --- a/tests/agents/test_base_agent_api.py +++ b/tests/agents/test_base_agent_api.py @@ -9,9 +9,9 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.agents.base_agent import BaseAgent, _current_run_ctx_var -from agentpool.agents.context import AgentRunContext -from agentpool.orchestrator.core import SessionState +from wolfharness.agents.base_agent import BaseAgent, _current_run_ctx_var +from wolfharness.agents.context import AgentRunContext +from wolfharness.orchestrator.core import SessionState pytestmark = pytest.mark.integration @@ -22,7 +22,7 @@ from pydantic_ai.messages import ModelMessage - from agentpool.orchestrator.turn import Turn + from wolfharness.orchestrator.turn import Turn # --------------------------------------------------------------------------- @@ -113,7 +113,7 @@ def agent() -> _TestAgent: def _mock_session_pool(agent: _TestAgent, run_ctx: AgentRunContext) -> None: """Mock agent_pool.session_pool so _get_session_run_ctx() returns run_ctx.""" - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle session_state = SessionState(session_id="test-session", agent_name="test") session_state.current_run_id = run_ctx.run_id @@ -275,7 +275,7 @@ async def test_is_turn_active_true_with_current_run_ctx(agent: _TestAgent) -> No def test_is_turn_active_false_after_clearing_session_run_ctx(agent: _TestAgent) -> None: """After clearing session.current_run_id, is_turn_active() returns False.""" - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle run_ctx = AgentRunContext() session_state = SessionState(session_id="test-session", agent_name="test") @@ -340,8 +340,8 @@ async def test_is_turn_active_during_run_stream() -> None: We verify this by providing a tool that inspects the agent state mid-turn. """ - from agentpool import Agent - from agentpool.agents.events import StreamCompleteEvent + from wolfharness import Agent + from wolfharness.agents.events import StreamCompleteEvent turn_active_values: list[bool] = [] @@ -363,8 +363,8 @@ async def check_turn() -> str: @pytest.mark.asyncio async def test_get_active_run_context_during_run_stream() -> None: """get_active_run_context() returns a non-None context while inside run_stream.""" - from agentpool import Agent - from agentpool.agents.events import StreamCompleteEvent + from wolfharness import Agent + from wolfharness.agents.events import StreamCompleteEvent contexts: list[AgentRunContext | None] = [] diff --git a/tests/agents/test_base_agent_run_v2.py b/tests/agents/test_base_agent_run_v2.py index ff6f10b24..22d6de228 100644 --- a/tests/agents/test_base_agent_run_v2.py +++ b/tests/agents/test_base_agent_run_v2.py @@ -11,18 +11,18 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import RunErrorEvent, RunStartedEvent, StreamCompleteEvent -from agentpool.agents.native_agent.agent import Agent -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.turn import Turn +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import RunErrorEvent, RunStartedEvent, StreamCompleteEvent +from wolfharness.agents.native_agent.agent import Agent +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.turn import Turn if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -253,7 +253,7 @@ async def test_run_stream_breaks_on_stream_complete(minimal_pool: AgentPool) -> stream.receive() after the run completes because the session remains open and EndOfStream is never raised. """ - from agentpool.orchestrator.core import SessionController + from wolfharness.orchestrator.core import SessionController controller = SessionController(pool=minimal_pool) event_bus = EventBus() @@ -305,7 +305,7 @@ def test_no_duplicate_stream_complete_in_run_once() -> None: NativeTurn.execute() already yields StreamCompleteEvent as its terminal event. Publishing it again results in duplicate events on the EventBus. """ - import agentpool.agents.native_agent.agent as agent_module + import wolfharness.agents.native_agent.agent as agent_module source = inspect.getsource(agent_module.Agent._execute_node) # After the fix, there should be no explicit StreamCompleteEvent publish. @@ -327,7 +327,7 @@ def test_no_duplicate_stream_complete_in_stream_events() -> None: NativeTurn.execute() already yields StreamCompleteEvent as its terminal event. Publishing it again results in duplicate events on the EventBus. """ - import agentpool.agents.native_agent.agent as agent_module + import wolfharness.agents.native_agent.agent as agent_module source = inspect.getsource(agent_module.Agent._stream_events) import re @@ -346,7 +346,7 @@ def test_base_agent_imports_are_runtime_available() -> None: Gemini claimed they were only in TYPE_CHECKING, but they are actually imported at module level (line 23 of base_agent.py). """ - import agentpool.agents.base_agent as base_module + import wolfharness.agents.base_agent as base_module # Verify the classes are accessible as attributes (runtime import) assert hasattr(base_module, "StreamCompleteEvent"), ( @@ -363,7 +363,7 @@ def test_execute_node_handles_run_error_event() -> None: Without this, if turn.execute() yields RunErrorEvent and returns early, turn.final_message raises RuntimeError, masking the original error. """ - import agentpool.agents.native_agent.agent as agent_module + import wolfharness.agents.native_agent.agent as agent_module source = inspect.getsource(agent_module.Agent._execute_node) assert "RunErrorEvent" in source, ( @@ -379,7 +379,7 @@ def test_stream_events_handles_run_error_event() -> None: Same issue as _execute_node — if turn fails, final_message is not set. """ - import agentpool.agents.native_agent.agent as agent_module + import wolfharness.agents.native_agent.agent as agent_module source = inspect.getsource(agent_module.Agent._stream_events) assert "RunErrorEvent" in source, ( diff --git a/tests/agents/test_capability_hooks_graph.py b/tests/agents/test_capability_hooks_graph.py index aa2c5bec9..fbb5157e4 100644 --- a/tests/agents/test_capability_hooks_graph.py +++ b/tests/agents/test_capability_hooks_graph.py @@ -17,8 +17,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.agents.native_agent.agent import Agent -from agentpool.delegation import BaseTeam +from wolfharness.agents.native_agent.agent import Agent +from wolfharness.delegation import BaseTeam class _NodeHookTrackerCapability(AbstractCapability[Any]): diff --git a/tests/agents/test_capability_hooks_standalone.py b/tests/agents/test_capability_hooks_standalone.py index 9d5784435..94c8b9ed7 100644 --- a/tests/agents/test_capability_hooks_standalone.py +++ b/tests/agents/test_capability_hooks_standalone.py @@ -19,8 +19,8 @@ from pydantic_ai.capabilities import AbstractCapability import pytest -from agentpool.agents.native_agent.agent import Agent -from agentpool.models.agents import NativeAgentConfig +from wolfharness.agents.native_agent.agent import Agent +from wolfharness.models.agents import NativeAgentConfig class HookTrackerCapability(AbstractCapability[Any]): diff --git a/tests/agents/test_contextvar_concurrency.py b/tests/agents/test_contextvar_concurrency.py index 95ab06078..56b1518c4 100644 --- a/tests/agents/test_contextvar_concurrency.py +++ b/tests/agents/test_contextvar_concurrency.py @@ -23,7 +23,7 @@ def test_current_run_ctx_var_exists(): # Check it's a ContextVar from contextvars import ContextVar - from agentpool.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.base_agent import _current_run_ctx_var assert isinstance(_current_run_ctx_var, ContextVar) @@ -35,8 +35,8 @@ def test_current_run_ctx_var_exists(): def test_contextvar_isolation(): """Test that ContextVar provides proper isolation between contexts.""" - from agentpool.agents.base_agent import _current_run_ctx_var - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.context import AgentRunContext # Create two different run contexts ctx1 = AgentRunContext(session_id="session1") @@ -67,8 +67,8 @@ async def main(): def test_contextvar_with_tasks(): """Test ContextVar behavior with sequential asyncio tasks.""" - from agentpool.agents.base_agent import _current_run_ctx_var - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.context import AgentRunContext results = [] @@ -104,8 +104,8 @@ async def main(): def test_contextvar_context_manager(): """Test ContextVar usage with context manager pattern.""" - from agentpool.agents.base_agent import _current_run_ctx_var - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.context import AgentRunContext # Save current value old_value = _current_run_ctx_var.get() @@ -129,7 +129,7 @@ def test_contextvar_context_manager(): def test_no_instance_variable(): """Test that _current_run_ctx is NOT an instance variable.""" - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent # Create a mock agent instance class MockAgent(BaseAgent): @@ -156,12 +156,12 @@ def __init__(self): def test_background_run_ctx_unchanged(): """Test that _background_run_ctx is still an instance variable (unchanged).""" - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent # Create a minimal agent instance class TestAgent(BaseAgent): def create_turn(self, prompts, run_ctx, message_history): - from agentpool.orchestrator.turn import Turn + from wolfharness.orchestrator.turn import Turn class _EmptyTurn(Turn): async def execute(self): @@ -185,8 +185,8 @@ async def execute(self): def test_concurrent_runs_isolation(): """Test that concurrent agent runs have isolated contexts (RFC-0021).""" - from agentpool.agents.base_agent import _current_run_ctx_var - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.base_agent import _current_run_ctx_var + from wolfharness.agents.context import AgentRunContext async def simulate_run(run_id: int): # Simulate setting up a run context diff --git a/tests/agents/test_create_child_session.py b/tests/agents/test_create_child_session.py index 99f1eb8ea..d4cc98106 100644 --- a/tests/agents/test_create_child_session.py +++ b/tests/agents/test_create_child_session.py @@ -10,9 +10,9 @@ import pytest -from agentpool.agents.context import AgentContext -from agentpool.sessions import SessionData -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness.agents.context import AgentContext +from wolfharness.sessions import SessionData +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider pytestmark = pytest.mark.integration @@ -65,7 +65,7 @@ async def mock_create_child_session( session_id: str | None = None, **kwargs: object, ) -> MagicMock: - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id child_sid = session_id or generate_session_id() parent_data = None diff --git a/tests/agents/test_create_turn.py b/tests/agents/test_create_turn.py index f37a1c9e4..9b2ce2bce 100644 --- a/tests/agents/test_create_turn.py +++ b/tests/agents/test_create_turn.py @@ -7,10 +7,10 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.native_agent.agent import Agent -from agentpool.agents.native_agent.turn import NativeTurn +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.native_agent.agent import Agent +from wolfharness.agents.native_agent.turn import NativeTurn @pytest.mark.unit @@ -73,14 +73,14 @@ def test_acp_adapter_satisfies_protocol() -> None: import inspect from acp.agent.acp_agent_api import ACPAgentAPI - from agentpool.agents.acp_agent.turn import ACPClientProtocol + from wolfharness.agents.acp_agent.turn import ACPClientProtocol # ACPAgentAPI must have stream_events and get_messages methods assert hasattr(ACPAgentAPI, "stream_events"), "ACPAgentAPI must have stream_events()" assert hasattr(ACPAgentAPI, "get_messages"), "ACPAgentAPI must have get_messages()" # create_turn must not contain cast or TODO - import agentpool.agents.acp_agent.acp_agent as acp_module + import wolfharness.agents.acp_agent.acp_agent as acp_module source = inspect.getsource(acp_module.ACPAgent.create_turn) assert "cast" not in source, "create_turn must not use cast — adapter is complete" @@ -89,8 +89,8 @@ def test_acp_adapter_satisfies_protocol() -> None: # ACPAgentAPI must satisfy ACPClientProtocol at runtime from unittest.mock import MagicMock - from agentpool.agents.acp_agent.client_handler import TimeoutableEvent - from agentpool.agents.acp_agent.session_state import ACPSessionState + from wolfharness.agents.acp_agent.client_handler import TimeoutableEvent + from wolfharness.agents.acp_agent.session_state import ACPSessionState api = ACPAgentAPI( MagicMock(), @@ -104,7 +104,7 @@ def test_acp_adapter_satisfies_protocol() -> None: def test_acp_turn_uses_run_ctx_run_id() -> None: """ACPTurn.execute() must use self._run_ctx.run_id, not generate uuid4.""" - import agentpool.agents.acp_agent.turn as turn_module + import wolfharness.agents.acp_agent.turn as turn_module source = inspect.getsource(turn_module.ACPTurn.execute) assert "self._run_ctx.run_id" in source, ( @@ -121,7 +121,7 @@ def test_acp_turn_no_redundant_run_started_event() -> None: RunHandle.start() already publishes RunStartedEvent before calling turn.execute(). Yielding it again causes duplicate events. """ - import agentpool.agents.acp_agent.turn as turn_module + import wolfharness.agents.acp_agent.turn as turn_module source = inspect.getsource(turn_module.ACPTurn.execute) # Check that RunStartedEvent is not yielded in the execute method body @@ -136,7 +136,7 @@ def test_acp_turn_no_redundant_run_started_event() -> None: def test_acp_turn_no_unused_initial_message_history() -> None: """ACPTurn must not store _initial_message_history (dead code).""" - import agentpool.agents.acp_agent.turn as turn_module + import wolfharness.agents.acp_agent.turn as turn_module source = inspect.getsource(turn_module.ACPTurn.__init__) assert "_initial_message_history" not in source, ( @@ -151,7 +151,7 @@ def test_acp_turn_no_unused_initial_message_history() -> None: def test_acp_turn_accepts_agent_name() -> None: """ACPTurn.__init__ must accept and store agent_name parameter.""" - import agentpool.agents.acp_agent.turn as turn_module + import wolfharness.agents.acp_agent.turn as turn_module source = inspect.getsource(turn_module.ACPTurn.__init__) assert "agent_name" in source, ( @@ -162,7 +162,7 @@ def test_acp_turn_accepts_agent_name() -> None: def test_acp_turn_run_error_event_includes_agent_name() -> None: """ACPTurn.execute() must pass agent_name to RunErrorEvent yields.""" - import agentpool.agents.acp_agent.turn as turn_module + import wolfharness.agents.acp_agent.turn as turn_module source = inspect.getsource(turn_module.ACPTurn.execute) assert "agent_name=self._agent_name" in source, ( diff --git a/tests/agents/test_delegation_depth_error.py b/tests/agents/test_delegation_depth_error.py index b057d8ae0..25f7a2a80 100644 --- a/tests/agents/test_delegation_depth_error.py +++ b/tests/agents/test_delegation_depth_error.py @@ -4,7 +4,7 @@ import pytest -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError pytestmark = pytest.mark.unit @@ -62,7 +62,7 @@ def test_delegation_depth_error_catchable_as_runtime_error() -> None: def test_import_from_agents_init() -> None: """DelegationDepthError and MAX_DELEGATION_DEPTH should be importable from agents package.""" - import agentpool.agents as agents_mod + import wolfharness.agents as agents_mod assert agents_mod.DelegationDepthError is DelegationDepthError assert agents_mod.MAX_DELEGATION_DEPTH is MAX_DELEGATION_DEPTH diff --git a/tests/agents/test_deprecation_warnings.py b/tests/agents/test_deprecation_warnings.py index 3e6c0355b..a8b8585e2 100644 --- a/tests/agents/test_deprecation_warnings.py +++ b/tests/agents/test_deprecation_warnings.py @@ -19,7 +19,7 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool +from wolfharness import Agent, AgentPool pytestmark = pytest.mark.integration diff --git a/tests/agents/test_external_agent_event_sequence.py b/tests/agents/test_external_agent_event_sequence.py index 3e3dfe458..9c6142ec1 100644 --- a/tests/agents/test_external_agent_event_sequence.py +++ b/tests/agents/test_external_agent_event_sequence.py @@ -24,8 +24,8 @@ from pydantic_ai import RunContext # noqa: TC002 import pytest -from agentpool import Agent, AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.events import StreamCompleteEvent, ToolCallCompleteEvent +from wolfharness import Agent, AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.events import StreamCompleteEvent, ToolCallCompleteEvent # Mark all tests in this module as integration tests @@ -125,7 +125,7 @@ def extract_key_events(events: list[Any]) -> list[str]: @pytest.fixture def acp_agent_config_with_tool(tmp_path: Path) -> tuple[Any, Path]: """Create ACPAgent config with echo tool via config file.""" - from agentpool.models.acp_agents import ACPAgentConfig + from wolfharness.models.acp_agents import ACPAgentConfig config_yaml = """ agents: @@ -142,7 +142,7 @@ def acp_agent_config_with_tool(tmp_path: Path) -> tuple[Any, Path]: command="uv", args=[ "run", - "agentpool", + "wolfharness", "serve-acp", str(config_file), "--agent", @@ -215,7 +215,7 @@ async def test_native_agent_event_sequence(): async def test_acp_agent_event_sequence(acp_agent_config_with_tool: tuple[Any, Path]): """Test ACPAgent emits events in expected sequence.""" - from agentpool.agents.acp_agent import ACPAgent + from wolfharness.agents.acp_agent import ACPAgent config, _ = acp_agent_config_with_tool collector = EventCollector() @@ -242,7 +242,7 @@ async def test_event_sequence_consistency_across_agents( acp_agent_config_with_tool: tuple[Any, Path], ): """Test that native Agent and ACPAgent emit consistent key event sequences.""" - from agentpool.agents.acp_agent import ACPAgent + from wolfharness.agents.acp_agent import ACPAgent # Collect from native agent native_collector = EventCollector() diff --git a/tests/agents/test_import_corrections.py b/tests/agents/test_import_corrections.py index a6ead9847..10956d835 100644 --- a/tests/agents/test_import_corrections.py +++ b/tests/agents/test_import_corrections.py @@ -15,12 +15,12 @@ def test_runusage_imports(): """Test that RunUsage is imported from pydantic_ai.usage, not pydantic_ai.""" files_to_check = [ - "src/agentpool_storage/sql_provider/sql_provider.py", - "src/agentpool_storage/file_provider/provider.py", + "src/wolfharness_storage/sql_provider/sql_provider.py", + "src/wolfharness_storage/file_provider/provider.py", "tests/test_history.py", "tests/mcp_client/test_client_conversion.py", - "src/agentpool_storage/sql_provider/utils.py", - "src/agentpool_storage/claude_provider/converters.py", + "src/wolfharness_storage/sql_provider/utils.py", + "src/wolfharness_storage/claude_provider/converters.py", ] root = Path(__file__).parent.parent.parent diff --git a/tests/agents/test_native_agent_streaming_cancellation.py b/tests/agents/test_native_agent_streaming_cancellation.py index 1e56b5e5b..5ccbdb7d0 100644 --- a/tests/agents/test_native_agent_streaming_cancellation.py +++ b/tests/agents/test_native_agent_streaming_cancellation.py @@ -22,10 +22,10 @@ from pydantic_ai.models.test import TestModel, TestStreamedResponse import pytest -from agentpool import Agent -from agentpool.agents.base_agent import _current_run_ctx_var -from agentpool.agents.events import StreamCompleteEvent -from agentpool.orchestrator.core import SessionState +from wolfharness import Agent +from wolfharness.agents.base_agent import _current_run_ctx_var +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.orchestrator.core import SessionState if TYPE_CHECKING: @@ -86,7 +86,7 @@ async def slow_agent() -> AsyncGenerator[Agent[None]]: def _mock_session_pool(agent: Agent[Any], run_ctx: Any) -> None: """Mock agent_pool.session_pool so _get_session_run_ctx() returns run_ctx.""" - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle session_state = SessionState(session_id="test-session", agent_name="test") session_state.current_run_id = run_ctx.run_id diff --git a/tests/agents/test_native_agent_streaming_ordering.py b/tests/agents/test_native_agent_streaming_ordering.py index 458d4f239..433d7f745 100644 --- a/tests/agents/test_native_agent_streaming_ordering.py +++ b/tests/agents/test_native_agent_streaming_ordering.py @@ -10,8 +10,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.events import StreamCompleteEvent +from wolfharness import Agent +from wolfharness.agents.events import StreamCompleteEvent TEST_RESPONSE = "I am a test response" diff --git a/tests/agents/test_native_agent_streaming_realtime.py b/tests/agents/test_native_agent_streaming_realtime.py index b4ef277aa..9b2ca3eaa 100644 --- a/tests/agents/test_native_agent_streaming_realtime.py +++ b/tests/agents/test_native_agent_streaming_realtime.py @@ -19,8 +19,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.events import StreamCompleteEvent +from wolfharness import Agent +from wolfharness.agents.events import StreamCompleteEvent # --------------------------------------------------------------------------- diff --git a/tests/agents/test_run_stream_depth.py b/tests/agents/test_run_stream_depth.py index 281f9a1aa..31eb4b7be 100644 --- a/tests/agents/test_run_stream_depth.py +++ b/tests/agents/test_run_stream_depth.py @@ -4,7 +4,7 @@ import pytest -from agentpool.agents.context import AgentRunContext +from wolfharness.agents.context import AgentRunContext pytestmark = pytest.mark.unit @@ -44,7 +44,7 @@ def test_agent_run_context_terminal_tool_state_default() -> None: def test_terminal_tool_metadata_marks_run_completion() -> None: """Terminal-tool behavior should be declared by metadata, not by tool name.""" - from agentpool.tools.base import TERMINAL_TOOL_METADATA_KEY, has_terminal_tool_metadata + from wolfharness.tools.base import TERMINAL_TOOL_METADATA_KEY, has_terminal_tool_metadata assert has_terminal_tool_metadata({TERMINAL_TOOL_METADATA_KEY: "true"}) assert not has_terminal_tool_metadata({"name": "attempt_completion"}) @@ -59,7 +59,7 @@ def test_run_stream_accepts_depth_param() -> None: """ import inspect - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent sig = inspect.signature(BaseAgent.run_stream) assert "depth" in sig.parameters @@ -70,7 +70,7 @@ def test_run_accepts_depth_param() -> None: """BaseAgent.run() should accept depth parameter without TypeError.""" import inspect - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent sig = inspect.signature(BaseAgent.run) assert "depth" in sig.parameters diff --git a/tests/agents/test_run_stream_direct_gating.py b/tests/agents/test_run_stream_direct_gating.py index bf92bc758..8da9ea486 100644 --- a/tests/agents/test_run_stream_direct_gating.py +++ b/tests/agents/test_run_stream_direct_gating.py @@ -12,8 +12,8 @@ import pytest -from agentpool.agents.base_agent import BaseAgent -from agentpool.orchestrator.turn import Turn +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.orchestrator.turn import Turn pytestmark = pytest.mark.unit @@ -22,8 +22,8 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.agents.context import AgentRunContext - from agentpool.agents.events import RichAgentStreamEvent + from wolfharness.agents.context import AgentRunContext + from wolfharness.agents.events import RichAgentStreamEvent # --------------------------------------------------------------------------- diff --git a/tests/agents/test_runusage_corrections.py b/tests/agents/test_runusage_corrections.py index a8a5d138e..2d330f5dd 100644 --- a/tests/agents/test_runusage_corrections.py +++ b/tests/agents/test_runusage_corrections.py @@ -19,7 +19,7 @@ from pydantic_ai.usage import RequestUsage, RunUsage # noqa: E402 -from agentpool.messaging.messages import TokenCost # noqa: E402 +from wolfharness.messaging.messages import TokenCost # noqa: E402 pytestmark = pytest.mark.unit diff --git a/tests/agents/test_session_id_deprecation.py b/tests/agents/test_session_id_deprecation.py index d939d40d1..a43af16fb 100644 --- a/tests/agents/test_session_id_deprecation.py +++ b/tests/agents/test_session_id_deprecation.py @@ -6,7 +6,7 @@ import pytest -from agentpool.agents.context import AgentRunContext +from wolfharness.agents.context import AgentRunContext pytestmark = pytest.mark.unit diff --git a/tests/agents/test_signatures_context_binding.py b/tests/agents/test_signatures_context_binding.py index ff18f9ec8..fb56eb336 100644 --- a/tests/agents/test_signatures_context_binding.py +++ b/tests/agents/test_signatures_context_binding.py @@ -5,10 +5,10 @@ import pytest -from agentpool import Agent -from agentpool.agents.context import AgentContext -from agentpool.utils.inspection import get_fn_name -from agentpool.utils.signatures import create_bound_callable +from wolfharness import Agent +from wolfharness.agents.context import AgentContext +from wolfharness.utils.inspection import get_fn_name +from wolfharness.utils.signatures import create_bound_callable pytestmark = pytest.mark.unit @@ -290,7 +290,7 @@ def test_invalid_callable_signature(self): bad_callable.__name__ = "bad_callable" with ( patch( - "agentpool.utils.signatures.inspect.signature", + "wolfharness.utils.signatures.inspect.signature", side_effect=ValueError("no signature"), ), pytest.raises(ValueError, match="Cannot inspect signature"), diff --git a/tests/benchmark/test_graph_performance.py b/tests/benchmark/test_graph_performance.py index 9bfdb34e7..8097fe894 100644 --- a/tests/benchmark/test_graph_performance.py +++ b/tests/benchmark/test_graph_performance.py @@ -21,16 +21,16 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, BaseTeam -from agentpool.agents.events import RunStartedEvent -from agentpool.delegation.graph_team import build_team_graph +from wolfharness import Agent, BaseTeam +from wolfharness.agents.events import RunStartedEvent +from wolfharness.delegation.graph_team import build_team_graph pytestmark = pytest.mark.unit if TYPE_CHECKING: - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage # Threshold: graph-based must be within 40% of direct execution. diff --git a/tests/capabilities/background_task/integration/test_background_task_concurrency_stress.py b/tests/capabilities/background_task/integration/test_background_task_concurrency_stress.py index ef7f1583e..0415a6cb0 100644 --- a/tests/capabilities/background_task/integration/test_background_task_concurrency_stress.py +++ b/tests/capabilities/background_task/integration/test_background_task_concurrency_stress.py @@ -25,16 +25,16 @@ if TYPE_CHECKING: from collections.abc import Callable -from agentpool import ChatMessage -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import StreamCompleteEvent -from agentpool.capabilities.background_task.capability import ( +from wolfharness import ChatMessage +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.manager import TERMINAL_STATES, BackgroundTaskManager -from agentpool.capabilities.background_task.types import BackgroundTask -from agentpool.delegation import AgentPool +from wolfharness.capabilities.background_task.manager import TERMINAL_STATES, BackgroundTaskManager +from wolfharness.capabilities.background_task.types import BackgroundTask +from wolfharness.delegation import AgentPool # --------------------------------------------------------------------------- @@ -443,7 +443,7 @@ async def _capturing_task_async(self_inner, *args, **kwargs): with ( patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_team_001", ), patch.object( diff --git a/tests/capabilities/background_task/integration/test_background_task_e2e.py b/tests/capabilities/background_task/integration/test_background_task_e2e.py index f9cc74062..4b335a6f5 100644 --- a/tests/capabilities/background_task/integration/test_background_task_e2e.py +++ b/tests/capabilities/background_task/integration/test_background_task_e2e.py @@ -20,19 +20,19 @@ from pydantic_ai.models.function import AgentInfo, FunctionModel import pytest -from agentpool import Agent, AgentContext, ChatMessage -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import StreamCompleteEvent -from agentpool.capabilities.background_task import ( +from wolfharness import Agent, AgentContext, ChatMessage +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.capabilities.background_task import ( BackgroundTaskCapability as BackgroundTaskProvider, ) -from agentpool.capabilities.background_task.capability import ( +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.manager import BackgroundTaskManager -from agentpool.capabilities.background_task.types import BackgroundTask -from agentpool.delegation import AgentPool -from agentpool.tools.exceptions import ToolError +from wolfharness.capabilities.background_task.manager import BackgroundTaskManager +from wolfharness.capabilities.background_task.types import BackgroundTask +from wolfharness.delegation import AgentPool +from wolfharness.tools.exceptions import ToolError def _wrap_in_run_context(agent_ctx): @@ -562,7 +562,7 @@ async def test_shutdown_clears_empty_manager(): @pytest.mark.integration async def test_background_task_provider_importable_from_package(): """Test that BackgroundTaskCapability is importable from the capabilities package.""" - from agentpool.capabilities.background_task import ( + from wolfharness.capabilities.background_task import ( BackgroundTaskCapability as Imported, ) diff --git a/tests/capabilities/background_task/resource_providers/test_background_task_cancellation_regression.py b/tests/capabilities/background_task/resource_providers/test_background_task_cancellation_regression.py index 5df7f8917..02df52673 100644 --- a/tests/capabilities/background_task/resource_providers/test_background_task_cancellation_regression.py +++ b/tests/capabilities/background_task/resource_providers/test_background_task_cancellation_regression.py @@ -21,16 +21,16 @@ from pydantic_ai import RunContext import pytest -from agentpool import ChatMessage -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import StreamCompleteEvent -from agentpool.capabilities.background_task.capability import ( +from wolfharness import ChatMessage +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.manager import BackgroundTaskManager -from agentpool.capabilities.background_task.types import BackgroundTask -from agentpool.delegation import AgentPool +from wolfharness.capabilities.background_task.manager import BackgroundTaskManager +from wolfharness.capabilities.background_task.types import BackgroundTask +from wolfharness.delegation import AgentPool pytestmark = pytest.mark.anyio @@ -297,7 +297,7 @@ async def test_parallel_background_tasks_via_provider_all_complete(self): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value=f"bg_parl{i:02d}", ): result = await capability._task( @@ -359,7 +359,7 @@ async def test_blocking_on_task_a_does_not_cancel_task_b(self): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_task_a01", ): await capability._task( @@ -376,7 +376,7 @@ async def _slow_stream_b(**kwargs): mock_node.run_stream = MagicMock(return_value=_slow_stream_b()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_task_b01", ): await capability._task( @@ -422,7 +422,7 @@ async def _delayed_stream(**kwargs): mock_node.run_stream = MagicMock(return_value=_delayed_stream()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value=f"bg_concur_{task_label}", ): await capability._task( @@ -482,7 +482,7 @@ async def test_blocking_output_while_third_task_running(self): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_three_a", ): await capability._task( @@ -499,7 +499,7 @@ async def _stream_b(**kwargs): mock_node.run_stream = MagicMock(return_value=_stream_b()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_three_b", ): await capability._task( @@ -516,7 +516,7 @@ async def _stream_c(**kwargs): mock_node.run_stream = MagicMock(return_value=_stream_c()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_three_c", ): await capability._task( @@ -565,7 +565,7 @@ async def test_blocking_waiter_unregister_does_not_affect_other_tasks(self): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_waiter01", ): await capability._task( @@ -582,7 +582,7 @@ async def _stream_2(**kwargs): mock_node.run_stream = MagicMock(return_value=_stream_2()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_waiter02", ): await capability._task( diff --git a/tests/capabilities/background_task/resource_providers/test_background_task_completion_notification.py b/tests/capabilities/background_task/resource_providers/test_background_task_completion_notification.py index 18e482360..d863279ea 100644 --- a/tests/capabilities/background_task/resource_providers/test_background_task_completion_notification.py +++ b/tests/capabilities/background_task/resource_providers/test_background_task_completion_notification.py @@ -28,16 +28,16 @@ from pydantic_ai import RunContext import pytest -from agentpool import ChatMessage -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import StreamCompleteEvent -from agentpool.capabilities.background_task.capability import ( +from wolfharness import ChatMessage +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.manager import BackgroundTaskManager -from agentpool.capabilities.background_task.types import BackgroundTask, TaskHandle -from agentpool.delegation import AgentPool +from wolfharness.capabilities.background_task.manager import BackgroundTaskManager +from wolfharness.capabilities.background_task.types import BackgroundTask, TaskHandle +from wolfharness.delegation import AgentPool pytestmark = pytest.mark.anyio @@ -212,7 +212,7 @@ async def test_no_inject_when_blocking_waiter_present(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_a1b2c3d4", ): result_text = await capability._task( @@ -271,7 +271,7 @@ async def test_inject_when_no_blocking_waiter(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_noblock99", ): await capability._task( @@ -321,7 +321,7 @@ async def test_nonblocking_output_does_not_suppress_inject(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_nonblock1", ): await capability._task( @@ -377,7 +377,7 @@ async def test_waiter_unregistered_after_blocking_returns(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_unreg001", ): await capability._task( @@ -433,7 +433,7 @@ async def test_inject_prompt_queues_and_triggers_auto_resume(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_autoresume01", ): await capability._task( @@ -502,7 +502,7 @@ async def test_auto_resume_triggered_when_inject_prompt_returns_false(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_autoresume02", ): await capability._task( @@ -566,7 +566,7 @@ async def test_inject_prompt_safe_when_no_run_context(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_noctx001", ): await capability._task( @@ -658,7 +658,7 @@ async def test_debounce_window_no_notification_before_timeout(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_debounce01", ): await capability._task( @@ -723,7 +723,7 @@ async def test_multiple_tasks_batched_in_debounce_window(): task_ids = [] with ( patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", side_effect=["bg_batch01", "bg_batch02"], ), ): diff --git a/tests/capabilities/background_task/resource_providers/test_background_task_history_isolation.py b/tests/capabilities/background_task/resource_providers/test_background_task_history_isolation.py index f940de955..fd9c8bc04 100644 --- a/tests/capabilities/background_task/resource_providers/test_background_task_history_isolation.py +++ b/tests/capabilities/background_task/resource_providers/test_background_task_history_isolation.py @@ -30,15 +30,15 @@ from pydantic_ai import RunContext import pytest -from agentpool import ChatMessage -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import StreamCompleteEvent -from agentpool.capabilities.background_task.capability import ( +from wolfharness import ChatMessage +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.delegation import AgentPool -from agentpool.messaging import MessageHistory +from wolfharness.delegation import AgentPool +from wolfharness.messaging import MessageHistory pytestmark = pytest.mark.anyio @@ -233,7 +233,7 @@ def _capturing_run_stream(*args: Any, **kwargs: Any) -> Any: node.run_stream = MagicMock(side_effect=_capturing_run_stream) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_isoasyn1", ): await capability._task( @@ -320,7 +320,7 @@ def _capturing_run_stream(*args: Any, **kwargs: Any) -> Any: for i in range(2): with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value=f"bg_parl{i:02d}", ): await capability._task( @@ -362,7 +362,7 @@ async def test_node_conversation_not_polluted_by_async_task(self) -> None: ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_nopoll1", ): await capability._task( @@ -418,7 +418,7 @@ def _capturing_run_stream(*args: Any, **kwargs: Any) -> Any: for i in range(2): with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value=f"bg_sid{i:02d}", ): await capability._task( @@ -485,7 +485,7 @@ def _capturing_run_stream(*args: Any, **kwargs: Any) -> Any: for i in range(2): with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value=f"bg_dep{i:02d}", ): await capability._task( diff --git a/tests/capabilities/background_task/resource_providers/test_background_task_provider.py b/tests/capabilities/background_task/resource_providers/test_background_task_provider.py index c7bda95f2..bcf39c785 100644 --- a/tests/capabilities/background_task/resource_providers/test_background_task_provider.py +++ b/tests/capabilities/background_task/resource_providers/test_background_task_provider.py @@ -13,12 +13,12 @@ from pydantic_ai import RunContext import pytest -from agentpool import Agent, AgentContext -from agentpool.agents.base_agent import BaseAgent -from agentpool.capabilities.background_task.capability import BackgroundTaskCapability -from agentpool.capabilities.background_task.manager import BackgroundTaskManager -from agentpool.delegation import AgentPool -from agentpool.tools.exceptions import ToolError +from wolfharness import Agent, AgentContext +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.capabilities.background_task.capability import BackgroundTaskCapability +from wolfharness.capabilities.background_task.manager import BackgroundTaskManager +from wolfharness.delegation import AgentPool +from wolfharness.tools.exceptions import ToolError # --------------------------------------------------------------------------- diff --git a/tests/capabilities/background_task/resource_providers/test_background_task_provider_async_events.py b/tests/capabilities/background_task/resource_providers/test_background_task_provider_async_events.py index 4107433e6..446b2d184 100644 --- a/tests/capabilities/background_task/resource_providers/test_background_task_provider_async_events.py +++ b/tests/capabilities/background_task/resource_providers/test_background_task_provider_async_events.py @@ -24,19 +24,19 @@ from pydantic_ai.messages import TextPartDelta import pytest -from agentpool import ChatMessage -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import ( +from wolfharness import ChatMessage +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import ( PartDeltaEvent, RunFailedEvent, StreamCompleteEvent, SubAgentEvent, ) -from agentpool.capabilities.background_task.capability import ( +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.delegation import AgentPool +from wolfharness.delegation import AgentPool pytestmark = pytest.mark.anyio @@ -230,7 +230,7 @@ async def test_async_task_does_not_emit_subagent_events(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_asevnt01", ): await capability._task( @@ -278,7 +278,7 @@ async def test_async_task_does_not_emit_stream_complete_on_finish(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_asynct01", ): await capability._task( @@ -323,7 +323,7 @@ async def _error_stream(**kwargs): mock_node.run_stream = MagicMock(return_value=_error_stream()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_asynct01", ): await capability._task( @@ -363,7 +363,7 @@ async def test_async_task_preserves_filesystem_writing(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_fswrt001", ): await capability._task( @@ -408,7 +408,7 @@ async def test_async_task_writes_part_delta_to_filesystem(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_delta001", ): await capability._task( @@ -452,7 +452,7 @@ async def test_async_task_returns_immediately(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_quick0001", ): result = await capability._task( @@ -500,7 +500,7 @@ async def _slow_stream(**kwargs): mock_node.run_stream = MagicMock(return_value=_slow_stream()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_parend01", ): await capability._task( @@ -538,7 +538,7 @@ async def _slow_stream(**kwargs): @pytest.mark.unit async def test_async_task_emits_spawn_session_start(): """Verify that create_child_session() is called with spawn_mechanism='task' - so that SpawnSessionStart is auto-emitted by the agentpool framework. + so that SpawnSessionStart is auto-emitted by the wolfharness framework. """ capability = BackgroundTaskCapability(schemas=None) pool = _make_mock_pool() @@ -553,7 +553,7 @@ async def test_async_task_emits_spawn_session_start(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_spawn01", ): await capability._task( @@ -596,7 +596,7 @@ async def test_async_task_completion_callback_injects_prompt(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_inject01", ): await capability._task( diff --git a/tests/capabilities/background_task/resource_providers/test_background_task_provider_execution.py b/tests/capabilities/background_task/resource_providers/test_background_task_provider_execution.py index b70cf7530..8ee02c569 100644 --- a/tests/capabilities/background_task/resource_providers/test_background_task_provider_execution.py +++ b/tests/capabilities/background_task/resource_providers/test_background_task_provider_execution.py @@ -13,10 +13,10 @@ from pydantic_ai import RunContext import pytest -from agentpool import ChatMessage -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import ( +from wolfharness import ChatMessage +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import ( RunErrorEvent, RunFailedEvent, StreamCompleteEvent, @@ -24,14 +24,14 @@ ToolCallCompleteEvent, ToolCallStartEvent, ) -from agentpool.capabilities.background_task.capability import ( +from wolfharness.capabilities.background_task.capability import ( MAX_DELEGATION_DEPTH, BackgroundTaskCapability, _generate_task_id, ) -from agentpool.capabilities.background_task.types import BackgroundTask -from agentpool.delegation import AgentPool, BaseTeam -from agentpool.tools.exceptions import ToolError +from wolfharness.capabilities.background_task.types import BackgroundTask +from wolfharness.delegation import AgentPool, BaseTeam +from wolfharness.tools.exceptions import ToolError # --------------------------------------------------------------------------- @@ -285,7 +285,7 @@ async def test_task_delegation_depth_limit(): @pytest.mark.unit async def test_task_sync_emits_spawn_session_start(): """Test that create_child_session is called with spawn_mechanism='task' - so SpawnSessionStart is auto-emitted by the agentpool framework. + so SpawnSessionStart is auto-emitted by the wolfharness framework. """ capability = BackgroundTaskCapability(schemas=None) pool = _make_mock_pool() @@ -459,7 +459,8 @@ async def test_task_creates_child_session(): @pytest.mark.unit @patch( - "agentpool.capabilities.background_task.capability.load_skill_for_node", new_callable=AsyncMock + "wolfharness.capabilities.background_task.capability.load_skill_for_node", + new_callable=AsyncMock, ) async def test_task_injects_skills(mock_load_skill): """Test that task injects skills into the formatted prompt.""" @@ -550,7 +551,7 @@ async def test_task_async_returns_formatted_text(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_testtask", ): result = await capability._task( @@ -588,7 +589,7 @@ async def test_task_async_registers_background_task(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_regtest1", ): await capability._task( @@ -630,7 +631,7 @@ async def test_task_async_creates_task_directory(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_dirtst01", ): await capability._task( @@ -651,7 +652,7 @@ async def test_task_async_creates_task_directory(): @pytest.mark.unit async def test_task_async_emits_spawn_session_start(): """Test that async path calls create_child_session with spawn_mechanism='task' - so SpawnSessionStart is auto-emitted by the agentpool framework. + so SpawnSessionStart is auto-emitted by the wolfharness framework. """ capability = BackgroundTaskCapability(schemas=None) pool = _make_mock_pool() @@ -666,7 +667,7 @@ async def test_task_async_emits_spawn_session_start(): ) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_spawnt01", ): await capability._task( @@ -818,7 +819,7 @@ async def test_task_extracts_model_id_from_base_agent(): """Test that task calls create_child_session correctly for BaseAgent nodes. Model ID extraction was removed — SpawnSessionStart (including model_id) - is now auto-emitted by create_child_session() in the agentpool framework. + is now auto-emitted by create_child_session() in the wolfharness framework. """ capability = BackgroundTaskCapability(schemas=None) pool = _make_mock_pool() diff --git a/tests/capabilities/background_task/resource_providers/test_background_task_provider_output_cancel.py b/tests/capabilities/background_task/resource_providers/test_background_task_provider_output_cancel.py index 67a82bfe4..bffd8c87a 100644 --- a/tests/capabilities/background_task/resource_providers/test_background_task_provider_output_cancel.py +++ b/tests/capabilities/background_task/resource_providers/test_background_task_provider_output_cancel.py @@ -10,13 +10,13 @@ import pytest from upathtools.filesystems.isolated_memory_fs import IsolatedMemoryFileSystem -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.capabilities.background_task.capability import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.types import BackgroundTask, TaskStatus -from agentpool.tools.exceptions import ToolError +from wolfharness.capabilities.background_task.types import BackgroundTask, TaskStatus +from wolfharness.tools.exceptions import ToolError def _wrap_in_run_context(agent_ctx): diff --git a/tests/capabilities/background_task/test_background_output_edge_cases.py b/tests/capabilities/background_task/test_background_output_edge_cases.py index 074cdef3b..0f8d552d6 100644 --- a/tests/capabilities/background_task/test_background_output_edge_cases.py +++ b/tests/capabilities/background_task/test_background_output_edge_cases.py @@ -18,13 +18,13 @@ import pytest from upathtools.filesystems.isolated_memory_fs import IsolatedMemoryFileSystem -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.capabilities.background_task.capability import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.manager import BackgroundTaskManager -from agentpool.capabilities.background_task.types import BackgroundTask +from wolfharness.capabilities.background_task.manager import BackgroundTaskManager +from wolfharness.capabilities.background_task.types import BackgroundTask class _NullWaitManager(BackgroundTaskManager): diff --git a/tests/capabilities/background_task/test_background_task_capability.py b/tests/capabilities/background_task/test_background_task_capability.py index 0c1441534..78f52edb6 100644 --- a/tests/capabilities/background_task/test_background_task_capability.py +++ b/tests/capabilities/background_task/test_background_task_capability.py @@ -12,10 +12,10 @@ from pydantic_ai.toolsets import FunctionToolset from pydantic_graph import End -from agentpool.agents.context import AgentContext -from agentpool.capabilities.background_task import BackgroundTaskCapability -import agentpool.capabilities.background_task.capability as btc_module -from agentpool.capabilities.background_task.capability import ForceRetrievalMode +from wolfharness.agents.context import AgentContext +from wolfharness.capabilities.background_task import BackgroundTaskCapability +import wolfharness.capabilities.background_task.capability as btc_module +from wolfharness.capabilities.background_task.capability import ForceRetrievalMode CONFIG_DIR = Path(__file__).resolve().parents[3] / "config" @@ -650,7 +650,7 @@ async def test_steer_task_interrupt_calls_steer(): async def test_steer_task_raises_error_for_unknown_task(): """_steer_task should raise ToolError when task_id is not found.""" - from agentpool.tools.exceptions import ToolError + from wolfharness.tools.exceptions import ToolError capability = BackgroundTaskCapability() @@ -695,7 +695,7 @@ async def test_steer_task_returns_message_for_terminal_task(): async def test_steer_task_raises_error_when_no_session_pool(): """_steer_task should raise ToolError when SessionPool is unavailable.""" - from agentpool.tools.exceptions import ToolError + from wolfharness.tools.exceptions import ToolError capability = BackgroundTaskCapability() diff --git a/tests/capabilities/background_task/test_error_propagation.py b/tests/capabilities/background_task/test_error_propagation.py index 749329153..fa439168a 100644 --- a/tests/capabilities/background_task/test_error_propagation.py +++ b/tests/capabilities/background_task/test_error_propagation.py @@ -19,12 +19,12 @@ import pytest -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.capabilities.background_task.capability import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.manager import BackgroundTaskManager +from wolfharness.capabilities.background_task.manager import BackgroundTaskManager # --------------------------------------------------------------------------- @@ -99,7 +99,7 @@ def _make_agent_context(pool: MagicMock) -> MagicMock: @pytest.mark.unit @patch( - "agentpool.capabilities.background_task.capability.load_skill_for_node", + "wolfharness.capabilities.background_task.capability.load_skill_for_node", new_callable=AsyncMock, ) async def test_load_skill_exception_propagates_uncaught(mock_load_skill: AsyncMock) -> None: @@ -175,7 +175,7 @@ async def test_mkdirs_exception_propagates_from_task_async() -> None: ctx.internal_fs.mkdirs = MagicMock(side_effect=OSError("disk full")) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_mkdir01", ): result = await capability._task( diff --git a/tests/capabilities/background_task/test_manager.py b/tests/capabilities/background_task/test_manager.py index 094f7aad2..dfd0c2346 100644 --- a/tests/capabilities/background_task/test_manager.py +++ b/tests/capabilities/background_task/test_manager.py @@ -6,7 +6,7 @@ import contextlib import time -from agentpool.capabilities.background_task import BackgroundTask, BackgroundTaskManager +from wolfharness.capabilities.background_task import BackgroundTask, BackgroundTaskManager # --------------------------------------------------------------------------- diff --git a/tests/capabilities/background_task/test_manager_state_and_cleanup.py b/tests/capabilities/background_task/test_manager_state_and_cleanup.py index acc1ff627..ca60506ef 100644 --- a/tests/capabilities/background_task/test_manager_state_and_cleanup.py +++ b/tests/capabilities/background_task/test_manager_state_and_cleanup.py @@ -18,7 +18,7 @@ import pytest -from agentpool.capabilities.background_task import BackgroundTask, BackgroundTaskManager +from wolfharness.capabilities.background_task import BackgroundTask, BackgroundTaskManager # --------------------------------------------------------------------------- diff --git a/tests/capabilities/background_task/test_notification_batcher.py b/tests/capabilities/background_task/test_notification_batcher.py index 91a2e56b0..73fe2ce55 100644 --- a/tests/capabilities/background_task/test_notification_batcher.py +++ b/tests/capabilities/background_task/test_notification_batcher.py @@ -16,8 +16,8 @@ import pytest -from agentpool.capabilities.background_task.notification import NotificationBatcher -from agentpool.capabilities.background_task.types import BackgroundTask +from wolfharness.capabilities.background_task.notification import NotificationBatcher +from wolfharness.capabilities.background_task.types import BackgroundTask # Flush delivery runs via anyio TimerHandle callbacks, so tests must run under diff --git a/tests/capabilities/background_task/test_run_and_stream_errors.py b/tests/capabilities/background_task/test_run_and_stream_errors.py index 100840baa..912a7d21d 100644 --- a/tests/capabilities/background_task/test_run_and_stream_errors.py +++ b/tests/capabilities/background_task/test_run_and_stream_errors.py @@ -25,10 +25,10 @@ from pydantic_ai import RunContext import pytest -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.context import AgentContext -from agentpool.agents.events import RunErrorEvent -from agentpool.capabilities.background_task.capability import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.context import AgentContext +from wolfharness.agents.events import RunErrorEvent +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, _generate_task_id, ) @@ -193,7 +193,7 @@ async def test_run_and_stream_swallows_exception_and_marks_completed(): pool.session_pool.send_message = AsyncMock(side_effect=ValueError("connection lost")) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_err001", ): await capability._task( @@ -247,7 +247,7 @@ async def test_run_and_stream_end_of_stream_no_terminal_event(): pool.session_pool.send_message = AsyncMock(return_value=MagicMock()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_eos001", ): await capability._task( @@ -299,11 +299,11 @@ async def test_run_and_stream_send_message_returns_none(): with ( patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_none01", ), patch( - "agentpool.capabilities.background_task.capability.logger", + "wolfharness.capabilities.background_task.capability.logger", ) as mock_logger, ): await capability._task( @@ -361,7 +361,7 @@ async def test_run_and_stream_writes_error_on_run_error_event(): pool.session_pool.send_message = AsyncMock(return_value=MagicMock()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_reer01", ): await capability._task( @@ -413,7 +413,7 @@ async def test_run_and_stream_writes_cancelled_on_cancelled_error(): pool.session_pool.send_message = AsyncMock(return_value=MagicMock()) with patch( - "agentpool.capabilities.background_task.capability._generate_task_id", + "wolfharness.capabilities.background_task.capability._generate_task_id", return_value="bg_cancel01", ): await capability._task( diff --git a/tests/capabilities/background_task/test_session_task_state.py b/tests/capabilities/background_task/test_session_task_state.py index 74513df3d..d7cedcb63 100644 --- a/tests/capabilities/background_task/test_session_task_state.py +++ b/tests/capabilities/background_task/test_session_task_state.py @@ -35,10 +35,10 @@ import anyio import pytest -from agentpool.capabilities.background_task.capability import ( +from wolfharness.capabilities.background_task.capability import ( BackgroundTaskCapability, ) -from agentpool.capabilities.background_task.types import BackgroundTask, SessionTaskState +from wolfharness.capabilities.background_task.types import BackgroundTask, SessionTaskState # --------------------------------------------------------------------------- @@ -147,7 +147,7 @@ class _MockRunContext: """A lightweight mock AgentRunContext. Real AgentRunContext cannot be imported in this test directory due to - the ``tests/agentpool/__init__.py`` shadowing the ``agentpool`` package. + the ``tests/wolfharness/__init__.py`` shadowing the ``wolfharness`` package. This mock provides the attributes needed by the capability: ``child_done_events``, ``_run_handle``, ``session_id``, ``run_id``. """ diff --git a/tests/capabilities/background_task/test_types.py b/tests/capabilities/background_task/test_types.py index b1e61a96f..2cb2a6cf5 100644 --- a/tests/capabilities/background_task/test_types.py +++ b/tests/capabilities/background_task/test_types.py @@ -7,8 +7,8 @@ import pytest -from agentpool.capabilities.background_task import BackgroundTask, TaskHandle, TaskStatus -from agentpool.capabilities.background_task.types import TaskStatus as TaskStatusFromTypes +from wolfharness.capabilities.background_task import BackgroundTask, TaskHandle, TaskStatus +from wolfharness.capabilities.background_task.types import TaskStatus as TaskStatusFromTypes def test_background_task_defaults() -> None: diff --git a/tests/capabilities/dcp/test_edge_cases.py b/tests/capabilities/dcp/test_edge_cases.py index 9767d7b74..98939bced 100644 --- a/tests/capabilities/dcp/test_edge_cases.py +++ b/tests/capabilities/dcp/test_edge_cases.py @@ -22,17 +22,17 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.capabilities.dcp.block_store import CompressionBlockStore -from agentpool.capabilities.dcp.capability import DynamicContextPruningCapability -from agentpool.capabilities.dcp.state import ( +from wolfharness.capabilities.dcp.block_store import CompressionBlockStore +from wolfharness.capabilities.dcp.capability import DynamicContextPruningCapability +from wolfharness.capabilities.dcp.state import ( CompressionBlock, DCPState, PruneAction, WatermarkLevel, ) -from agentpool.capabilities.dcp.strategies import _is_pruned, _prune_part -from agentpool.capabilities.tool_output_budget import ToolOutputBudgetCapability -from agentpool_config.capabilities import ( +from wolfharness.capabilities.dcp.strategies import _is_pruned, _prune_part +from wolfharness.capabilities.tool_output_budget import ToolOutputBudgetCapability +from wolfharness_config.capabilities import ( GenericCapabilityConfig, ) @@ -206,7 +206,7 @@ async def test_protected_pattern_matches_nothing_all_prunable() -> None: # Build prunable list to verify "read" is not protected. state = cap._get_dcp_state(ctx) - from agentpool.capabilities.dcp.prunable_list import build_prunable_list + from wolfharness.capabilities.dcp.prunable_list import build_prunable_list build_prunable_list([msg1, msg2, msg3], state, cap._config) # "read" should appear in tool_id_list since "nonexistent_*" doesn't match it. @@ -233,7 +233,7 @@ async def test_star_glob_protects_all_tools() -> None: ctx = _make_run_context(messages=[msg1, msg2, msg3]) state = cap._get_dcp_state(ctx) - from agentpool.capabilities.dcp.prunable_list import build_prunable_list + from wolfharness.capabilities.dcp.prunable_list import build_prunable_list build_prunable_list([msg1, msg2, msg3], state, cap._config) # Since "*" is a literal string in protected_tools (not a glob), it won't @@ -326,7 +326,7 @@ async def test_decompress_on_non_pruned_tool_returns_error() -> None: is unavailable. We provide a real ``SessionData`` mock so the state lookup succeeds and the decompress logic can run. """ - from agentpool.sessions.models import SessionData + from wolfharness.sessions.models import SessionData cap = DynamicContextPruningCapability() @@ -560,7 +560,7 @@ def test_dynamic_context_type_raises_error() -> None: which tries to import ``dynamic_context`` as a module path — that fails. """ # "dynamic_context" is not a known built-in type. - from agentpool_config.capabilities import is_known_capability_type + from wolfharness_config.capabilities import is_known_capability_type assert not is_known_capability_type("dynamic_context") diff --git a/tests/capabilities/dcp/test_integration.py b/tests/capabilities/dcp/test_integration.py index 76280fb36..2a944e559 100644 --- a/tests/capabilities/dcp/test_integration.py +++ b/tests/capabilities/dcp/test_integration.py @@ -30,16 +30,16 @@ from pydantic_ai.usage import RequestUsage import pytest -from agentpool.capabilities.dcp.capability import DynamicContextPruningCapability -from agentpool.capabilities.dcp.state import ( +from wolfharness.capabilities.dcp.capability import DynamicContextPruningCapability +from wolfharness.capabilities.dcp.state import ( DCPState, DistillTarget, PruneAction, WatermarkLevel, ) -from agentpool.capabilities.dcp.strategies import _is_pruned -from agentpool.capabilities.dcp.tools import decompress_tool -from agentpool.sessions.models import SessionData +from wolfharness.capabilities.dcp.strategies import _is_pruned +from wolfharness.capabilities.dcp.tools import decompress_tool +from wolfharness.sessions.models import SessionData pytestmark = pytest.mark.integration @@ -861,7 +861,7 @@ async def test_prune_then_decompress_same_turn() -> None: because prune_tool applies the action to state.current_messages immediately. """ - from agentpool.capabilities.dcp.tools import decompress_tool, prune_tool + from wolfharness.capabilities.dcp.tools import decompress_tool, prune_tool session_data, state = _make_session_with_state() ctx = _make_run_context(session_data=session_data) @@ -896,7 +896,7 @@ async def test_distill_then_decompress_same_turn() -> None: because distill_tool applies the action to state.current_messages immediately. """ - from agentpool.capabilities.dcp.tools import decompress_tool, distill_tool + from wolfharness.capabilities.dcp.tools import decompress_tool, distill_tool session_data, state = _make_session_with_state() ctx = _make_run_context(session_data=session_data) diff --git a/tests/capabilities/dcp/test_unit.py b/tests/capabilities/dcp/test_unit.py index e12a8811a..3847a8935 100644 --- a/tests/capabilities/dcp/test_unit.py +++ b/tests/capabilities/dcp/test_unit.py @@ -23,22 +23,22 @@ ) import pytest -from agentpool.capabilities.dcp.block_store import CompressionBlockStore -from agentpool.capabilities.dcp.config import DCPConfig -from agentpool.capabilities.dcp.nudge import build_nudge_text -from agentpool.capabilities.dcp.prunable_list import ( +from wolfharness.capabilities.dcp.block_store import CompressionBlockStore +from wolfharness.capabilities.dcp.config import DCPConfig +from wolfharness.capabilities.dcp.nudge import build_nudge_text +from wolfharness.capabilities.dcp.prunable_list import ( META_TOOL_NAMES, build_prunable_list, inject_prunable_list, ) -from agentpool.capabilities.dcp.state import ( +from wolfharness.capabilities.dcp.state import ( CompressionBlock, DCPState, DistillTarget, PruneAction, WatermarkLevel, ) -from agentpool.capabilities.dcp.strategies import ( +from wolfharness.capabilities.dcp.strategies import ( PrunableState, _dedup_exact, _is_pruned, @@ -46,17 +46,17 @@ _strip_thinking_content, purge_failed_tool_inputs, ) -from agentpool.capabilities.dcp.token_utils import ( +from wolfharness.capabilities.dcp.token_utils import ( calculate_context_pressure, estimate_tokens, ) -from agentpool.capabilities.dcp.tools import ( +from wolfharness.capabilities.dcp.tools import ( decompress_tool, distill_tool, prune_tool, ) -from agentpool.capabilities.dcp.watermark import WatermarkStateMachine -from agentpool.sessions.models import SessionData +from wolfharness.capabilities.dcp.watermark import WatermarkStateMachine +from wolfharness.sessions.models import SessionData pytestmark = pytest.mark.unit diff --git a/tests/capabilities/test_agent_context.py b/tests/capabilities/test_agent_context.py index 6a83eb92b..686ad6653 100644 --- a/tests/capabilities/test_agent_context.py +++ b/tests/capabilities/test_agent_context.py @@ -16,9 +16,9 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator -from agentpool.capabilities.agent_context import AgentContextDeps -from agentpool.capabilities.delegation import AgentNotFoundError, DelegationService -from agentpool.host.context import HostContext, RunScope +from wolfharness.capabilities.agent_context import AgentContextDeps +from wolfharness.capabilities.delegation import AgentNotFoundError, DelegationService +from wolfharness.host.context import HostContext, RunScope pytestmark = pytest.mark.unit @@ -91,7 +91,7 @@ def _make_session_state() -> Any: def _make_agent_registry() -> Any: """Build an AgentRegistry-like object for testing.""" - from agentpool.host.registry import AgentRegistry + from wolfharness.host.registry import AgentRegistry return AgentRegistry() @@ -231,7 +231,7 @@ def test_agent_context_team_mode_config_defaults_none() -> None: @pytest.mark.unit def test_agent_context_team_mode_config_accepts_instance() -> None: """AgentContextDeps accepts a TeamModeConfig instance.""" - from agentpool_config.team_mode import TeamModeConfig + from wolfharness_config.team_mode import TeamModeConfig config = TeamModeConfig(enabled=True, member_eligible=["translator"]) ctx = AgentContextDeps( @@ -255,7 +255,7 @@ def test_agent_context_team_mode_config_accepts_instance() -> None: def test_resolve_agent_context_from_deps_direct() -> None: """resolve_agent_context_from_deps returns deps when it is directly an AgentContextDeps.""" - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps agent_ctx = _make_agent_context() result = resolve_agent_context_from_deps(agent_ctx) @@ -264,8 +264,8 @@ def test_resolve_agent_context_from_deps_direct() -> None: def test_resolve_agent_context_from_deps_runtime_context() -> None: """resolve_agent_context_from_deps unwraps .data from RuntimeAgentContext.""" - from agentpool.agents.context import AgentContext as RuntimeAgentContext - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps agent_ctx = _make_agent_context() runtime_ctx = RuntimeAgentContext(node=MagicMock()) @@ -277,7 +277,7 @@ def test_resolve_agent_context_from_deps_runtime_context() -> None: def test_resolve_agent_context_from_deps_none() -> None: """resolve_agent_context_from_deps raises RuntimeError when deps is None.""" - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps with pytest.raises( RuntimeError, match=r"TestCap requires AgentContextDeps as deps\. Got: None" @@ -287,8 +287,8 @@ def test_resolve_agent_context_from_deps_none() -> None: def test_resolve_agent_context_from_deps_runtime_ctx_none_data() -> None: """resolve_agent_context_from_deps raises RuntimeError when .data is None.""" - from agentpool.agents.context import AgentContext as RuntimeAgentContext - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps runtime_ctx = RuntimeAgentContext(node=MagicMock()) runtime_ctx.data = None @@ -301,7 +301,7 @@ def test_resolve_agent_context_from_deps_runtime_ctx_none_data() -> None: def test_resolve_agent_context_from_deps_neither_type() -> None: """resolve_agent_context_from_deps raises RuntimeError for unknown deps type.""" - from agentpool.capabilities.agent_context import resolve_agent_context_from_deps + from wolfharness.capabilities.agent_context import resolve_agent_context_from_deps with pytest.raises(RuntimeError, match=r"TestCap requires AgentContextDeps as deps\. Got: str"): resolve_agent_context_from_deps("not a context", capability_name="TestCap") diff --git a/tests/capabilities/test_agent_context_resources_unused.py b/tests/capabilities/test_agent_context_resources_unused.py index 5cdf3e17d..e963b370b 100644 --- a/tests/capabilities/test_agent_context_resources_unused.py +++ b/tests/capabilities/test_agent_context_resources_unused.py @@ -44,7 +44,7 @@ def test_no_agent_context_resources_reads() -> None: indicate a read, excluding: - Import statements - Comments/docstrings - - The agentpool_sync module (unrelated 'resources' attribute) + - The wolfharness_sync module (unrelated 'resources' attribute) - Dataclass field definitions """ # Look for patterns like `.resources.list()`, `.resources.read(`, etc. diff --git a/tests/capabilities/test_code_mode_capability.py b/tests/capabilities/test_code_mode_capability.py index 399146b13..9ce3f4f31 100644 --- a/tests/capabilities/test_code_mode_capability.py +++ b/tests/capabilities/test_code_mode_capability.py @@ -9,8 +9,8 @@ from pydantic_ai.toolsets import FunctionToolset import pytest -from agentpool.capabilities.code_mode_capability import CodeModeCapability -from agentpool.tools.base import FunctionTool +from wolfharness.capabilities.code_mode_capability import CodeModeCapability +from wolfharness.tools.base import FunctionTool pytestmark = pytest.mark.unit diff --git a/tests/capabilities/test_combined_toolset.py b/tests/capabilities/test_combined_toolset.py index 5528babb8..fd9b93da4 100644 --- a/tests/capabilities/test_combined_toolset.py +++ b/tests/capabilities/test_combined_toolset.py @@ -10,10 +10,10 @@ from pydantic_ai.toolsets import CombinedToolset, FunctionToolset import pytest -from agentpool.capabilities.change_event import ChangeEvent -from agentpool.capabilities.combined_toolset import CombinedToolsetCapability -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.tools.base import Tool +from wolfharness.capabilities.change_event import ChangeEvent +from wolfharness.capabilities.combined_toolset import CombinedToolsetCapability +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.tools.base import Tool pytestmark = pytest.mark.unit @@ -67,7 +67,7 @@ def name(self) -> str: def get_toolset(self) -> Any: if not self._tools: return None - from agentpool.tools.tool_wrapping import wrap_tool_for_pydantic_ai + from wolfharness.tools.tool_wrapping import wrap_tool_for_pydantic_ai pa_tools = [wrap_tool_for_pydantic_ai(tool) for tool in self._tools] return FunctionToolset(pa_tools, id=self._name) diff --git a/tests/capabilities/test_extension_registry.py b/tests/capabilities/test_extension_registry.py index 851de035b..d044d518e 100644 --- a/tests/capabilities/test_extension_registry.py +++ b/tests/capabilities/test_extension_registry.py @@ -16,8 +16,8 @@ import pytest -from agentpool.capabilities.change_event import ChangeEvent -from agentpool.capabilities.extension_registry import ( +from wolfharness.capabilities.change_event import ChangeEvent +from wolfharness.capabilities.extension_registry import ( CircularCompositionError, ExtensionRegistry, Scope, @@ -50,7 +50,7 @@ def __init__(self, name: str = "skill_cap", skills: dict[str, str] | None = None self._skills = skills or {"test-skill": "skill content"} async def list_skills(self) -> list[Any]: - from agentpool.capabilities.resource_protocols import SkillEntry + from wolfharness.capabilities.resource_protocols import SkillEntry return [ SkillEntry(name=n, description=d, uri=f"skill://{n}") for n, d in self._skills.items() @@ -79,17 +79,17 @@ async def list_tools(self) -> list[Any]: return [] async def call_tool(self, name: str, args: dict[str, Any]) -> Any: - from agentpool.capabilities.resource_protocols import ToolResult + from wolfharness.capabilities.resource_protocols import ToolResult return ToolResult(content="tool result") async def list_resources(self) -> list[Any]: - from agentpool.capabilities.resource_protocols import ResourceEntry + from wolfharness.capabilities.resource_protocols import ResourceEntry return [ResourceEntry(uri=u, name=n) for u, n in self._resources.items()] async def read_resource(self, uri: str) -> list[Any] | None: - from agentpool.capabilities.resource_protocols import TextResourceContent + from wolfharness.capabilities.resource_protocols import TextResourceContent content = self._resources.get(uri) if content is None: @@ -126,12 +126,12 @@ def __init__(self, name: str = "cmd_cap") -> None: super().__init__(name) async def list_commands(self) -> list[Any]: - from agentpool.capabilities.resource_protocols import CommandEntry + from wolfharness.capabilities.resource_protocols import CommandEntry return [CommandEntry(name="test-cmd", description="Test command")] async def get_command(self, name: str) -> Any: - from agentpool.capabilities.resource_protocols import CommandEntry + from wolfharness.capabilities.resource_protocols import CommandEntry if name == "test-cmd": return CommandEntry(name=name, description="Test command") @@ -145,7 +145,7 @@ def __init__(self, name: str = "template_cap") -> None: super().__init__(name) async def list_resource_templates(self) -> list[Any]: - from agentpool.capabilities.resource_protocols import ResourceTemplateEntry + from wolfharness.capabilities.resource_protocols import ResourceTemplateEntry return [ ResourceTemplateEntry( @@ -161,7 +161,7 @@ async def complete_resource_template( argument: Any, context: dict[str, str] | None = None, ) -> Any: - from agentpool.capabilities.resource_protocols import CompletionResult + from wolfharness.capabilities.resource_protocols import CompletionResult return CompletionResult(values=["completion1", "completion2"]) @@ -408,7 +408,7 @@ class TestURIRouting: @pytest.mark.asyncio async def test_resolve_skill_uri(self) -> None: - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill reg = ExtensionRegistry() skill_cap = FakeSkillResource(skills={"my-skill": "skill content here"}) diff --git a/tests/capabilities/test_extension_registry_errors.py b/tests/capabilities/test_extension_registry_errors.py index cd1e8b369..b09e35839 100644 --- a/tests/capabilities/test_extension_registry_errors.py +++ b/tests/capabilities/test_extension_registry_errors.py @@ -12,7 +12,7 @@ import pytest -from agentpool.capabilities.extension_registry import ( +from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, @@ -37,7 +37,7 @@ def name(self) -> str: return self._name async def list_skills(self) -> list: - from agentpool.capabilities.resource_protocols import SkillEntry + from wolfharness.capabilities.resource_protocols import SkillEntry entries = [] for name, content in self._skills.items(): @@ -71,7 +71,7 @@ async def list_tools(self) -> list: return [] async def call_tool(self, name: str, args: dict[str, Any]) -> Any: - from agentpool.capabilities.resource_protocols import ToolResult + from wolfharness.capabilities.resource_protocols import ToolResult self._call_count += 1 if self._call_count <= self._timeout_count: @@ -115,7 +115,7 @@ async def test_corrupted_skill_not_resolvable(self) -> None: @pytest.mark.asyncio async def test_good_skill_still_resolvable(self) -> None: """Non-corrupted skills still resolve correctly.""" - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill reg = ExtensionRegistry() cap = CorruptSkillResource() @@ -159,7 +159,7 @@ async def test_other_capabilities_continue_working(self) -> None: class GoodMcpResource(TimeoutMcpResource): async def call_tool(self, name: str, args: dict[str, Any]) -> Any: - from agentpool.capabilities.resource_protocols import ToolResult + from wolfharness.capabilities.resource_protocols import ToolResult return ToolResult(content="good server working") diff --git a/tests/capabilities/test_extension_registry_integration.py b/tests/capabilities/test_extension_registry_integration.py index 315bad643..c3b9e1f6a 100644 --- a/tests/capabilities/test_extension_registry_integration.py +++ b/tests/capabilities/test_extension_registry_integration.py @@ -8,7 +8,7 @@ import pytest -from agentpool.capabilities.extension_registry import ( +from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, @@ -30,7 +30,7 @@ def name(self) -> str: return self._name async def list_skills(self) -> list: - from agentpool.capabilities.resource_protocols import SkillEntry + from wolfharness.capabilities.resource_protocols import SkillEntry return [ SkillEntry(name=n, description=d, uri=f"skill://{n}") for n, d in self._skills.items() @@ -112,7 +112,7 @@ async def test_resolve_uri_session_scoped(self) -> None: Scope(level=ScopeLevel.SESSION, session_id="ses2"), ) - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill assert isinstance(result1, Skill) assert result1.instructions == "Session 1 content" diff --git a/tests/capabilities/test_filtered_toolset.py b/tests/capabilities/test_filtered_toolset.py index 3ba292819..f5946aa36 100644 --- a/tests/capabilities/test_filtered_toolset.py +++ b/tests/capabilities/test_filtered_toolset.py @@ -8,9 +8,9 @@ from pydantic_ai.toolsets import FilteredToolset import pytest -from agentpool.capabilities.filtered_toolset import FilteredToolsetCapability -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.tools.base import Tool +from wolfharness.capabilities.filtered_toolset import FilteredToolsetCapability +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.tools.base import Tool pytestmark = pytest.mark.unit @@ -196,7 +196,7 @@ async def test_get_toolset_wraps_concrete_toolset() -> None: tool_a = _make_test_tool("tool_a") tool_b = _make_test_tool("tool_b") - from agentpool.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability inner = FunctionToolsetCapability([tool_a, tool_b], name="test") @@ -219,7 +219,7 @@ async def test_concrete_toolset_filter_excludes_tools() -> None: tool_b = _make_test_tool("tool_b") tool_c = _make_test_tool("tool_c") - from agentpool.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability inner = FunctionToolsetCapability([tool_a, tool_b, tool_c], name="test") @@ -240,7 +240,7 @@ async def filter_func(_ctx: Any, tool_def: Any) -> bool: @pytest.mark.asyncio async def test_concrete_toolset_returns_none_when_inner_returns_none() -> None: """Concrete toolset path returns None when inner returns None.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability inner = FunctionToolsetCapability(name="empty") @@ -268,7 +268,7 @@ async def test_on_change_delegates_to_wrapped() -> None: @pytest.mark.asyncio -async def test_on_change_returns_none_for_non_agentpool_capability() -> None: +async def test_on_change_returns_none_for_non_wolfharness_capability() -> None: """on_change() returns None when wrapped capability doesn't implement on_change.""" class _BareCapability: diff --git a/tests/capabilities/test_function_toolset.py b/tests/capabilities/test_function_toolset.py index 98e874557..d723f3c19 100644 --- a/tests/capabilities/test_function_toolset.py +++ b/tests/capabilities/test_function_toolset.py @@ -7,8 +7,8 @@ from pydantic_ai.toolsets import FunctionToolset import pytest -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.tools.base import Tool +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.tools.base import Tool pytestmark = pytest.mark.unit diff --git a/tests/capabilities/test_mcp_server_cap.py b/tests/capabilities/test_mcp_server_cap.py index 27543625a..347c781d5 100644 --- a/tests/capabilities/test_mcp_server_cap.py +++ b/tests/capabilities/test_mcp_server_cap.py @@ -21,9 +21,9 @@ from pydantic import AnyUrl import pytest -from agentpool.capabilities.change_event import ChangeEvent -from agentpool.capabilities.mcp_server_cap import McpServerCap -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.change_event import ChangeEvent +from wolfharness.capabilities.mcp_server_cap import McpServerCap +from wolfharness.capabilities.resource_protocols import ( BlobResourceContent, ChangeObservable, CompletionArgument, @@ -36,7 +36,7 @@ ToolAccess, ToolEntry, ) -from agentpool.mcp_server.message_handler import MCPMessageHandler +from wolfharness.mcp_server.message_handler import MCPMessageHandler pytestmark = pytest.mark.unit @@ -850,8 +850,8 @@ async def callback() -> None: @pytest.mark.anyio async def test_mcpclient_subscribe_resource_delegates_to_session() -> None: """subscribe_resource calls ClientSession.subscribe_resource with AnyUrl.""" - from agentpool.mcp_server.client import MCPClient - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness_config.mcp_server import StdioMCPServerConfig config = StdioMCPServerConfig(command="echo", args=["test"]) client = MCPClient(config) @@ -873,8 +873,8 @@ async def test_mcpclient_subscribe_resource_delegates_to_session() -> None: @pytest.mark.anyio async def test_mcpclient_unsubscribe_resource_delegates_to_session() -> None: """unsubscribe_resource calls ClientSession.unsubscribe_resource with AnyUrl.""" - from agentpool.mcp_server.client import MCPClient - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness_config.mcp_server import StdioMCPServerConfig config = StdioMCPServerConfig(command="echo", args=["test"]) client = MCPClient(config) @@ -896,8 +896,8 @@ async def test_mcpclient_unsubscribe_resource_delegates_to_session() -> None: @pytest.mark.anyio async def test_mcpclient_subscribe_resource_not_connected_raises() -> None: """subscribe_resource raises RuntimeError when not connected.""" - from agentpool.mcp_server.client import MCPClient - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness_config.mcp_server import StdioMCPServerConfig config = StdioMCPServerConfig(command="echo", args=["test"]) client = MCPClient(config) diff --git a/tests/capabilities/test_question_capability.py b/tests/capabilities/test_question_capability.py index 877bfe0df..62131eda4 100644 --- a/tests/capabilities/test_question_capability.py +++ b/tests/capabilities/test_question_capability.py @@ -8,8 +8,8 @@ from pydantic_ai.toolsets import FunctionToolset import pytest -from agentpool.agents.context import AgentContext -from agentpool.capabilities.question import QuestionCapability +from wolfharness.agents.context import AgentContext +from wolfharness.capabilities.question import QuestionCapability def test_get_toolset_returns_function_toolset_without_schemas(): diff --git a/tests/capabilities/test_registry.py b/tests/capabilities/test_registry.py index 981c62f6c..a99fefb9e 100644 --- a/tests/capabilities/test_registry.py +++ b/tests/capabilities/test_registry.py @@ -15,7 +15,7 @@ import pytest -from agentpool.capabilities.registry import ( +from wolfharness.capabilities.registry import ( CapabilityNotFoundError, discover_entry_point_capabilities, resolve_capability_type, @@ -28,7 +28,7 @@ if TYPE_CHECKING: from pydantic_ai.capabilities import AbstractCapability - from agentpool import AgentPool + from wolfharness import AgentPool # ---- Helpers ---- @@ -61,14 +61,14 @@ def _make_fake_entry_points( def test_discover_returns_mapping_from_entry_points() -> None: """discover_entry_point_capabilities returns a name→class mapping.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.capabilities.subagent_capability import SubagentCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.subagent_capability import SubagentCapability fake_eps = _make_fake_entry_points( {"function_tools": FunctionToolsetCapability, "subagent": SubagentCapability}, ) with patch( - "agentpool.capabilities.registry.entry_points", + "wolfharness.capabilities.registry.entry_points", return_value=fake_eps, ): result = discover_entry_point_capabilities() @@ -78,7 +78,7 @@ def test_discover_returns_mapping_from_entry_points() -> None: def test_discover_returns_empty_when_no_entry_points() -> None: """discover_entry_point_capabilities returns empty dict when no entry points.""" with patch( - "agentpool.capabilities.registry.entry_points", + "wolfharness.capabilities.registry.entry_points", return_value=[], ): result = discover_entry_point_capabilities() @@ -87,15 +87,15 @@ def test_discover_returns_empty_when_no_entry_points() -> None: def test_discover_first_wins_on_duplicate_names() -> None: """discover_entry_point_capabilities keeps the first entry on duplicate names.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.capabilities.subagent_capability import SubagentCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.subagent_capability import SubagentCapability fake_eps = [ _FakeEntryPoint("dup", FunctionToolsetCapability), _FakeEntryPoint("dup", SubagentCapability), ] with patch( - "agentpool.capabilities.registry.entry_points", + "wolfharness.capabilities.registry.entry_points", return_value=fake_eps, ): result = discover_entry_point_capabilities() @@ -133,7 +133,7 @@ def test_capability_not_found_error_with_empty_available() -> None: def test_resolve_capability_type_found() -> None: """resolve_capability_type returns the class when type is registered.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability registry: dict[str, type[AbstractCapability[object]]] = { "function_tools": FunctionToolsetCapability, @@ -158,8 +158,8 @@ def test_resolve_capability_type_not_found_raises() -> None: def test_factory_compile_discovers_entry_point_capabilities(minimal_pool: AgentPool) -> None: """AgentFactory.compile() populates entry_point_capabilities from discovery.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.host.factory import AgentFactory + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.host.factory import AgentFactory fake_eps = _make_fake_entry_points({"function_tools": FunctionToolsetCapability}) @@ -167,10 +167,10 @@ def test_factory_compile_discovers_entry_point_capabilities(minimal_pool: AgentP with ( patch( - "agentpool.capabilities.registry.entry_points", + "wolfharness.capabilities.registry.entry_points", return_value=fake_eps, ), - patch("agentpool.host.registry.AgentRegistry"), + patch("wolfharness.host.registry.AgentRegistry"), ): # compile() iterates manifest.agents — use empty manifest. mock_manifest = MagicMock() @@ -183,9 +183,9 @@ def test_factory_compile_discovers_entry_point_capabilities(minimal_pool: AgentP def test_factory_resolve_capability_type_known(minimal_pool: AgentPool) -> None: """AgentFactory.resolve_capability_type resolves a known type.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.capabilities.subagent_capability import SubagentCapability - from agentpool.host.factory import AgentFactory + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.capabilities.subagent_capability import SubagentCapability + from wolfharness.host.factory import AgentFactory fake_eps = _make_fake_entry_points( {"function_tools": FunctionToolsetCapability, "subagent": SubagentCapability}, @@ -194,10 +194,10 @@ def test_factory_resolve_capability_type_known(minimal_pool: AgentPool) -> None: with ( patch( - "agentpool.capabilities.registry.entry_points", + "wolfharness.capabilities.registry.entry_points", return_value=fake_eps, ), - patch("agentpool.host.registry.AgentRegistry"), + patch("wolfharness.host.registry.AgentRegistry"), ): mock_manifest = MagicMock() mock_manifest.agents = {} @@ -210,18 +210,18 @@ def test_factory_resolve_capability_type_known(minimal_pool: AgentPool) -> None: def test_factory_resolve_capability_type_unknown_raises(minimal_pool: AgentPool) -> None: """AgentFactory.resolve_capability_type raises for unknown type.""" - from agentpool.capabilities.function_toolset import FunctionToolsetCapability - from agentpool.host.factory import AgentFactory + from wolfharness.capabilities.function_toolset import FunctionToolsetCapability + from wolfharness.host.factory import AgentFactory fake_eps = _make_fake_entry_points({"function_tools": FunctionToolsetCapability}) factory = AgentFactory(minimal_pool) with ( patch( - "agentpool.capabilities.registry.entry_points", + "wolfharness.capabilities.registry.entry_points", return_value=fake_eps, ), - patch("agentpool.host.registry.AgentRegistry"), + patch("wolfharness.host.registry.AgentRegistry"), ): mock_manifest = MagicMock() mock_manifest.agents = {} diff --git a/tests/capabilities/test_resource_capability.py b/tests/capabilities/test_resource_capability.py index 8626c0c31..0ce45b9a1 100644 --- a/tests/capabilities/test_resource_capability.py +++ b/tests/capabilities/test_resource_capability.py @@ -11,10 +11,10 @@ from pydantic_ai.toolsets import FunctionToolset import pytest -from agentpool.capabilities.agent_context import AgentContextDeps -from agentpool.capabilities.extension_registry import ExtensionRegistry, Scope, ScopeLevel -from agentpool.capabilities.resource_capability import ResourceCapability -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.agent_context import AgentContextDeps +from wolfharness.capabilities.extension_registry import ExtensionRegistry, Scope, ScopeLevel +from wolfharness.capabilities.resource_capability import ResourceCapability +from wolfharness.capabilities.resource_protocols import ( BlobResourceContent, CompletionArgument, CompletionResult, @@ -23,7 +23,7 @@ SkillEntry, TextResourceContent, ) -from agentpool.host.context import RunScope +from wolfharness.host.context import RunScope if TYPE_CHECKING: @@ -1039,7 +1039,7 @@ async def test_resolve_agent_context_from_runtime_context() -> None: ctx.deps = agents.context.AgentContext, and our capabilities.agent_context.AgentContextDeps is at ctx.deps.data. """ - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext agent_ctx = _make_agent_context(registry=None) runtime_ctx = RuntimeAgentContext(node=MagicMock()) @@ -1070,7 +1070,7 @@ async def test_resolve_agent_context_none_deps() -> None: async def test_resolve_agent_context_runtime_ctx_none_data() -> None: """_resolve_agent_context raises RuntimeError when RuntimeAgentContext.data is None.""" - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext runtime_ctx = RuntimeAgentContext(node=MagicMock()) runtime_ctx.data = None diff --git a/tests/capabilities/test_resource_registration.py b/tests/capabilities/test_resource_registration.py index 5da44de20..ba5e7ca43 100644 --- a/tests/capabilities/test_resource_registration.py +++ b/tests/capabilities/test_resource_registration.py @@ -14,8 +14,8 @@ import pytest import yamling -from agentpool import AgentPool, AgentsManifest -from agentpool_config.nodes import BaseAgentConfig, ResourceConfig +from wolfharness import AgentPool, AgentsManifest +from wolfharness_config.nodes import BaseAgentConfig, ResourceConfig if TYPE_CHECKING: @@ -124,8 +124,8 @@ async def test_resource_capability_registered_after_enter(minimal_pool): but it should NOT appear in ``get_resource_access()`` results because it is a tool wrapper, not a ``ResourceAccess`` data provider. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.resource_capability import ResourceCapability + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.resource_capability import ResourceCapability pool = minimal_pool assert pool.resource_capability is not None diff --git a/tests/capabilities/test_review_fixes.py b/tests/capabilities/test_review_fixes.py index f0ca81935..7b3707eea 100644 --- a/tests/capabilities/test_review_fixes.py +++ b/tests/capabilities/test_review_fixes.py @@ -15,7 +15,7 @@ import pytest -from agentpool.capabilities.mcp_server_cap import McpServerCap +from wolfharness.capabilities.mcp_server_cap import McpServerCap pytestmark = pytest.mark.unit @@ -24,7 +24,7 @@ if TYPE_CHECKING: from typing import Self - from agentpool.capabilities.resource_protocols import SkillEntry + from wolfharness.capabilities.resource_protocols import SkillEntry # --------------------------------------------------------------------------- @@ -225,8 +225,8 @@ class TestMatcherFnBackwardCompat: @pytest.mark.asyncio async def test_two_arg_matcher_fn_receives_skill_names(self) -> None: """Matcher functions expecting (messages, skill_names) should work.""" - from agentpool.capabilities.skill_manager_cap import SkillManagerCap - from agentpool.skills.skill import Skill + from wolfharness.capabilities.skill_manager_cap import SkillManagerCap + from wolfharness.skills.skill import Skill # Create a minimal skill skill = MagicMock(spec=Skill) @@ -269,7 +269,7 @@ def matcher_2arg(messages: Any, skill_names: list[str]) -> list[str]: @pytest.mark.asyncio async def test_one_arg_matcher_fn_still_works(self) -> None: """Single-arg matcher functions should continue to work.""" - from agentpool.capabilities.skill_manager_cap import SkillManagerCap + from wolfharness.capabilities.skill_manager_cap import SkillManagerCap skill = MagicMock() skill.name = "test-skill" @@ -322,13 +322,13 @@ async def test_resolve_uri_with_provider_routes_to_correct_cap(self) -> None: With D9, provider segment is removed. skill://shared-name iterates all caps and returns the first match. """ - from agentpool.capabilities.extension_registry import ( + from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, ) - from agentpool.capabilities.resource_protocols import SkillEntry - from agentpool.skills.skill import Skill + from wolfharness.capabilities.resource_protocols import SkillEntry + from wolfharness.skills.skill import Skill class NamedSkillCap: """Fake SkillResource with a configurable serialization name.""" @@ -369,13 +369,13 @@ async def skill_exists(self, name: str) -> bool: @pytest.mark.asyncio async def test_resolve_uri_without_provider_iterates_all(self) -> None: """skill://name without provider should iterate all (backward compat).""" - from agentpool.capabilities.extension_registry import ( + from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, ) - from agentpool.capabilities.resource_protocols import SkillEntry - from agentpool.skills.skill import Skill + from wolfharness.capabilities.resource_protocols import SkillEntry + from wolfharness.skills.skill import Skill class NamedSkillCap: def __init__(self, ser_name: str, skills: dict[str, str]) -> None: diff --git a/tests/capabilities/test_runloop_delegation_review.py b/tests/capabilities/test_runloop_delegation_review.py index 91d7183a9..3129089d9 100644 --- a/tests/capabilities/test_runloop_delegation_review.py +++ b/tests/capabilities/test_runloop_delegation_review.py @@ -14,12 +14,12 @@ import pytest -from agentpool.agents.events.events import StreamCompleteEvent -from agentpool.capabilities.runloop_delegation import RunLoopDelegationService -from agentpool.host.context import HostContext -from agentpool.host.registry import AgentRegistry -from agentpool.orchestrator.core import EventBus -from agentpool.orchestrator.run import RunHandle +from wolfharness.agents.events.events import StreamCompleteEvent +from wolfharness.capabilities.runloop_delegation import RunLoopDelegationService +from wolfharness.host.context import HostContext +from wolfharness.host.registry import AgentRegistry +from wolfharness.orchestrator.core import EventBus +from wolfharness.orchestrator.run import RunHandle pytestmark = pytest.mark.unit diff --git a/tests/capabilities/test_runloop_injection.py b/tests/capabilities/test_runloop_injection.py index 40f15fd99..96c128581 100644 --- a/tests/capabilities/test_runloop_injection.py +++ b/tests/capabilities/test_runloop_injection.py @@ -11,13 +11,13 @@ import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.capabilities.agent_context import AgentContextDeps -from agentpool.capabilities.delegation import DelegationService -from agentpool.capabilities.runloop_delegation import RunLoopDelegationService -from agentpool.host.context import HostContext, RunScope -from agentpool.host.registry import AgentRegistry -from agentpool.orchestrator.run import RunHandle +from wolfharness.agents.context import AgentRunContext +from wolfharness.capabilities.agent_context import AgentContextDeps +from wolfharness.capabilities.delegation import DelegationService +from wolfharness.capabilities.runloop_delegation import RunLoopDelegationService +from wolfharness.host.context import HostContext, RunScope +from wolfharness.host.registry import AgentRegistry +from wolfharness.orchestrator.run import RunHandle pytestmark = pytest.mark.unit @@ -163,7 +163,7 @@ def test_delegation_service_lists_agents() -> None: def test_delegation_service_raises_for_unknown_agent() -> None: """Test that RunLoopDelegationService raises for unknown agents.""" - from agentpool.capabilities.delegation import AgentNotFoundError + from wolfharness.capabilities.delegation import AgentNotFoundError host = _make_host_context() registry = AgentRegistry() diff --git a/tests/capabilities/test_session_pool_get_client.py b/tests/capabilities/test_session_pool_get_client.py index 8590e14ab..af0ea0fe4 100644 --- a/tests/capabilities/test_session_pool_get_client.py +++ b/tests/capabilities/test_session_pool_get_client.py @@ -14,8 +14,8 @@ import pytest -from agentpool.mcp_server.session_pool import SessionConnectionPool -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness.mcp_server.session_pool import SessionConnectionPool +from wolfharness_config.mcp_server import StdioMCPServerConfig pytestmark = pytest.mark.unit @@ -40,11 +40,11 @@ async def test_get_client_returns_connected_client() -> None: with ( patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", return_value=MagicMock(), ), patch( - "agentpool.mcp_server.client.MCPClient", + "wolfharness.mcp_server.client.MCPClient", return_value=mock_client_instance, ), ): @@ -84,11 +84,11 @@ def fake_create_transport(cfg: Any) -> Any: with ( patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", side_effect=fake_create_transport, ), patch( - "agentpool.mcp_server.client.MCPClient", + "wolfharness.mcp_server.client.MCPClient", side_effect=[mock_client_1, mock_client_2], ), ): @@ -132,11 +132,11 @@ def fake_create_transport(cfg: Any) -> Any: with ( patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", side_effect=fake_create_transport, ), patch( - "agentpool.mcp_server.client.MCPClient", + "wolfharness.mcp_server.client.MCPClient", side_effect=[mock_client_1, mock_client_2], ), ): diff --git a/tests/capabilities/test_skill_manager_cap_integration.py b/tests/capabilities/test_skill_manager_cap_integration.py index e7a8f6eba..dd9d2a5e9 100644 --- a/tests/capabilities/test_skill_manager_cap_integration.py +++ b/tests/capabilities/test_skill_manager_cap_integration.py @@ -15,12 +15,12 @@ from pydantic_ai.capabilities import AbstractCapability import pytest -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.resource_protocols import ( SkillEntry, SkillResource, ) -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.skills.skill import Skill +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.skills.skill import Skill pytestmark = pytest.mark.unit @@ -467,7 +467,7 @@ async def test_safe_uri_flat_form() -> None: """Skill.safe_uri returns flat skill://{name} for local skills.""" from upathtools import UPath - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill # Local filesystem skill local_skill = Skill( diff --git a/tests/capabilities/test_skill_manager_cap_tools.py b/tests/capabilities/test_skill_manager_cap_tools.py index b04ba3081..c8e180389 100644 --- a/tests/capabilities/test_skill_manager_cap_tools.py +++ b/tests/capabilities/test_skill_manager_cap_tools.py @@ -3,7 +3,7 @@ Covers ``SkillManagerCap._load_skill_impl`` / ``_list_skills_impl`` and the bare-name helpers (``_load_visible_bare_skill`` / ``_available_skill_names``), which are the real path pydantic-ai agents use (vs. the backward-compat -wrappers in ``agentpool_toolsets/builtin/skills.py``). +wrappers in ``wolfharness_toolsets/builtin/skills.py``). Specifically verifies: - bare-name load of a local skill @@ -22,9 +22,9 @@ import pytest -from agentpool.capabilities.resource_protocols import SkillEntry, SkillResource -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.skills.skill import Skill +from wolfharness.capabilities.resource_protocols import SkillEntry, SkillResource +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.skills.skill import Skill pytestmark = pytest.mark.unit @@ -73,7 +73,7 @@ def _local_skill(name: str, desc: str = "local") -> Skill: def _make_ctx(pool: Any, node_name: str | None = None) -> Any: """Build a RunContext-like object with a RuntimeAgentContext deps.""" - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext node = MagicMock() node.name = node_name if node_name is not None else "default-node" diff --git a/tests/capabilities/test_skill_tool_registration.py b/tests/capabilities/test_skill_tool_registration.py index 16167d82f..4f1310e72 100644 --- a/tests/capabilities/test_skill_tool_registration.py +++ b/tests/capabilities/test_skill_tool_registration.py @@ -35,14 +35,14 @@ ) import pytest -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.resource_protocols import ( SkillEntry, SkillResource, ) -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.skills.skill import Skill -from agentpool.skills.skill_tool_manager import SkillToolManager -from agentpool_config.skills import SkillMcpServerConfig, SkillToolConfig +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.skills.skill import Skill +from wolfharness.skills.skill_tool_manager import SkillToolManager +from wolfharness_config.skills import SkillMcpServerConfig, SkillToolConfig pytestmark = pytest.mark.unit @@ -480,7 +480,7 @@ def test_invalid_import_path_graceful_error() -> None: ) manager = SkillToolManager() - with patch("agentpool.capabilities.skill_manager_cap.logger"): + with patch("wolfharness.capabilities.skill_manager_cap.logger"): cap = SkillManagerCap( local_skills={"bad-skill": bad_skill, "good-skill": good_skill}, tool_manager=manager, @@ -518,7 +518,7 @@ def test_mcp_creation_failure_other_skills_unaffected() -> None: ) manager = SkillToolManager() - with patch("agentpool.capabilities.skill_manager_cap.logger"): + with patch("wolfharness.capabilities.skill_manager_cap.logger"): cap = SkillManagerCap( local_skills={ "bad-mcp-skill": bad_mcp_skill, diff --git a/tests/capabilities/test_skill_watcher.py b/tests/capabilities/test_skill_watcher.py index c69b422bb..a7ad6ab89 100644 --- a/tests/capabilities/test_skill_watcher.py +++ b/tests/capabilities/test_skill_watcher.py @@ -12,7 +12,7 @@ import pytest -from agentpool.capabilities.skill_watcher import SkillFilesystemWatcher +from wolfharness.capabilities.skill_watcher import SkillFilesystemWatcher pytestmark = pytest.mark.unit diff --git a/tests/capabilities/test_subagent_capability.py b/tests/capabilities/test_subagent_capability.py index 78c0a21cd..bcedf8344 100644 --- a/tests/capabilities/test_subagent_capability.py +++ b/tests/capabilities/test_subagent_capability.py @@ -9,10 +9,10 @@ from pydantic_ai.toolsets import FunctionToolset import pytest -from agentpool.capabilities.agent_context import AgentContextDeps -from agentpool.capabilities.delegation import AgentNotFoundError, DelegationService -from agentpool.capabilities.subagent_capability import SubagentCapability -from agentpool.host.context import RunScope +from wolfharness.capabilities.agent_context import AgentContextDeps +from wolfharness.capabilities.delegation import AgentNotFoundError, DelegationService +from wolfharness.capabilities.subagent_capability import SubagentCapability +from wolfharness.host.context import RunScope pytestmark = pytest.mark.unit @@ -246,7 +246,7 @@ async def test_resolve_agent_context_from_runtime_context() -> None: ctx.deps = agents.context.AgentContext, and our capabilities.agent_context.AgentContextDeps is at ctx.deps.data. """ - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext delegation = FakeDelegationService(agents=["alpha", "beta"]) host = MagicMock() @@ -285,7 +285,7 @@ async def test_resolve_agent_context_none_deps() -> None: async def test_resolve_agent_context_runtime_ctx_none_data() -> None: """_resolve_agent_context raises RuntimeError when RuntimeAgentContext.data is None.""" - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext runtime_ctx = RuntimeAgentContext(node=MagicMock()) runtime_ctx.data = None diff --git a/tests/capabilities/test_tool_display_capability.py b/tests/capabilities/test_tool_display_capability.py index c5e412d9c..dc57d3353 100644 --- a/tests/capabilities/test_tool_display_capability.py +++ b/tests/capabilities/test_tool_display_capability.py @@ -10,11 +10,11 @@ from pydantic_ai.toolsets import RenamedToolset import pytest -from agentpool.capabilities.tool_display_capability import ToolDisplayCapability +from wolfharness.capabilities.tool_display_capability import ToolDisplayCapability if TYPE_CHECKING: - from agentpool.agents.events import DiffContentItem + from wolfharness.agents.events import DiffContentItem pytestmark = pytest.mark.unit diff --git a/tests/capabilities/test_tool_display_integration.py b/tests/capabilities/test_tool_display_integration.py index f41d4b208..a34e86465 100644 --- a/tests/capabilities/test_tool_display_integration.py +++ b/tests/capabilities/test_tool_display_integration.py @@ -15,14 +15,14 @@ from pydantic_ai.toolsets.renamed import RenamedToolset import pytest -from agentpool.capabilities.tool_display_capability import ToolDisplayCapability -from agentpool.capabilities.viking import VikingCapability -from agentpool.capabilities.viking.identity import VikingIdentity -from agentpool_config.capabilities import VikingCapabilityConfig, build_capability +from wolfharness.capabilities.tool_display_capability import ToolDisplayCapability +from wolfharness.capabilities.viking import VikingCapability +from wolfharness.capabilities.viking.identity import VikingIdentity +from wolfharness_config.capabilities import VikingCapabilityConfig, build_capability if TYPE_CHECKING: - from agentpool.agents.events import DiffContentItem + from wolfharness.agents.events import DiffContentItem pytestmark = pytest.mark.integration diff --git a/tests/capabilities/test_tool_display_semantics.py b/tests/capabilities/test_tool_display_semantics.py index bbbf0b185..044973c78 100644 --- a/tests/capabilities/test_tool_display_semantics.py +++ b/tests/capabilities/test_tool_display_semantics.py @@ -22,11 +22,11 @@ from pydantic_ai.toolsets import AbstractToolset, RenamedToolset import pytest -from agentpool.capabilities.tool_display_capability import ToolDisplayCapability +from wolfharness.capabilities.tool_display_capability import ToolDisplayCapability if TYPE_CHECKING: - from agentpool.agents.events import DiffContentItem + from wolfharness.agents.events import DiffContentItem pytestmark = pytest.mark.unit diff --git a/tests/capabilities/test_transport_reuse.py b/tests/capabilities/test_transport_reuse.py index d92076f77..044ea6781 100644 --- a/tests/capabilities/test_transport_reuse.py +++ b/tests/capabilities/test_transport_reuse.py @@ -11,9 +11,9 @@ import pytest -from agentpool.capabilities.mcp_server_cap import McpServerCap -from agentpool.mcp_server.session_pool import SessionConnectionPool -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness.capabilities.mcp_server_cap import McpServerCap +from wolfharness.mcp_server.session_pool import SessionConnectionPool +from wolfharness_config.mcp_server import StdioMCPServerConfig pytestmark = pytest.mark.unit @@ -44,11 +44,11 @@ def fake_create_transport(cfg: Any) -> Any: with ( patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", side_effect=fake_create_transport, ), patch( - "agentpool.mcp_server.client.MCPClient", + "wolfharness.mcp_server.client.MCPClient", side_effect=[mock_client_1, mock_client_2], ), ): @@ -92,11 +92,11 @@ def fake_create_transport(cfg: Any) -> Any: with ( patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", side_effect=fake_create_transport, ), patch( - "agentpool.mcp_server.client.MCPClient", + "wolfharness.mcp_server.client.MCPClient", side_effect=[mock_client_1, mock_client_2], ), ): diff --git a/tests/capabilities/viking/test_viking.py b/tests/capabilities/viking/test_viking.py index 87817373f..d40934cc5 100644 --- a/tests/capabilities/viking/test_viking.py +++ b/tests/capabilities/viking/test_viking.py @@ -17,27 +17,27 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.capabilities.viking import VikingCapability, _normalize_search_results -from agentpool.capabilities.viking.identity import VikingIdentity, _try_decode_api_key -from agentpool.capabilities.viking.profile import ( +from wolfharness.capabilities.viking import VikingCapability, _normalize_search_results +from wolfharness.capabilities.viking.identity import VikingIdentity, _try_decode_api_key +from wolfharness.capabilities.viking.profile import ( _derive_context_hint, _format_profile_block, ) -from agentpool.capabilities.viking.recall import ( +from wolfharness.capabilities.viking.recall import ( _extract_latest_user_prompt, _format_recall_block, _inject_system_message, _rank_and_dedup, ) -from agentpool.capabilities.viking.tools import build_tools -from agentpool.capabilities.viking.utils import ( +from wolfharness.capabilities.viking.tools import build_tools +from wolfharness.capabilities.viking.utils import ( add_line_numbers, format_ls_entries, format_search_results, is_viking_uri, truncate_text, ) -from agentpool_config.capabilities import VikingCapabilityConfig +from wolfharness_config.capabilities import VikingCapabilityConfig pytestmark = pytest.mark.unit @@ -193,7 +193,7 @@ def test_discriminator_works(self) -> None: """The 'type' field discriminator correctly identifies VikingCapabilityConfig.""" import typing - from agentpool_config.capabilities import BuiltinCapabilityConfig + from wolfharness_config.capabilities import BuiltinCapabilityConfig cfg = VikingCapabilityConfig() assert cfg.type == "viking" @@ -1884,7 +1884,7 @@ class TestResourceAccessProtocol: def test_isinstance_resource_access(self, viking_cap: VikingCapability) -> None: """VikingCapability should be recognized as ResourceAccess.""" - from agentpool.capabilities.resource_protocols import ResourceAccess + from wolfharness.capabilities.resource_protocols import ResourceAccess assert isinstance(viking_cap, ResourceAccess) @@ -2040,44 +2040,44 @@ def test_supports_modality_no_caps(self) -> None: assert cap._supports_modality("audio/mpeg") is False def test_supports_modality_image(self) -> None: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability(model_capabilities=ModelCapabilities(image_input=True)) assert cap._supports_modality("image/png") is True assert cap._supports_modality("image/jpeg") is True def test_supports_modality_image_false(self) -> None: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability(model_capabilities=ModelCapabilities(image_input=False)) assert cap._supports_modality("image/png") is False def test_supports_modality_audio(self) -> None: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability(model_capabilities=ModelCapabilities(audio_input=True)) assert cap._supports_modality("audio/mpeg") is True def test_supports_modality_video(self) -> None: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability(model_capabilities=ModelCapabilities(video_input=True)) assert cap._supports_modality("video/mp4") is True def test_supports_modality_document(self) -> None: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability(model_capabilities=ModelCapabilities(document_input=True)) assert cap._supports_modality("application/pdf") is True def test_supports_modality_unknown(self) -> None: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability(model_capabilities=ModelCapabilities(image_input=True)) assert cap._supports_modality("application/zip") is False def test_guess_extension_known(self) -> None: - from agentpool.capabilities.viking import _guess_extension + from wolfharness.capabilities.viking import _guess_extension assert _guess_extension("image/png") == "png" assert _guess_extension("image/jpeg") == "jpg" @@ -2086,7 +2086,7 @@ def test_guess_extension_known(self) -> None: assert _guess_extension("application/pdf") == "pdf" def test_guess_extension_unknown(self) -> None: - from agentpool.capabilities.viking import _guess_extension + from wolfharness.capabilities.viking import _guess_extension assert _guess_extension("application/zip") == "bin" @@ -2129,7 +2129,7 @@ async def test_before_model_request_text_only_model( UserPromptPart, ) - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability( multimodal_bridge=True, @@ -2164,7 +2164,7 @@ async def test_before_model_request_multimodal_with_url( UserPromptPart, ) - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability( multimodal_bridge=True, @@ -2196,7 +2196,7 @@ async def test_before_model_request_multimodal_no_url( UserPromptPart, ) - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability( multimodal_bridge=True, @@ -2221,7 +2221,7 @@ async def test_before_model_request_upload_failure( UserPromptPart, ) - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability( multimodal_bridge=True, @@ -2284,7 +2284,7 @@ async def test_upload_binary_no_client(self) -> None: assert uri is None async def test_for_run_preserves_model_capabilities(self) -> None: - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities caps = ModelCapabilities(image_input=True) cap = VikingCapability(model_capabilities=caps, multimodal_bridge=True) @@ -3902,7 +3902,7 @@ class TestCompaction: def test_estimate_tokens_ascii(self) -> None: """ASCII text: chars / 4.""" - from agentpool.capabilities.viking.compaction import _estimate_tokens + from wolfharness.capabilities.viking.compaction import _estimate_tokens assert _estimate_tokens("") == 0 assert _estimate_tokens("hello") == 1 # 5 // 4 = 1 @@ -3911,7 +3911,7 @@ def test_estimate_tokens_ascii(self) -> None: def test_estimate_tokens_cjk(self) -> None: """CJK characters count as 1 token each.""" - from agentpool.capabilities.viking.compaction import _estimate_tokens + from wolfharness.capabilities.viking.compaction import _estimate_tokens # Each CJK char is 1 token assert _estimate_tokens("你好") == 2 @@ -3921,14 +3921,14 @@ def test_estimate_tokens_cjk(self) -> None: def test_estimate_tokens_mixed(self) -> None: """Mixed ASCII + CJK: ASCII at 4:1, CJK at 1:1.""" - from agentpool.capabilities.viking.compaction import _estimate_tokens + from wolfharness.capabilities.viking.compaction import _estimate_tokens # 2 CJK chars (2 tokens) + 8 ASCII chars (2 tokens) = 4 assert _estimate_tokens("你好abcdefgh") == 4 def test_estimate_tokens_empty(self) -> None: """Empty string returns 0.""" - from agentpool.capabilities.viking.compaction import _estimate_tokens + from wolfharness.capabilities.viking.compaction import _estimate_tokens assert _estimate_tokens("") == 0 @@ -3938,7 +3938,7 @@ def test_split_archivable_basic(self) -> None: """Split messages keeping last N turns.""" from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.compaction import _split_archivable + from wolfharness.capabilities.viking.compaction import _split_archivable # 4 user turns, keep last 2 messages: list[Any] = [] @@ -3960,7 +3960,7 @@ def test_split_archivable_keep_all_when_fewer_turns(self) -> None: """Keep all messages when fewer turns than keep_recent_turns.""" from pydantic_ai.messages import ModelRequest, UserPromptPart - from agentpool.capabilities.viking.compaction import _split_archivable + from wolfharness.capabilities.viking.compaction import _split_archivable messages = [ ModelRequest(parts=[UserPromptPart(content="Turn 1")]), @@ -3974,7 +3974,7 @@ def test_split_archivable_keep_zero(self) -> None: """keep_recent_turns=0 means all messages are archivable.""" from pydantic_ai.messages import ModelRequest, UserPromptPart - from agentpool.capabilities.viking.compaction import _split_archivable + from wolfharness.capabilities.viking.compaction import _split_archivable messages = [ ModelRequest(parts=[UserPromptPart(content="Turn 1")]), @@ -3986,7 +3986,7 @@ def test_split_archivable_keep_zero(self) -> None: def test_split_archivable_empty(self) -> None: """Empty messages list returns empty tuple.""" - from agentpool.capabilities.viking.compaction import _split_archivable + from wolfharness.capabilities.viking.compaction import _split_archivable archivable, keep = _split_archivable([], keep_recent_turns=3) assert archivable == [] @@ -3998,7 +3998,7 @@ def test_serialize_messages_basic(self) -> None: """Serialize messages as markdown with role headers.""" from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.compaction import _serialize_messages + from wolfharness.capabilities.viking.compaction import _serialize_messages messages = [ ModelRequest(parts=[UserPromptPart(content="Hello there")]), @@ -4012,7 +4012,7 @@ def test_serialize_messages_basic(self) -> None: def test_serialize_messages_empty(self) -> None: """Empty messages produce empty string.""" - from agentpool.capabilities.viking.compaction import _serialize_messages + from wolfharness.capabilities.viking.compaction import _serialize_messages assert _serialize_messages([]) == "" @@ -4022,7 +4022,7 @@ def test_summarize_messages_basic(self) -> None: """Summary contains first 200 chars of each message.""" from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.compaction import _summarize_messages + from wolfharness.capabilities.viking.compaction import _summarize_messages messages = [ ModelRequest(parts=[UserPromptPart(content="What is Python?")]), @@ -4036,7 +4036,7 @@ def test_summarize_messages_truncation(self) -> None: """Summary truncates long content to 200 chars.""" from pydantic_ai.messages import ModelRequest, UserPromptPart - from agentpool.capabilities.viking.compaction import _summarize_messages + from wolfharness.capabilities.viking.compaction import _summarize_messages long_text = "x" * 300 messages = [ @@ -4049,7 +4049,7 @@ def test_summarize_messages_truncation(self) -> None: def test_summarize_messages_empty(self) -> None: """Empty messages produce empty string.""" - from agentpool.capabilities.viking.compaction import _summarize_messages + from wolfharness.capabilities.viking.compaction import _summarize_messages assert _summarize_messages([]) == "" @@ -4377,7 +4377,7 @@ class TestAutoIngest: def test_sanitize_strips_recall_block(self) -> None: """_sanitize_message replaces with placeholder.""" - from agentpool.capabilities.viking.ingest import _sanitize_message + from wolfharness.capabilities.viking.ingest import _sanitize_message content = "Hello secret data world" result = _sanitize_message(content) @@ -4389,7 +4389,7 @@ def test_sanitize_strips_recall_block(self) -> None: def test_sanitize_strips_profile_block(self) -> None: """_sanitize_message replaces with placeholder.""" - from agentpool.capabilities.viking.ingest import _sanitize_message + from wolfharness.capabilities.viking.ingest import _sanitize_message content = "Context: user profile data end" result = _sanitize_message(content) @@ -4399,7 +4399,7 @@ def test_sanitize_strips_profile_block(self) -> None: def test_sanitize_strips_both_blocks(self) -> None: """_sanitize_message strips both recall and profile blocks.""" - from agentpool.capabilities.viking.ingest import _sanitize_message + from wolfharness.capabilities.viking.ingest import _sanitize_message content = ( "recall data" @@ -4413,7 +4413,7 @@ def test_sanitize_strips_both_blocks(self) -> None: def test_sanitize_multiline_blocks(self) -> None: """_sanitize_message handles multi-line XML blocks.""" - from agentpool.capabilities.viking.ingest import _sanitize_message + from wolfharness.capabilities.viking.ingest import _sanitize_message content = ( "\n \n data\n rest" @@ -4425,7 +4425,7 @@ def test_sanitize_multiline_blocks(self) -> None: def test_sanitize_disabled_returns_original(self) -> None: """When enabled=False, content is returned unchanged.""" - from agentpool.capabilities.viking.ingest import _sanitize_message + from wolfharness.capabilities.viking.ingest import _sanitize_message content = "keep this" result = _sanitize_message(content, enabled=False) @@ -4433,7 +4433,7 @@ def test_sanitize_disabled_returns_original(self) -> None: def test_sanitize_no_xml_blocks(self) -> None: """Content without XML blocks is returned unchanged.""" - from agentpool.capabilities.viking.ingest import _sanitize_message + from wolfharness.capabilities.viking.ingest import _sanitize_message content = "just plain text without any xml blocks" result = _sanitize_message(content) @@ -4441,7 +4441,7 @@ def test_sanitize_no_xml_blocks(self) -> None: def test_sanitize_empty_string(self) -> None: """Empty string returns empty string.""" - from agentpool.capabilities.viking.ingest import _sanitize_message + from wolfharness.capabilities.viking.ingest import _sanitize_message assert _sanitize_message("") == "" @@ -4451,7 +4451,7 @@ def test_extract_conversation_pairs_basic(self) -> None: """Extracts user+assistant pairs from messages.""" from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.ingest import _extract_conversation_pairs + from wolfharness.capabilities.viking.ingest import _extract_conversation_pairs messages = [ ModelRequest(parts=[UserPromptPart(content="What is X?")]), @@ -4466,7 +4466,7 @@ def test_extract_conversation_pairs_cursor_tracking(self) -> None: """Only extracts messages after start_idx.""" from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.ingest import _extract_conversation_pairs + from wolfharness.capabilities.viking.ingest import _extract_conversation_pairs messages = [ ModelRequest(parts=[UserPromptPart(content="old question")]), @@ -4483,7 +4483,7 @@ def test_extract_conversation_pairs_no_new_messages(self) -> None: """Returns empty list when start_idx >= len(messages).""" from pydantic_ai.messages import ModelRequest, UserPromptPart - from agentpool.capabilities.viking.ingest import _extract_conversation_pairs + from wolfharness.capabilities.viking.ingest import _extract_conversation_pairs messages = [ModelRequest(parts=[UserPromptPart(content="hello")])] result = _extract_conversation_pairs(messages, start_idx=1) @@ -4493,7 +4493,7 @@ def test_extract_conversation_pairs_multiple_turns(self) -> None: """Extracts multiple user+assistant turns.""" from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.ingest import _extract_conversation_pairs + from wolfharness.capabilities.viking.ingest import _extract_conversation_pairs messages = [ ModelRequest(parts=[UserPromptPart(content="Q1")]), @@ -4514,7 +4514,7 @@ def test_extract_conversation_pairs_skips_non_text_content(self) -> None: """Skips UserPromptPart with list (multimodal) content.""" from pydantic_ai.messages import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.capabilities.viking.ingest import _extract_conversation_pairs + from wolfharness.capabilities.viking.ingest import _extract_conversation_pairs messages = [ ModelRequest(parts=[UserPromptPart(content=["image_data", "text"])]), @@ -4529,7 +4529,7 @@ def test_extract_conversation_pairs_skips_non_text_content(self) -> None: def test_extract_conversation_pairs_empty_messages(self) -> None: """Empty messages list returns empty list.""" - from agentpool.capabilities.viking.ingest import _extract_conversation_pairs + from wolfharness.capabilities.viking.ingest import _extract_conversation_pairs assert _extract_conversation_pairs([], start_idx=0) == [] @@ -4941,9 +4941,9 @@ def test_config_auto_ingest_sanitize_default(self) -> None: assert cfg.auto_ingest_sanitize is True def test_config_auto_ingest_source_type_default(self) -> None: - """VikingCapabilityConfig has auto_ingest_source_type='agentpool' by default.""" + """VikingCapabilityConfig has auto_ingest_source_type='wolfharness' by default.""" cfg = VikingCapabilityConfig() - assert cfg.auto_ingest_source_type == "agentpool" + assert cfg.auto_ingest_source_type == "wolfharness" def test_config_auto_ingest_keep_recent_turns_default(self) -> None: """VikingCapabilityConfig has auto_ingest_keep_recent_turns=0 by default.""" diff --git a/tests/capabilities/viking/test_viking_e2e.py b/tests/capabilities/viking/test_viking_e2e.py index dabe071d9..d7e3f8de5 100644 --- a/tests/capabilities/viking/test_viking_e2e.py +++ b/tests/capabilities/viking/test_viking_e2e.py @@ -35,8 +35,8 @@ from pydantic_ai.messages import ToolReturn import pytest -from agentpool.capabilities.viking import VikingCapability -from agentpool.capabilities.viking.tools import build_tools +from wolfharness.capabilities.viking import VikingCapability +from wolfharness.capabilities.viking.tools import build_tools if TYPE_CHECKING: @@ -448,7 +448,7 @@ async def test_resource_access_workflow( viking_cap.resources_uri = test_dir viking_cap.resource_read_level = "read" try: - from agentpool.capabilities.resource_protocols import TextResourceContent + from wolfharness.capabilities.resource_protocols import TextResourceContent # read_resource result = await viking_cap.read_resource(resource_uri) @@ -488,7 +488,7 @@ async def test_multimodal_bridge( from pydantic_ai.models import ModelRequestContext from pydantic_ai.models.test import TestModel - from agentpool_config.model_capabilities import ModelCapabilities + from wolfharness_config.model_capabilities import ModelCapabilities cap = VikingCapability( mode="all", diff --git a/tests/capabilities/viking/test_viking_integration.py b/tests/capabilities/viking/test_viking_integration.py index 02a8424fb..6882f6fd1 100644 --- a/tests/capabilities/viking/test_viking_integration.py +++ b/tests/capabilities/viking/test_viking_integration.py @@ -26,10 +26,10 @@ import pytest import yamling -from agentpool.capabilities.viking import VikingCapability -from agentpool.capabilities.viking.identity import VikingIdentity -from agentpool.capabilities.viking.tools import build_tools -from agentpool_config.capabilities import VikingCapabilityConfig, build_capability +from wolfharness.capabilities.viking import VikingCapability +from wolfharness.capabilities.viking.identity import VikingIdentity +from wolfharness.capabilities.viking.tools import build_tools +from wolfharness_config.capabilities import VikingCapabilityConfig, build_capability pytestmark = pytest.mark.integration @@ -114,7 +114,7 @@ def _build_cap_from_config( def test_yaml_config_loading_viking_all() -> None: """YAML config with type=viking, mode=all produces VikingCapabilityConfig.""" - from agentpool import AgentsManifest + from wolfharness import AgentsManifest yaml_str = """ agents: @@ -137,7 +137,7 @@ def test_yaml_config_loading_viking_all() -> None: def test_yaml_config_loading_viking_retrieve() -> None: """YAML config with mode=retrieve produces VikingCapabilityConfig with that mode.""" - from agentpool import AgentsManifest + from wolfharness import AgentsManifest yaml_str = """ agents: @@ -157,7 +157,7 @@ def test_yaml_config_loading_viking_retrieve() -> None: def test_yaml_config_loading_viking_with_fields() -> None: """YAML config with all fields populated parses correctly.""" - from agentpool import AgentsManifest + from wolfharness import AgentsManifest yaml_str = """ agents: @@ -190,7 +190,7 @@ def test_yaml_config_loading_viking_with_fields() -> None: def test_yaml_config_loading_default_mode() -> None: """YAML config without mode defaults to 'all'.""" - from agentpool import AgentsManifest + from wolfharness import AgentsManifest yaml_str = """ agents: @@ -519,7 +519,7 @@ def test_resource_read_level_read() -> None: def test_resource_read_level_yaml_parsing() -> None: """YAML config with resource_read_level parses correctly.""" - from agentpool import AgentsManifest + from wolfharness import AgentsManifest yaml_str = """ agents: diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py index 42e6549da..135ff3f25 100644 --- a/tests/cli/test_cli.py +++ b/tests/cli/test_cli.py @@ -7,8 +7,8 @@ import pytest from typer.testing import CliRunner -from agentpool_cli.__main__ import cli -from agentpool_cli.agent import agent_cli +from wolfharness_cli.__main__ import cli +from wolfharness_cli.agent import agent_cli pytestmark = pytest.mark.unit @@ -43,11 +43,11 @@ def setup_environment(): """Disable logging and mock config store for all tests.""" # Disable logging - logging.getLogger("agentpool").setLevel(logging.CRITICAL) + logging.getLogger("wolfharness").setLevel(logging.CRITICAL) logging.getLogger("yamling").setLevel(logging.CRITICAL) # Mock ConfigStore - with mock.patch("agentpool_cli.agent.agent_store") as mock_store: + with mock.patch("wolfharness_cli.agent.agent_store") as mock_store: # Setup basic mock behavior mock_store.get_active.return_value = None mock_store.get_config.side_effect = KeyError("Not found") @@ -70,7 +70,7 @@ def test_list_agents_command(config_file: Path): assert "test_agent" in result.stdout -@mock.patch("agentpool_cli.agent.agent_store") +@mock.patch("wolfharness_cli.agent.agent_store") def test_add_agent_command(mock_store: mock.MagicMock, tmp_path: Path, config_file: Path): """Test that add command runs successfully.""" result = runner.invoke(agent_cli, ["add", "test", str(config_file)]) @@ -79,7 +79,7 @@ def test_add_agent_command(mock_store: mock.MagicMock, tmp_path: Path, config_fi mock_store.add_config.assert_called_once_with("test", str(config_file)) -@mock.patch("agentpool_cli.agent.agent_store") +@mock.patch("wolfharness_cli.agent.agent_store") def test_set_agent_command(mock_store: mock.MagicMock, config_file: Path): """Test that set command runs.""" # Configure mock to simulate existing config diff --git a/tests/cli/test_cli_config_path_resolution.py b/tests/cli/test_cli_config_path_resolution.py index 305942c8c..6ff1a2469 100644 --- a/tests/cli/test_cli_config_path_resolution.py +++ b/tests/cli/test_cli_config_path_resolution.py @@ -22,8 +22,8 @@ import pytest -from agentpool.models.manifest import AgentsManifest -from agentpool_config.context import CONFIG_DIR, ConfigContextManager +from wolfharness.models.manifest import AgentsManifest +from wolfharness_config.context import CONFIG_DIR, ConfigContextManager pytestmark = pytest.mark.unit @@ -108,7 +108,7 @@ def test_serve_opencode_resolves_paths_relative_to_config( os.chdir(cwd_outside_config) # Import and test the resolution logic - from agentpool_config.resolution import resolve_config + from wolfharness_config.resolution import resolve_config resolved = resolve_config(explicit_path=str(config_path)) @@ -147,7 +147,7 @@ def test_serve_opencode_without_context_resolves_to_cwd( try: os.chdir(cwd_outside_config) - from agentpool_config.resolution import resolve_config + from wolfharness_config.resolution import resolve_config resolved = resolve_config(explicit_path=str(config_path)) @@ -185,7 +185,7 @@ def test_serve_acp_resolves_paths_relative_to_config( try: os.chdir(cwd_outside_config) - from agentpool_config.resolution import resolve_config + from wolfharness_config.resolution import resolve_config resolved = resolve_config(explicit_path=str(config_path)) diff --git a/tests/cli/test_commands.py b/tests/cli/test_commands.py index 31b9bf21f..73c8b65e0 100644 --- a/tests/cli/test_commands.py +++ b/tests/cli/test_commands.py @@ -8,8 +8,8 @@ import pytest -from agentpool.utils.inspection import get_fn_name -from agentpool_config.commands import ( +from wolfharness.utils.inspection import get_fn_name +from wolfharness_config.commands import ( CallableCommandConfig, FileCommandConfig, StaticCommandConfig, diff --git a/tests/cli/test_serve_acp_streamable_http.py b/tests/cli/test_serve_acp_streamable_http.py index cf7c85984..94ce41cc9 100644 --- a/tests/cli/test_serve_acp_streamable_http.py +++ b/tests/cli/test_serve_acp_streamable_http.py @@ -16,7 +16,7 @@ import typer as t from typer.testing import CliRunner -from agentpool_cli.serve_acp import acp_command +from wolfharness_cli.serve_acp import acp_command if TYPE_CHECKING: @@ -64,8 +64,8 @@ def test_default_transport_is_stdio( ) -> None: """Default --transport should create StdioTransport.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -86,8 +86,8 @@ def test_transport_streamable_http_creates_acp_websocket_transport( ) -> None: """--transport streamable-http should create ACPWebSocketTransport.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -107,8 +107,8 @@ def test_transport_streamable_http_with_custom_host_and_port( ) -> None: """--host and --port should be passed to ACPWebSocketTransport.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -140,8 +140,8 @@ def test_transport_streamable_http_uses_default_host_port( ) -> None: """ACPWebSocketTransport should use default host/port when not specified.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -161,8 +161,8 @@ def test_transport_websocket_emits_deprecation_warning( ) -> None: """--transport websocket should emit DeprecationWarning.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -182,8 +182,8 @@ def test_transport_websocket_uses_ws_host_and_ws_port( ) -> None: """--ws-host and --ws-port should be passed to WebSocketTransport.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -221,8 +221,8 @@ def test_short_options_for_host_and_port( ) -> None: """-h and -p should work as short options for --host and --port.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -258,8 +258,8 @@ def test_agent_option_is_passed_through( ) -> None: """--agent option should be passed to ACPServer.from_config.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server @@ -279,8 +279,8 @@ def test_debug_messages_option_is_passed_through( ) -> None: """--debug-messages option should be passed to ACPServer.from_config.""" with ( - patch("agentpool_server.acp_server.ACPServer.from_config") as mock_from_config, - patch("agentpool_cli.serve_acp.asyncio.run"), + patch("wolfharness_server.acp_server.ACPServer.from_config") as mock_from_config, + patch("wolfharness_cli.serve_acp.asyncio.run"), ): mock_server = MagicMock() mock_from_config.return_value = mock_server diff --git a/tests/commands/test_commands_integration.py b/tests/commands/test_commands_integration.py index ef9c08802..16e33d818 100644 --- a/tests/commands/test_commands_integration.py +++ b/tests/commands/test_commands_integration.py @@ -7,9 +7,9 @@ import pytest from slashed import CommandStore -from agentpool import AgentPool, AgentsManifest -from agentpool.messaging.context import NodeContext -from agentpool_commands.prompts import ShowPromptCommand +from wolfharness import AgentPool, AgentsManifest +from wolfharness.messaging.context import NodeContext +from wolfharness_commands.prompts import ShowPromptCommand pytestmark = pytest.mark.unit diff --git a/tests/compat/test_compat.py b/tests/compat/test_compat.py index 5b3ac4d8a..d772ecdf1 100644 --- a/tests/compat/test_compat.py +++ b/tests/compat/test_compat.py @@ -14,8 +14,8 @@ import pytest -from agentpool.hooks.agent_hooks import AgentHooks -from agentpool.utils.context_wrapping import wrap_instruction +from wolfharness.hooks.agent_hooks import AgentHooks +from wolfharness.utils.context_wrapping import wrap_instruction pytestmark = pytest.mark.unit @@ -38,7 +38,7 @@ def test_agent_hooks_still_works() -> None: def test_mcp_manager_still_works() -> None: """MCPManager initializes and accepts server configs.""" - from agentpool.mcp_server.manager import MCPManager + from wolfharness.mcp_server.manager import MCPManager mm = MCPManager() assert mm.name == "mcp" @@ -48,7 +48,7 @@ def test_mcp_manager_still_works() -> None: def test_resolve_history_processors_still_works() -> None: """_resolve_history_processors with _warn=False returns list.""" - from agentpool.agents.native_agent.agent import Agent + from wolfharness.agents.native_agent.agent import Agent agent = Agent.__new__(Agent) agent._resolved_history_processors = None @@ -85,7 +85,7 @@ def simple_instruction() -> str: def test_resolve_history_processors_emits_deprecation_warning() -> None: """_resolve_history_processors emits DeprecationWarning with v0.5.0 and alternative.""" - from agentpool.agents.native_agent.agent import Agent + from wolfharness.agents.native_agent.agent import Agent agent = Agent.__new__(Agent) agent._resolved_history_processors = None diff --git a/tests/compat/test_no_deprecation_warnings.py b/tests/compat/test_no_deprecation_warnings.py index 7816ec640..01c22d2ca 100644 --- a/tests/compat/test_no_deprecation_warnings.py +++ b/tests/compat/test_no_deprecation_warnings.py @@ -11,12 +11,12 @@ import pytest -from agentpool.hooks.agent_hooks import AgentHooks -from agentpool.mcp_server.manager import MCPManager -from agentpool.messaging import ChatMessage -from agentpool.messaging.connection_manager import ConnectionManager -from agentpool.messaging.messagenode import MessageNode -from agentpool.utils.context_wrapping import wrap_instruction +from wolfharness.hooks.agent_hooks import AgentHooks +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.messaging import ChatMessage +from wolfharness.messaging.connection_manager import ConnectionManager +from wolfharness.messaging.messagenode import MessageNode +from wolfharness.utils.context_wrapping import wrap_instruction pytestmark = pytest.mark.unit diff --git a/tests/config/__init__.py b/tests/config/__init__.py index d45ef6812..1872757ba 100644 --- a/tests/config/__init__.py +++ b/tests/config/__init__.py @@ -1 +1 @@ -"""Tests for agentpool_config package.""" +"""Tests for wolfharness_config package.""" diff --git a/tests/config/test_capabilities_yaml.py b/tests/config/test_capabilities_yaml.py index 68b58f2a6..292d14bfc 100644 --- a/tests/config/test_capabilities_yaml.py +++ b/tests/config/test_capabilities_yaml.py @@ -4,8 +4,8 @@ import pytest -from agentpool import AgentsManifest, NativeAgentConfig -from agentpool_config.capabilities import ( +from wolfharness import AgentsManifest, NativeAgentConfig +from wolfharness_config.capabilities import ( EntryPointCapabilityConfig, GenericCapabilityConfig, LoopDetectionCapabilityConfig, diff --git a/tests/config/test_config_resources.py b/tests/config/test_config_resources.py index ff2483e0f..49cac1c45 100644 --- a/tests/config/test_config_resources.py +++ b/tests/config/test_config_resources.py @@ -6,8 +6,8 @@ import pytest -from agentpool import config_resources -from agentpool.models.manifest import AgentsManifest +from wolfharness import config_resources +from wolfharness.models.manifest import AgentsManifest pytestmark = pytest.mark.unit diff --git a/tests/config/test_deferred_wiring.py b/tests/config/test_deferred_wiring.py index 32fe32c2f..f6548fe98 100644 --- a/tests/config/test_deferred_wiring.py +++ b/tests/config/test_deferred_wiring.py @@ -33,7 +33,7 @@ def test_base_tool_config_has_deferred_fields(self): Checks deferred, deferred_kind, deferred_strategy, deferred_placeholder, deferred_timeout fields. """ - from agentpool_config.tools import BaseToolConfig + from wolfharness_config.tools import BaseToolConfig # Check fields exist and have correct defaults fields = BaseToolConfig.model_fields @@ -57,7 +57,7 @@ def test_base_tool_config_has_deferred_fields(self): def test_deferred_fields_have_json_schema_annotations(self): """Deferred fields should have Field titles for YAML auto-completion.""" - from agentpool_config.tools import BaseToolConfig + from wolfharness_config.tools import BaseToolConfig schema = BaseToolConfig.model_json_schema() props = schema["properties"] @@ -74,7 +74,7 @@ class TestDeferredToolWiring: def test_import_tool_default_not_deferred(self): """ImportToolConfig with default settings produces non-deferred tool.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig(import_path=f"{__name__}:tool_conf") tool = config.get_tool() @@ -83,7 +83,7 @@ def test_import_tool_default_not_deferred(self): def test_import_tool_deferred_true(self): """ImportToolConfig with deferred=True produces deferred tool.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -95,7 +95,7 @@ def test_import_tool_deferred_true(self): def test_import_tool_deferred_kind_unapproved(self): """deferred_kind='unapproved' forces strategy='block'.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -110,7 +110,7 @@ def test_import_tool_deferred_kind_unapproved(self): def test_import_tool_deferred_strategy_continue(self): """deferred_strategy='continue' with deferred_kind='external'.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -125,7 +125,7 @@ def test_import_tool_deferred_strategy_continue(self): def test_import_tool_deferred_placeholder_custom(self): """Custom deferred_placeholder is passed through.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -138,7 +138,7 @@ def test_import_tool_deferred_placeholder_custom(self): def test_import_tool_deferred_timeout_string(self): """deferred_timeout as a string '30s' is parsed to timedelta(seconds=30).""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -151,7 +151,7 @@ def test_import_tool_deferred_timeout_string(self): def test_import_tool_deferred_timeout_timedelta(self): """deferred_timeout as timedelta is passed through directly.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -164,7 +164,7 @@ def test_import_tool_deferred_timeout_timedelta(self): def test_import_tool_deferred_timeout_none(self): """deferred_timeout=None means no timeout.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -177,7 +177,7 @@ def test_import_tool_deferred_timeout_none(self): def test_import_tool_deferred_fields_are_all_kwargs_passthrough(self): """All deferred fields should pass through to Tool() via kwargs.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -198,8 +198,8 @@ def test_import_tool_deferred_fields_are_all_kwargs_passthrough(self): def test_invalid_combination_raises_tool_error(self): """deferred_kind='unapproved' with strategy='continue' should raise ToolError.""" - from agentpool.tools.exceptions import ToolError - from agentpool_config.tools import ImportToolConfig + from wolfharness.tools.exceptions import ToolError + from wolfharness_config.tools import ImportToolConfig config = ImportToolConfig( import_path=f"{__name__}:tool_conf", @@ -217,7 +217,7 @@ class TestCheckpointConfigWiring: def test_session_pool_config_has_checkpoint_field(self): """SessionPoolConfig should have a checkpoint field accepting CheckpointConfig.""" - from agentpool_config.session_pool import SessionPoolConfig + from wolfharness_config.session_pool import SessionPoolConfig fields = SessionPoolConfig.model_fields assert "checkpoint" in fields @@ -225,15 +225,15 @@ def test_session_pool_config_has_checkpoint_field(self): def test_session_pool_config_default_checkpoint_is_none(self): """Default SessionPoolConfig should have checkpoint=None.""" - from agentpool_config.session_pool import SessionPoolConfig + from wolfharness_config.session_pool import SessionPoolConfig config = SessionPoolConfig() assert config.checkpoint is None def test_session_pool_config_with_checkpoint_enabled(self): """SessionPoolConfig with checkpoint enabled.""" - from agentpool_config.durable import CheckpointConfig - from agentpool_config.session_pool import SessionPoolConfig + from wolfharness_config.durable import CheckpointConfig + from wolfharness_config.session_pool import SessionPoolConfig config = SessionPoolConfig( checkpoint=CheckpointConfig(enabled=True), @@ -243,8 +243,8 @@ def test_session_pool_config_with_checkpoint_enabled(self): def test_session_pool_config_with_checkpoint_disabled(self): """SessionPoolConfig with checkpoint disabled.""" - from agentpool_config.durable import CheckpointConfig - from agentpool_config.session_pool import SessionPoolConfig + from wolfharness_config.durable import CheckpointConfig + from wolfharness_config.session_pool import SessionPoolConfig config = SessionPoolConfig( checkpoint=CheckpointConfig(enabled=False), @@ -254,7 +254,7 @@ def test_session_pool_config_with_checkpoint_disabled(self): def test_checkpoint_field_json_schema(self): """Checkpoint field should have JSON schema annotations.""" - from agentpool_config.session_pool import SessionPoolConfig + from wolfharness_config.session_pool import SessionPoolConfig schema = SessionPoolConfig.model_json_schema() assert "checkpoint" in schema["properties"] @@ -264,14 +264,14 @@ class TestDeferredToolConfigExists: """Verify DeferredToolConfig is importable.""" def test_deferred_tool_config_importable(self): - """DeferredToolConfig can be imported from agentpool_config.durable.""" - from agentpool_config.durable import DeferredToolConfig + """DeferredToolConfig can be imported from wolfharness_config.durable.""" + from wolfharness_config.durable import DeferredToolConfig assert DeferredToolConfig is not None def test_deferred_tool_config_defaults(self): """DeferredToolConfig has correct defaults.""" - from agentpool_config.durable import DeferredToolConfig + from wolfharness_config.durable import DeferredToolConfig config = DeferredToolConfig() assert config.enabled is True @@ -284,7 +284,7 @@ class TestYamlSchema: def test_import_tool_config_schema_has_deferred_fields(self): """ImportToolConfig JSON schema should include deferred fields.""" - from agentpool_config.tools import ImportToolConfig + from wolfharness_config.tools import ImportToolConfig schema = ImportToolConfig.model_json_schema() props = schema["properties"] @@ -296,7 +296,7 @@ def test_import_tool_config_schema_has_deferred_fields(self): def test_session_pool_config_schema_has_checkpoint_field(self): """SessionPoolConfig JSON schema should include checkpoint field.""" - from agentpool_config.session_pool import SessionPoolConfig + from wolfharness_config.session_pool import SessionPoolConfig schema = SessionPoolConfig.model_json_schema() assert "checkpoint" in schema["properties"] diff --git a/tests/config/test_durable_config.py b/tests/config/test_durable_config.py index 9392ed366..832dd2507 100644 --- a/tests/config/test_durable_config.py +++ b/tests/config/test_durable_config.py @@ -12,7 +12,7 @@ import pytest import yaml -from agentpool_config.durable import CheckpointConfig, DeferredToolConfig +from wolfharness_config.durable import CheckpointConfig, DeferredToolConfig pytestmark = pytest.mark.unit diff --git a/tests/config/test_entrypoint_capabilities.py b/tests/config/test_entrypoint_capabilities.py index 8573a0b44..e97dd1d8c 100644 --- a/tests/config/test_entrypoint_capabilities.py +++ b/tests/config/test_entrypoint_capabilities.py @@ -3,7 +3,7 @@ Tests that ``type: `` in YAML resolves to the correct :class:`EntryPointCapabilityConfig` and that the config's ``build()`` method instantiates the capability class registered via the -``agentpool.capabilities`` entry-point group. +``wolfharness.capabilities`` entry-point group. """ from __future__ import annotations @@ -13,8 +13,8 @@ import pytest -from agentpool import AgentsManifest, NativeAgentConfig -from agentpool_config.capabilities import ( +from wolfharness import AgentsManifest, NativeAgentConfig +from wolfharness_config.capabilities import ( EntryPointCapabilityConfig, GenericCapabilityConfig, build_capability, @@ -49,7 +49,7 @@ def __init__(self, **kwargs: Any) -> None: } # Patch paths -_DISCOVER_PATH = "agentpool.capabilities.registry.discover_entry_point_capabilities" +_DISCOVER_PATH = "wolfharness.capabilities.registry.discover_entry_point_capabilities" _ENTRY_POINTS_PATH = "importlib.metadata.entry_points" diff --git a/tests/config/test_graph_translation.py b/tests/config/test_graph_translation.py index cf415ec26..b8c1bc304 100644 --- a/tests/config/test_graph_translation.py +++ b/tests/config/test_graph_translation.py @@ -4,7 +4,7 @@ import pytest -from agentpool_config import ( +from wolfharness_config import ( GraphConfig, GraphEdgeConfig, GraphStepConfig, @@ -15,12 +15,12 @@ translate_team_to_graph, translate_teams_to_graphs, ) -from agentpool_config.forward_targets import ( +from wolfharness_config.forward_targets import ( CallableConnectionConfig, FileConnectionConfig, NodeConnectionConfig, ) -from agentpool_config.nodes import NodeConfig +from wolfharness_config.nodes import NodeConfig pytestmark = pytest.mark.unit @@ -284,7 +284,7 @@ class TestConfigTranslation: def test_no_teams_no_connections_returns_none(self) -> None: """No teams or connections returns None.""" - from agentpool_config.nodes import NodeConfig + from wolfharness_config.nodes import NodeConfig agents = { "solo": NodeConfig(name="solo"), @@ -294,7 +294,7 @@ def test_no_teams_no_connections_returns_none(self) -> None: def test_with_teams_produces_graph(self) -> None: """Teams produce a graph config.""" - from agentpool_config.nodes import NodeConfig + from wolfharness_config.nodes import NodeConfig agents = { "analyzer": NodeConfig(name="analyzer"), diff --git a/tests/config/test_instructions_config.py b/tests/config/test_instructions_config.py index 6a6a11952..872badd05 100644 --- a/tests/config/test_instructions_config.py +++ b/tests/config/test_instructions_config.py @@ -13,7 +13,7 @@ class TestProviderInstructionConfig: async def test_valid_config_with_ref(self): """Test valid config with ref field only.""" - from agentpool_config.instructions import ProviderInstructionConfig + from wolfharness_config.instructions import ProviderInstructionConfig config = ProviderInstructionConfig( ref="my_provider", @@ -26,7 +26,7 @@ async def test_valid_config_with_ref(self): async def test_valid_config_with_import_path(self): """Test valid config with import_path field only.""" - from agentpool_config.instructions import ProviderInstructionConfig + from wolfharness_config.instructions import ProviderInstructionConfig config = ProviderInstructionConfig( import_path="my.module.Provider", @@ -39,7 +39,7 @@ async def test_valid_config_with_import_path(self): async def test_valid_config_with_import_path_and_kw_args(self): """Test valid config with import_path and kw_args.""" - from agentpool_config.instructions import ProviderInstructionConfig + from wolfharness_config.instructions import ProviderInstructionConfig config = ProviderInstructionConfig( import_path="my.module.Provider", @@ -53,7 +53,7 @@ async def test_valid_config_with_import_path_and_kw_args(self): async def test_invalid_config_both_ref_and_import_path(self): """Test that config with both ref and import_path raises error.""" - from agentpool_config.instructions import ProviderInstructionConfig + from wolfharness_config.instructions import ProviderInstructionConfig with pytest.raises(ValueError, match="Only one of 'ref' or 'import_path' can be provided"): ProviderInstructionConfig( @@ -63,7 +63,7 @@ async def test_invalid_config_both_ref_and_import_path(self): async def test_invalid_config_neither_ref_nor_import_path(self): """Test that config with neither ref nor import_path raises error.""" - from agentpool_config.instructions import ProviderInstructionConfig + from wolfharness_config.instructions import ProviderInstructionConfig with pytest.raises(ValueError, match="Either 'ref' or 'import_path' must be provided"): ProviderInstructionConfig() diff --git a/tests/config/test_mcp_server_config.py b/tests/config/test_mcp_server_config.py index 49a695872..682d64b90 100644 --- a/tests/config/test_mcp_server_config.py +++ b/tests/config/test_mcp_server_config.py @@ -9,7 +9,7 @@ from pydantic import HttpUrl import pytest -from agentpool_config.mcp_server import ( +from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, diff --git a/tests/config/test_path_resolution.py b/tests/config/test_path_resolution.py index da351af35..1a53c716f 100644 --- a/tests/config/test_path_resolution.py +++ b/tests/config/test_path_resolution.py @@ -22,8 +22,8 @@ import pytest from upathtools import UPath -from agentpool_config.context import CONFIG_DIR, ConfigContextManager -from agentpool_config.paths import ( +from wolfharness_config.context import CONFIG_DIR, ConfigContextManager +from wolfharness_config.paths import ( CONFIG_DIR_ENV_VAR, LEGACY_PATHS_ENV_VAR, ConfigPath, diff --git a/tests/config/test_skill_commands.py b/tests/config/test_skill_commands.py index 2401acba4..f360702a4 100644 --- a/tests/config/test_skill_commands.py +++ b/tests/config/test_skill_commands.py @@ -5,7 +5,7 @@ from pydantic import ValidationError import pytest -from agentpool_config.skill_commands import SkillCommandConfig, SkillSlashConfig +from wolfharness_config.skill_commands import SkillCommandConfig, SkillSlashConfig pytestmark = pytest.mark.unit diff --git a/tests/conftest.py b/tests/conftest.py index ddf69326a..c50e9d45a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,10 +11,9 @@ import pytest import yamling -from agentpool import Agent, AgentPool, AgentsManifest, NativeAgentConfig - # Import minimal_pool fixture so it's available to all tests without explicit import. from tests.fixtures.minimal_pool import minimal_pool # noqa: F401 +from wolfharness import Agent, AgentPool, AgentsManifest, NativeAgentConfig if TYPE_CHECKING: @@ -255,7 +254,7 @@ def remap_hardcoded_test_models(): """ from unittest.mock import patch - from agentpool.utils import model_helpers + from wolfharness.utils import model_helpers original = model_helpers.infer_model diff --git a/tests/data/test_skills/hello-world/SKILL.md b/tests/data/test_skills/hello-world/SKILL.md index 950bd1745..b065f7b02 100644 --- a/tests/data/test_skills/hello-world/SKILL.md +++ b/tests/data/test_skills/hello-world/SKILL.md @@ -29,7 +29,7 @@ When invoked, respond with a warm, friendly greeting message. Basic greeting: ```bash -agentpool skill hello-world +wolfharness skill hello-world ``` Expected output: diff --git a/tests/data/test_skills/test-with-args/SKILL.md b/tests/data/test_skills/test-with-args/SKILL.md index 9eef21fdf..88804d36c 100644 --- a/tests/data/test_skills/test-with-args/SKILL.md +++ b/tests/data/test_skills/test-with-args/SKILL.md @@ -35,12 +35,12 @@ When invoked with arguments, process them appropriately: With single argument: ```bash -agentpool skill test-with-args "my test input" +wolfharness skill test-with-args "my test input" ``` With multiple arguments: ```bash -agentpool skill test-with-args arg1 arg2 arg3 +wolfharness skill test-with-args arg1 arg2 arg3 ``` Expected output: diff --git a/tests/delegation/test_base_team_stop.py b/tests/delegation/test_base_team_stop.py index 024805683..ef327d4a3 100644 --- a/tests/delegation/test_base_team_stop.py +++ b/tests/delegation/test_base_team_stop.py @@ -7,7 +7,7 @@ import pytest -from agentpool.delegation.base_team import BaseTeam +from wolfharness.delegation.base_team import BaseTeam pytestmark = pytest.mark.unit diff --git a/tests/delegation/test_break_behavior.py b/tests/delegation/test_break_behavior.py index b9641dcb4..ec7452c7e 100644 --- a/tests/delegation/test_break_behavior.py +++ b/tests/delegation/test_break_behavior.py @@ -50,10 +50,10 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool -from agentpool.agents.events import StreamCompleteEvent, ToolCallStartEvent -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.orchestrator.core import SessionPool +from wolfharness import Agent, AgentPool +from wolfharness.agents.events import StreamCompleteEvent, ToolCallStartEvent +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.orchestrator.core import SessionPool pytestmark = pytest.mark.unit diff --git a/tests/delegation/test_cross_provider_session_lifecycle.py b/tests/delegation/test_cross_provider_session_lifecycle.py index c7cc0ecc9..049d71f41 100644 --- a/tests/delegation/test_cross_provider_session_lifecycle.py +++ b/tests/delegation/test_cross_provider_session_lifecycle.py @@ -25,16 +25,16 @@ import pytest -from agentpool import Agent, AgentPool, AgentsManifest, BaseTeam -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events import ( +from wolfharness import Agent, AgentPool, AgentsManifest, BaseTeam +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events import ( RunStartedEvent, SpawnSessionStart, SubAgentEvent, ) -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError -from agentpool.sessions import SessionData -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness.sessions import SessionData +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider pytestmark = pytest.mark.integration @@ -53,7 +53,7 @@ def _make_echo_agent(name: str, response: str = "hello") -> Agent[Any, str]: """Create an Agent that echoes a fixed response via function_to_model.""" from functools import partial - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def _echo(_msg: str, *, _response: str = response) -> str: return _response @@ -343,10 +343,10 @@ async def test_depth_increments_per_delegation_level() -> None: async def test_acp_child_session_inherits_parent_project_and_cwd() -> None: """TG-10: ACP child session created via ACPSessionManager with parent_session_id....""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest - from agentpool.orchestrator.core import SessionPool - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness.orchestrator.core import SessionPool + from wolfharness_server.acp_server.session_manager import ACPSessionManager manifest = AgentsManifest(agents={"acp_agent": NativeAgentConfig(model="test")}) pool = AgentPool(manifest) @@ -363,7 +363,7 @@ def simple_callback(message: str) -> str: # Create parent session with known project_id and cwd parent_cwd = tempfile.gettempdir() - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id parent_project_id = compute_project_id(parent_cwd) parent_data = SessionData( @@ -379,8 +379,8 @@ def simple_callback(message: str) -> str: mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session_instance = MagicMock() mock_session_instance.register_update_callback = MagicMock() @@ -412,7 +412,7 @@ def simple_callback(message: str) -> str: async def test_subagent_depth_guard_before_session_creation() -> None: """TG-14: When depth >= MAX_DELEGATION_DEPTH, DelegationDepthError is raised BEFORE....""" - from agentpool_toolsets.builtin.subagent_tools import SubagentTools + from wolfharness_toolsets.builtin.subagent_tools import SubagentTools manifest = AgentsManifest.from_yaml(""" agents: @@ -828,7 +828,7 @@ async def test_child_session_ids_unique_across_providers() -> None: async with AgentPool(manifest) as pool: # Add team to manifest so subagent tool can find it - from agentpool_config.teams import TeamConfig + from wolfharness_config.teams import TeamConfig pool.manifest.teams["work_team"] = TeamConfig(mode="parallel", members=["alpha", "beta"]) inner_team.agent_pool = pool diff --git a/tests/delegation/test_graph_adapter.py b/tests/delegation/test_graph_adapter.py index c5ad5de07..621faba66 100644 --- a/tests/delegation/test_graph_adapter.py +++ b/tests/delegation/test_graph_adapter.py @@ -7,9 +7,9 @@ import pytest -from agentpool.messaging import ChatMessage -from agentpool.messaging.graph_adapter import AgentPoolState, MessageNodeStep -from agentpool.messaging.messagenode import MessageNode +from wolfharness.messaging import ChatMessage +from wolfharness.messaging.graph_adapter import AgentPoolState, MessageNodeStep +from wolfharness.messaging.messagenode import MessageNode pytestmark = pytest.mark.unit @@ -39,7 +39,7 @@ class GraphMessageNodeWithEvents(MessageNode[Any, str]): async def _execute_node(self, *prompts: Any, **kwargs: Any) -> ChatMessage[str]: from pydantic_ai import TextPartDelta - from agentpool.agents.events import PartDeltaEvent + from wolfharness.agents.events import PartDeltaEvent state = kwargs.get("_state") if state is not None: @@ -99,7 +99,7 @@ async def test_message_node_run_stream_uses_graph_iter(): events = [event async for event in node.run_stream("stream_test")] assert len(events) == 1 - from agentpool.agents.events import StreamCompleteEvent + from wolfharness.agents.events import StreamCompleteEvent assert isinstance(events[0], StreamCompleteEvent) assert events[0].message.content == "stream_test" @@ -111,7 +111,7 @@ async def test_message_node_run_stream_drains_event_queue(): node = GraphMessageNodeWithEvents(name="test_events") events = [event async for event in node.run_stream("event_test")] - from agentpool.agents.events import PartDeltaEvent, StreamCompleteEvent + from wolfharness.agents.events import PartDeltaEvent, StreamCompleteEvent assert len(events) == 2 assert isinstance(events[0], PartDeltaEvent) diff --git a/tests/delegation/test_graph_edges.py b/tests/delegation/test_graph_edges.py index 4d9492189..78d712017 100644 --- a/tests/delegation/test_graph_edges.py +++ b/tests/delegation/test_graph_edges.py @@ -18,10 +18,10 @@ # Add src to path for imports sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) -from agentpool.messaging import ChatMessage, MessageNode -from agentpool.talk import Talk -from agentpool.talk.graph_edges import TalkEdgeTranslator -from agentpool.utils.time_utils import get_now +from wolfharness.messaging import ChatMessage, MessageNode +from wolfharness.talk import Talk +from wolfharness.talk.graph_edges import TalkEdgeTranslator +from wolfharness.utils.time_utils import get_now pytestmark = pytest.mark.unit @@ -54,7 +54,7 @@ async def run_message(self, message: ChatMessage[Any], **kwargs: Any) -> ChatMes return message async def get_stats(self) -> Any: - from agentpool.talk.stats import MessageStats + from wolfharness.talk.stats import MessageStats return MessageStats() diff --git a/tests/delegation/test_graph_teams.py b/tests/delegation/test_graph_teams.py index 204488634..d5edb78a9 100644 --- a/tests/delegation/test_graph_teams.py +++ b/tests/delegation/test_graph_teams.py @@ -13,24 +13,24 @@ import anyio import pytest -from agentpool import Agent -from agentpool.agents.base_agent import _current_run_ctx_var -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness import Agent +from wolfharness.agents.base_agent import _current_run_ctx_var +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( SpawnSessionStart, StreamCompleteEvent, SubAgentEvent, ) -from agentpool.delegation.base_team import BaseTeam -from agentpool.delegation.graph_team import ( +from wolfharness.delegation.base_team import BaseTeam +from wolfharness.delegation.graph_team import ( _MemberOutput, _TeamGraphState, build_team_graph, run_team_graph, ) -from agentpool.messaging import AgentResponse, ChatMessage, TeamResponse -from agentpool.messaging.messagenode import MessageNode -from agentpool.talk import Talk +from wolfharness.messaging import AgentResponse, ChatMessage, TeamResponse +from wolfharness.messaging.messagenode import MessageNode +from wolfharness.talk import Talk pytestmark = pytest.mark.unit @@ -158,7 +158,7 @@ class _FakeAgentPool: def __init__(self, agents: list[Agent[Any, str]]) -> None: from types import SimpleNamespace - from agentpool.mcp_server.manager import MCPManager + from wolfharness.mcp_server.manager import MCPManager self.session_pool = _FakeSessionPool() self.all_agents = {agent.name: agent for agent in agents} diff --git a/tests/delegation/test_pool_get_context.py b/tests/delegation/test_pool_get_context.py index feaecee83..f6a8a855d 100644 --- a/tests/delegation/test_pool_get_context.py +++ b/tests/delegation/test_pool_get_context.py @@ -4,9 +4,9 @@ import pytest -from agentpool.delegation.pool import AgentPool -from agentpool.host.context import HostContext -from agentpool.host.factory import AgentFactory +from wolfharness.delegation.pool import AgentPool +from wolfharness.host.context import HostContext +from wolfharness.host.factory import AgentFactory @pytest.fixture diff --git a/tests/delegation/test_pool_runs.py b/tests/delegation/test_pool_runs.py index eb7827c30..47ffa3205 100644 --- a/tests/delegation/test_pool_runs.py +++ b/tests/delegation/test_pool_runs.py @@ -8,8 +8,8 @@ import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.orchestrator import RunHandle +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.orchestrator import RunHandle @pytest.fixture diff --git a/tests/delegation/test_pool_session_integration.py b/tests/delegation/test_pool_session_integration.py index f76cdbc86..dc13c58f3 100644 --- a/tests/delegation/test_pool_session_integration.py +++ b/tests/delegation/test_pool_session_integration.py @@ -8,9 +8,9 @@ import pytest -from agentpool import Agent, AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.orchestrator import SessionPool -from agentpool_config.session_pool import SessionPoolConfig +from wolfharness import Agent, AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.orchestrator import SessionPool +from wolfharness_config.session_pool import SessionPoolConfig @pytest.fixture diff --git a/tests/delegation/test_pool_skills.py b/tests/delegation/test_pool_skills.py index 126982bc6..d03c70161 100644 --- a/tests/delegation/test_pool_skills.py +++ b/tests/delegation/test_pool_skills.py @@ -11,11 +11,11 @@ import pytest from upathtools import UPath -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.capabilities.resource_protocols import SkillResource -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.skills.uri_resolver import SkillURIResolver -from agentpool_config.skills import SkillsConfig +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.capabilities.resource_protocols import SkillResource +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.skills.uri_resolver import SkillURIResolver +from wolfharness_config.skills import SkillsConfig pytestmark = pytest.mark.integration diff --git a/tests/delegation/test_team_member_skills.py b/tests/delegation/test_team_member_skills.py index 2216d8eda..a4a48933d 100644 --- a/tests/delegation/test_team_member_skills.py +++ b/tests/delegation/test_team_member_skills.py @@ -4,8 +4,8 @@ import pytest -from agentpool import BaseTeam -from agentpool.skills.exceptions import SkillNotFoundError +from wolfharness import BaseTeam +from wolfharness.skills.exceptions import SkillNotFoundError pytestmark = pytest.mark.unit diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 2e6f4d302..bcc9000f4 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -1,7 +1,7 @@ """L4 subprocess E2E test fixtures for AgentPool protocol servers. This conftest provides the ``subprocess_server`` fixture that spawns a real -``agentpool serve-*`` process, waits for it to become healthy, and tears it +``wolfharness serve-*`` process, waits for it to become healthy, and tears it down gracefully on test exit. Design decisions (see ``openspec/changes/layered-testing-infrastructure/design.md``): @@ -45,12 +45,12 @@ # --------------------------------------------------------------------------- -def _agentpool_binary_available() -> bool: - """Check whether the ``agentpool`` CLI binary is on PATH.""" - return shutil.which("agentpool") is not None +def _wolfharness_binary_available() -> bool: + """Check whether the ``wolfharness`` CLI binary is on PATH.""" + return shutil.which("wolfharness") is not None -SKIP_NO_BINARY = not _agentpool_binary_available() +SKIP_NO_BINARY = not _wolfharness_binary_available() SKIP_WINDOWS = sys.platform == "win32" @@ -61,7 +61,7 @@ def _agentpool_binary_available() -> bool: @dataclass class SubprocessServer: - """Handle to a spawned ``agentpool serve-*`` subprocess. + """Handle to a spawned ``wolfharness serve-*`` subprocess. Attributes: process: The asyncio subprocess. @@ -136,7 +136,7 @@ def _build_command( port: Bind port for HTTP servers. extra_args: Additional CLI arguments. """ - cmd: list[str] = ["agentpool", serve_command, str(config_path)] + cmd: list[str] = ["wolfharness", serve_command, str(config_path)] if serve_command == "serve-acp": # ACP defaults to stdio; use streamable-http for HTTP-based e2e. # Caller must pass --transport via extra_args if HTTP is desired. @@ -522,7 +522,7 @@ async def subprocess_server( # noqa: PLR0915 session_e2e_config: Path, allow_model_requests: Any, ) -> AsyncIterator[SubprocessServer]: - """Spawn an ``agentpool serve-*`` subprocess, reusing cached servers when possible. + """Spawn an ``wolfharness serve-*`` subprocess, reusing cached servers when possible. Cache bypass conditions: - ``--no-server-cache`` flag is set @@ -742,7 +742,7 @@ async def _spawn_server( health_path: str = "/", extra_args: list[str] | None = None, ) -> AsyncIterator[SubprocessServer]: - """Spawn an agentpool server subprocess (internal helper for custom fixtures). + """Spawn an wolfharness server subprocess (internal helper for custom fixtures). Args: serve_command: The CLI subcommand (e.g. serve-api). @@ -948,7 +948,7 @@ async def subprocess_server_with_mcp( session_e2e_config_with_mcp: Path, allow_model_requests: Any, ) -> AsyncIterator[SubprocessServer]: - """Spawn an ``agentpool serve-*`` subprocess with the fake MCP server config. + """Spawn an ``wolfharness serve-*`` subprocess with the fake MCP server config. Uses ``_spawn_server`` (non-cached) because the MCP-config variant is unique to MCP status tests and not worth caching. The fake MCP server @@ -987,7 +987,7 @@ async def subprocess_server_with_mcp( @dataclass class ACPWSServerHandle: - """Handle to a spawned ``agentpool serve-acp --transport streamable-http`` server. + """Handle to a spawned ``wolfharness serve-acp --transport streamable-http`` server. Attributes: process: The asyncio subprocess. @@ -1021,7 +1021,7 @@ async def _spawn_acp_ws_server( agent: str = "test_agent", health_timeout: float = 15.0, ) -> AsyncIterator[ACPWSServerHandle]: - """Spawn ``agentpool serve-acp --transport streamable-http`` and wait for health. + """Spawn ``wolfharness serve-acp --transport streamable-http`` and wait for health. Args: config_path: Path to the YAML config file. @@ -1035,7 +1035,7 @@ async def _spawn_acp_ws_server( """ port = allocate_ephemeral_port() cmd: list[str] = [ - "agentpool", + "wolfharness", "serve-acp", str(config_path), "--agent", @@ -1159,6 +1159,6 @@ def verify_no_orphaned_processes( import warnings warnings.warn( - f"{len(orphans)} orphaned agentpool subprocess(es) found and killed at session end.", + f"{len(orphans)} orphaned wolfharness subprocess(es) found and killed at session end.", stacklevel=2, ) diff --git a/tests/e2e/test_acp_concurrent_prompts.py b/tests/e2e/test_acp_concurrent_prompts.py index 33452e4da..f33dd311b 100644 --- a/tests/e2e/test_acp_concurrent_prompts.py +++ b/tests/e2e/test_acp_concurrent_prompts.py @@ -36,7 +36,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -83,7 +83,7 @@ async def _spawn_acp_server( *, agent: str = "test_agent", ) -> AsyncIterator[_ACPServerHandle]: - """Spawn ``agentpool serve-acp`` and return a handle with client connection.""" + """Spawn ``wolfharness serve-acp`` and return a handle with client connection.""" import os env = os.environ.copy() @@ -93,7 +93,7 @@ async def _spawn_acp_server( client = DefaultACPClient(allow_file_operations=False) async with spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(config_path), "--agent", @@ -115,7 +115,7 @@ async def test_steer_injects_into_active_turn(e2e_config: Path) -> None: ``"asap"`` (steer), verify the server handles it without crashing. The ACP protocol's ``PromptRequest`` does not have a native ``priority`` - field. The agentpool SessionController supports ``priority="asap"`` for + field. The wolfharness SessionController supports ``priority="asap"`` for mid-turn injection (steer), but this is an internal API not exposed via ACP JSON-RPC. This test sends two concurrent prompts to the same session and verifies the server handles them gracefully — the first completes @@ -160,7 +160,7 @@ async def test_queue_waits_for_idle(e2e_config: Path) -> None: ``"when_idle"`` (queue), verify the server handles it without crashing. Similar to B7.1, the ACP protocol does not expose ``priority`` natively. - The agentpool SessionController supports ``priority="when_idle"`` for + The wolfharness SessionController supports ``priority="when_idle"`` for queueing, but this is internal. This test verifies the server handles sequential/concurrent prompts gracefully. """ diff --git a/tests/e2e/test_acp_config.py b/tests/e2e/test_acp_config.py index ec0c68ef7..ad5e513ec 100644 --- a/tests/e2e/test_acp_config.py +++ b/tests/e2e/test_acp_config.py @@ -38,7 +38,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -79,7 +79,7 @@ async def acp_server(e2e_config: Path) -> AsyncIterator[ACPConfigHandle]: client = DefaultACPClient(allow_file_operations=False) cm = spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(e2e_config), "--agent", diff --git a/tests/e2e/test_acp_elicitation.py b/tests/e2e/test_acp_elicitation.py index ce1c689a3..5585b2c90 100644 --- a/tests/e2e/test_acp_elicitation.py +++ b/tests/e2e/test_acp_elicitation.py @@ -21,7 +21,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] diff --git a/tests/e2e/test_acp_error_paths.py b/tests/e2e/test_acp_error_paths.py index 14967d0d7..aa9293a48 100644 --- a/tests/e2e/test_acp_error_paths.py +++ b/tests/e2e/test_acp_error_paths.py @@ -42,7 +42,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -89,7 +89,7 @@ async def _spawn_acp_server( *, agent: str = "test_agent", ) -> AsyncIterator[_ACPServerHandle]: - """Spawn ``agentpool serve-acp`` and return a handle with client connection.""" + """Spawn ``wolfharness serve-acp`` and return a handle with client connection.""" import os env = os.environ.copy() @@ -99,7 +99,7 @@ async def _spawn_acp_server( client = DefaultACPClient(allow_file_operations=False) async with spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(config_path), "--agent", diff --git a/tests/e2e/test_acp_mcp.py b/tests/e2e/test_acp_mcp.py index 49b0570aa..03c4b5a58 100644 --- a/tests/e2e/test_acp_mcp.py +++ b/tests/e2e/test_acp_mcp.py @@ -33,7 +33,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -89,7 +89,7 @@ async def test_mcp_message_agent_side(e2e_config: Path) -> None: client = DefaultACPClient(allow_file_operations=False) async with spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(e2e_config), "--agent", diff --git a/tests/e2e/test_acp_permission_flow.py b/tests/e2e/test_acp_permission_flow.py index 894fb2283..073ea0738 100644 --- a/tests/e2e/test_acp_permission_flow.py +++ b/tests/e2e/test_acp_permission_flow.py @@ -23,7 +23,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] diff --git a/tests/e2e/test_acp_providers.py b/tests/e2e/test_acp_providers.py index bade71b07..2bf39db3f 100644 --- a/tests/e2e/test_acp_providers.py +++ b/tests/e2e/test_acp_providers.py @@ -36,7 +36,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -74,7 +74,7 @@ async def acp_server(e2e_config: Path) -> AsyncIterator[ACPProvidersHandle]: client = DefaultACPClient(allow_file_operations=False) cm = spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(e2e_config), "--agent", diff --git a/tests/e2e/test_acp_session_lifecycle.py b/tests/e2e/test_acp_session_lifecycle.py index 0c9c9c7fd..9f34c8895 100644 --- a/tests/e2e/test_acp_session_lifecycle.py +++ b/tests/e2e/test_acp_session_lifecycle.py @@ -50,7 +50,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -110,7 +110,7 @@ async def acp_server( client = DefaultACPClient(allow_file_operations=False) cm = spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(e2e_config), "--agent", diff --git a/tests/e2e/test_acp_session_updates.py b/tests/e2e/test_acp_session_updates.py index 7b348c274..deac6919a 100644 --- a/tests/e2e/test_acp_session_updates.py +++ b/tests/e2e/test_acp_session_updates.py @@ -11,7 +11,7 @@ during a basic prompt turn with TestModel. Skip tests (B9.4-B9.12): SessionUpdate variants not yet implemented in -the agentpool ACP server. Each skip test documents the test intent so +the wolfharness ACP server. Each skip test documents the test intent so the expected behavior is clear once the feature is implemented. All tests use ``model: test`` (pydantic-ai TestModel) so NO API key is needed. @@ -51,7 +51,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -99,7 +99,7 @@ async def _spawn_acp_server( *, agent: str = "test_agent", ) -> AsyncIterator[ACPServerHandle]: - """Spawn ``agentpool serve-acp`` and return a handle with client connection. + """Spawn ``wolfharness serve-acp`` and return a handle with client connection. Args: config_path: Path to the YAML config file. @@ -114,7 +114,7 @@ async def _spawn_acp_server( client = DefaultACPClient(allow_file_operations=False) async with spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(config_path), "--agent", @@ -172,7 +172,7 @@ async def test_user_message_chunk_notification(e2e_config: Path) -> None: ``session/update`` notification with ``update.session_update="user_message_chunk"`` is emitted when the user's message is processed by the server. - Note: The agentpool ACP server does not currently emit ``user_message_chunk`` + Note: The wolfharness ACP server does not currently emit ``user_message_chunk`` notifications (the event_converter only emits ``agent_message_chunk`` for agent output). This test documents the expected behavior per the ACP spec. See issue #188 for the server-side gap. diff --git a/tests/e2e/test_acp_stop_reasons.py b/tests/e2e/test_acp_stop_reasons.py index 86d9a4ac8..cb6640e85 100644 --- a/tests/e2e/test_acp_stop_reasons.py +++ b/tests/e2e/test_acp_stop_reasons.py @@ -49,7 +49,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -97,7 +97,7 @@ async def _spawn_acp_server( *, agent: str = "test_agent", ) -> AsyncIterator[ACPServerHandle]: - """Spawn ``agentpool serve-acp`` and return a handle with client connection. + """Spawn ``wolfharness serve-acp`` and return a handle with client connection. Args: config_path: Path to the YAML config file. @@ -112,7 +112,7 @@ async def _spawn_acp_server( client = DefaultACPClient(allow_file_operations=False) async with spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(config_path), "--agent", diff --git a/tests/e2e/test_acp_subprocess.py b/tests/e2e/test_acp_subprocess.py index 09cc1daae..0f3395656 100644 --- a/tests/e2e/test_acp_subprocess.py +++ b/tests/e2e/test_acp_subprocess.py @@ -1,4 +1,4 @@ -"""L4 subprocess E2E tests for the ACP server (``agentpool serve-acp``). +"""L4 subprocess E2E tests for the ACP server (``wolfharness serve-acp``). L4a smoke tests (``@pytest.mark.e2e``, NOT slow): - test_server_startup: spawn serve-acp, verify process alive, initialize JSON-RPC @@ -51,7 +51,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -99,7 +99,7 @@ async def _spawn_acp_server( *, agent: str = "test_agent", ) -> AsyncIterator[ACPServerHandle]: - """Spawn ``agentpool serve-acp`` and return a handle with client connection. + """Spawn ``wolfharness serve-acp`` and return a handle with client connection. Args: config_path: Path to the YAML config file. @@ -114,7 +114,7 @@ async def _spawn_acp_server( client = DefaultACPClient(allow_file_operations=False) async with spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(config_path), "--agent", diff --git a/tests/e2e/test_acp_v2_alignment.py b/tests/e2e/test_acp_v2_alignment.py index ea7d4d585..9b6763a20 100644 --- a/tests/e2e/test_acp_v2_alignment.py +++ b/tests/e2e/test_acp_v2_alignment.py @@ -32,7 +32,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] diff --git a/tests/e2e/test_acp_ws_transport.py b/tests/e2e/test_acp_ws_transport.py index 2a6361055..0b976d2c4 100644 --- a/tests/e2e/test_acp_ws_transport.py +++ b/tests/e2e/test_acp_ws_transport.py @@ -41,7 +41,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] diff --git a/tests/e2e/test_agui_subprocess.py b/tests/e2e/test_agui_subprocess.py index f4c0a1950..6f9334f35 100644 --- a/tests/e2e/test_agui_subprocess.py +++ b/tests/e2e/test_agui_subprocess.py @@ -1,4 +1,4 @@ -"""L4 subprocess E2E tests for the AG-UI server (``agentpool serve-agui``). +"""L4 subprocess E2E tests for the AG-UI server (``wolfharness serve-agui``). L4a smoke tests (``@pytest.mark.e2e``, NOT slow): - test_server_startup: spawn serve-agui, verify HTTP health @@ -28,7 +28,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_openai_api_subprocess.py b/tests/e2e/test_openai_api_subprocess.py index b7024532f..b834d20b8 100644 --- a/tests/e2e/test_openai_api_subprocess.py +++ b/tests/e2e/test_openai_api_subprocess.py @@ -1,4 +1,4 @@ -"""L4 subprocess E2E tests for the OpenAI-compatible API server (agentpool serve-api). +"""L4 subprocess E2E tests for the OpenAI-compatible API server (wolfharness serve-api). L4a smoke tests (@pytest.mark.e2e, NOT slow): - test_server_startup: spawn serve-api, verify HTTP health @@ -32,7 +32,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_agent_skill_command.py b/tests/e2e/test_opencode_agent_skill_command.py index 063c32fee..92211cbfd 100644 --- a/tests/e2e/test_opencode_agent_skill_command.py +++ b/tests/e2e/test_opencode_agent_skill_command.py @@ -27,7 +27,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_error_paths.py b/tests/e2e/test_opencode_error_paths.py index 939c991e8..6838872f4 100644 --- a/tests/e2e/test_opencode_error_paths.py +++ b/tests/e2e/test_opencode_error_paths.py @@ -27,7 +27,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_lock_concurrency.py b/tests/e2e/test_opencode_lock_concurrency.py index a770e0dea..fa27ec09f 100644 --- a/tests/e2e/test_opencode_lock_concurrency.py +++ b/tests/e2e/test_opencode_lock_concurrency.py @@ -35,7 +35,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_mcp.py b/tests/e2e/test_opencode_mcp.py index 41111a5d5..f3ec7775c 100644 --- a/tests/e2e/test_opencode_mcp.py +++ b/tests/e2e/test_opencode_mcp.py @@ -31,7 +31,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_message_ops.py b/tests/e2e/test_opencode_message_ops.py index 00601bac3..da774b357 100644 --- a/tests/e2e/test_opencode_message_ops.py +++ b/tests/e2e/test_opencode_message_ops.py @@ -33,7 +33,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_misc.py b/tests/e2e/test_opencode_misc.py index 3bf810a02..ef20151e8 100644 --- a/tests/e2e/test_opencode_misc.py +++ b/tests/e2e/test_opencode_misc.py @@ -26,7 +26,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_multiturn_redflag.py b/tests/e2e/test_opencode_multiturn_redflag.py index 7c1450b3f..95822a3af 100644 --- a/tests/e2e/test_opencode_multiturn_redflag.py +++ b/tests/e2e/test_opencode_multiturn_redflag.py @@ -34,7 +34,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] @@ -217,7 +217,7 @@ def _extract_text_part_text(msg: dict[str, Any]) -> str: def _id_timestamp_ms(msg_id: str) -> int | None: - """Extract millisecond timestamp from an agentpool ascending ID. + """Extract millisecond timestamp from an wolfharness ascending ID. AgentPool ID format: ``{prefix}_{16 hex chars}{14 base62 chars}`` where the 16 hex chars encode 8 bytes (64 bits) of diff --git a/tests/e2e/test_opencode_permission_question.py b/tests/e2e/test_opencode_permission_question.py index 4b3cbf1ee..b3985f4df 100644 --- a/tests/e2e/test_opencode_permission_question.py +++ b/tests/e2e/test_opencode_permission_question.py @@ -32,7 +32,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_prompt_delivery.py b/tests/e2e/test_opencode_prompt_delivery.py index f30204aef..b414fd0f7 100644 --- a/tests/e2e/test_opencode_prompt_delivery.py +++ b/tests/e2e/test_opencode_prompt_delivery.py @@ -30,7 +30,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_provider_mode_config.py b/tests/e2e/test_opencode_provider_mode_config.py index 3385ab40a..6134a369e 100644 --- a/tests/e2e/test_opencode_provider_mode_config.py +++ b/tests/e2e/test_opencode_provider_mode_config.py @@ -31,7 +31,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_session_lifecycle.py b/tests/e2e/test_opencode_session_lifecycle.py index c34129a28..b98b11e21 100644 --- a/tests/e2e/test_opencode_session_lifecycle.py +++ b/tests/e2e/test_opencode_session_lifecycle.py @@ -37,7 +37,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_sse_streaming.py b/tests/e2e/test_opencode_sse_streaming.py index 18287b8f2..605be5238 100644 --- a/tests/e2e/test_opencode_sse_streaming.py +++ b/tests/e2e/test_opencode_sse_streaming.py @@ -33,7 +33,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_opencode_subprocess.py b/tests/e2e/test_opencode_subprocess.py index 84b866fd1..e3c9abc4c 100644 --- a/tests/e2e/test_opencode_subprocess.py +++ b/tests/e2e/test_opencode_subprocess.py @@ -1,4 +1,4 @@ -"""L4 subprocess E2E tests for the OpenCode server (``agentpool serve-opencode``). +"""L4 subprocess E2E tests for the OpenCode server (``wolfharness serve-opencode``). L4a smoke tests (``@pytest.mark.e2e``, NOT slow): - test_server_startup: spawn serve-opencode, verify HTTP health @@ -33,7 +33,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_team_mode_smoke.py b/tests/e2e/test_team_mode_smoke.py index 1b48779ca..a6b8d013c 100644 --- a/tests/e2e/test_team_mode_smoke.py +++ b/tests/e2e/test_team_mode_smoke.py @@ -1,6 +1,6 @@ -"""L4a smoke E2E test for dynamic team mode via ``agentpool serve-opencode``. +"""L4a smoke E2E test for dynamic team mode via ``wolfharness serve-opencode``. -Spawns a real ``agentpool serve-opencode`` subprocess with a YAML config +Spawns a real ``wolfharness serve-opencode`` subprocess with a YAML config that enables ``team_mode`` and uses ``model: test`` (pydantic-ai TestModel — no API key needed). Verifies the server starts, accepts connections, and emits the SSE handshake event. @@ -40,7 +40,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] @@ -135,7 +135,7 @@ async def test_team_mode_server_starts( Given: a YAML config with ``team_mode`` enabled and ``model: test``. - When: ``agentpool serve-opencode`` is spawned as a subprocess. + When: ``wolfharness serve-opencode`` is spawned as a subprocess. Then: - The server starts and responds to HTTP health checks. diff --git a/tests/e2e/test_turn_split_e2e.py b/tests/e2e/test_turn_split_e2e.py index 052d164c0..5fcc74995 100644 --- a/tests/e2e/test_turn_split_e2e.py +++ b/tests/e2e/test_turn_split_e2e.py @@ -12,7 +12,7 @@ - Messages REST endpoint (``GET /session/{id}/message``) Strategy: - 1. Start a real ``agentpool serve-opencode`` subprocess with a TestModel + 1. Start a real ``wolfharness serve-opencode`` subprocess with a TestModel configured to call the ``bash`` tool (``sleep 2``) on the first request. This gives a 2-second window during tool execution for the steer to arrive mid-turn. @@ -49,7 +49,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/e2e/test_user_message_inserted_e2e.py b/tests/e2e/test_user_message_inserted_e2e.py index 20f6bd46b..b58f416a2 100644 --- a/tests/e2e/test_user_message_inserted_e2e.py +++ b/tests/e2e/test_user_message_inserted_e2e.py @@ -34,7 +34,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows stdio subprocess issues"), ] @@ -45,7 +45,7 @@ async def _spawn_acp_server( *, agent: str = "test_agent", ) -> AsyncIterator[tuple[ClientSideConnection, Any, DefaultACPClient]]: - """Spawn ``agentpool serve-acp`` and return (conn, process, client). + """Spawn ``wolfharness serve-acp`` and return (conn, process, client). Args: config_path: Path to the YAML config file. @@ -60,7 +60,7 @@ async def _spawn_acp_server( client = DefaultACPClient(allow_file_operations=False) async with spawn_agent_process( lambda _conn: client, - "agentpool", + "wolfharness", "serve-acp", str(config_path), "--agent", @@ -76,7 +76,7 @@ async def test_acp_server_steer_user_message_chunk_position( ) -> None: """L4a: Verify ``UserMessageChunk`` appears for a steer-marked prompt. - Spawns a real ``agentpool serve-acp`` subprocess, connects an ACP client, + Spawns a real ``wolfharness serve-acp`` subprocess, connects an ACP client, sends an initial prompt, then sends a second prompt with ``_meta.delivery="steer"``. The ACP server's ``_emit_user_message_chunks`` emits ``UserMessageChunk`` session updates for the user's input before diff --git a/tests/e2e/test_user_message_no_duplicate_e2e.py b/tests/e2e/test_user_message_no_duplicate_e2e.py index 31e5dfb01..952569a9b 100644 --- a/tests/e2e/test_user_message_no_duplicate_e2e.py +++ b/tests/e2e/test_user_message_no_duplicate_e2e.py @@ -8,7 +8,7 @@ - Messages REST endpoint (``GET /session/{id}/message``) Strategy: - 1. Start a real ``agentpool serve-opencode`` subprocess with a TestModel. + 1. Start a real ``wolfharness serve-opencode`` subprocess with a TestModel. 2. Open the SSE event stream (``GET /event``). 3. Send ONE prompt via ``POST /prompt_async``. 4. Collect SSE events until the session goes idle. @@ -39,7 +39,7 @@ pytestmark = [ pytest.mark.e2e, - pytest.mark.skipif(SKIP_NO_BINARY, reason="agentpool binary not on PATH"), + pytest.mark.skipif(SKIP_NO_BINARY, reason="wolfharness binary not on PATH"), pytest.mark.skipif(SKIP_WINDOWS, reason="Windows subprocess issues"), ] diff --git a/tests/elicitation/test_e2e_crash_recovery.py b/tests/elicitation/test_e2e_crash_recovery.py index 90c01ced3..ccda15516 100644 --- a/tests/elicitation/test_e2e_crash_recovery.py +++ b/tests/elicitation/test_e2e_crash_recovery.py @@ -25,25 +25,25 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.native_agent.checkpoint import CheckpointManager -from agentpool.orchestrator.core import EventBus -from agentpool.orchestrator.session_pool import SessionPool -from agentpool.sessions.models import ( +from wolfharness import Agent +from wolfharness.agents.native_agent.checkpoint import CheckpointManager +from wolfharness.orchestrator.core import EventBus +from wolfharness.orchestrator.session_pool import SessionPool +from wolfharness.sessions.models import ( ElicitationResumePayload, PendingDeferredCall, SessionData, ) -from agentpool.storage.manager import StorageManager -from agentpool.ui.base import InputProvider -from agentpool_config.storage import SQLStorageConfig, StorageConfig -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.storage.manager import StorageManager +from wolfharness.ui.base import InputProvider +from wolfharness_config.storage import SQLStorageConfig, StorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider if TYPE_CHECKING: from pathlib import Path - from agentpool.agents.context import AgentContext, ConfirmationResult + from wolfharness.agents.context import AgentContext, ConfirmationResult # --------------------------------------------------------------------------- diff --git a/tests/elicitation/test_integration_elicitation.py b/tests/elicitation/test_integration_elicitation.py index 95cc107e4..e9d142f38 100644 --- a/tests/elicitation/test_integration_elicitation.py +++ b/tests/elicitation/test_integration_elicitation.py @@ -5,7 +5,7 @@ paths, ElicitationFutureRegistry lifecycle, ACP/OpenCode event conversion, and ACP capability-gated elicitation behavior. -Refs: https://github.com/Leoyzen/agentpool/issues/107 +Refs: https://github.com/Leoyzen/wolfharness/issues/107 """ from __future__ import annotations @@ -18,18 +18,18 @@ from pydantic_ai.tools import DeferredToolRequests, DeferredToolResults, RunContext import pytest -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events.events import ElicitationDeferredEvent -from agentpool.agents.native_agent.elicitation_bridge import ( +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events.events import ElicitationDeferredEvent +from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, create_elicitation_bridge_capability, ) -from agentpool.orchestrator.core import SessionClosedError -from agentpool.sessions.models import ( +from wolfharness.orchestrator.core import SessionClosedError +from wolfharness.sessions.models import ( ElicitationResumePayload, PendingDeferredCall, ) -from agentpool.ui.base import InputProvider +from wolfharness.ui.base import InputProvider # ============================================================================ @@ -141,7 +141,7 @@ async def test_in_process_resume_resolves_future_and_completes_tool( run_ctx.deps.run_ctx.event_bus = mock_bus with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ): result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -319,7 +319,7 @@ async def test_mixed_elicitation_and_deferred_tool_results_routing( run_ctx.deps.run_ctx.event_bus = mock_bus with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ): result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -352,7 +352,7 @@ async def test_acp_converter_outputs_toolcallstart_for_elicitation_event() -> No with elicitation params in field_meta (deferred_handle, elicitation: True, elicitation_message, elicitation_schema, elicitation_mode). """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter converter = ACPEventConverter() event = ElicitationDeferredEvent( @@ -442,7 +442,7 @@ async def capture_emit(ctx: RunContext[Any], event: ElicitationDeferredEvent) -> captured_event.append(event) with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new=capture_emit, ): result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -540,16 +540,16 @@ async def test_opencode_processor_creates_toolpart_for_elicitation_event() -> No deferred_handle, elicitation: True, elicitation_message, elicitation_schema, elicitation_mode. """ - from agentpool_server.opencode_server.event_processor import EventProcessor - from agentpool_server.opencode_server.event_processor_context import ( + from wolfharness_server.opencode_server.event_processor import EventProcessor + from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) - from agentpool_server.opencode_server.models.message import ( + from wolfharness_server.opencode_server.models.message import ( MessagePath, MessageTime, MessageWithParts, ) - from agentpool_server.opencode_server.models.parts import ToolStateRunning + from wolfharness_server.opencode_server.models.parts import ToolStateRunning mock_state = MagicMock() assistant_msg = MessageWithParts.assistant( @@ -616,8 +616,8 @@ def test_acp_capability_gated_elicitation_fallback_to_request_permission() -> No ``checkpoint_enabled`` is False, ``supports_durable_elicitation`` returns False, meaning elicitation goes through the synchronous path. """ - from agentpool_server.acp_server.input_provider import ACPInputProvider - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.input_provider import ACPInputProvider + from wolfharness_server.acp_server.session import ACPSession mock_session = MagicMock(spec=ACPSession) mock_session.checkpoint_enabled = False @@ -636,8 +636,8 @@ async def test_acp_capability_gated_form_but_not_url() -> None: only form-mode elicitation should be durable. This tests the ``_client_supports_elicitation`` method's mode-based gating. """ - from agentpool_server.acp_server.input_provider import ACPInputProvider - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.input_provider import ACPInputProvider + from wolfharness_server.acp_server.session import ACPSession mock_session = MagicMock(spec=ACPSession) mock_session.checkpoint_enabled = True diff --git a/tests/elicitation/test_protocol_integration.py b/tests/elicitation/test_protocol_integration.py index 47b6d0c48..3639a8727 100644 --- a/tests/elicitation/test_protocol_integration.py +++ b/tests/elicitation/test_protocol_integration.py @@ -11,7 +11,7 @@ - ACP event converter with real EventBus subscription - handle_elicitation raises CallDeferred with real AgentContext -Refs: https://github.com/Leoyzen/agentpool/issues/107 +Refs: https://github.com/Leoyzen/wolfharness/issues/107 """ from __future__ import annotations @@ -26,15 +26,15 @@ from pydantic_ai.tools import DeferredToolRequests, RunContext import pytest -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events.events import ElicitationDeferredEvent -from agentpool.agents.native_agent.elicitation_bridge import ( +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events.events import ElicitationDeferredEvent +from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, create_elicitation_bridge_capability, ) -from agentpool.orchestrator.core import EventBus, SessionController -from agentpool.tools import CallDeferred -from agentpool.ui.base import InputProvider +from wolfharness.orchestrator.core import EventBus, SessionController +from wolfharness.tools import CallDeferred +from wolfharness.ui.base import InputProvider # ============================================================================ @@ -50,7 +50,7 @@ async def test_deferred_tool_requests_in_output_type_with_capabilities() -> None DeferredToolRequests to the pydantic-ai agent's output_type so deferred tool calls can be returned as agent output. """ - from agentpool import Agent + from wolfharness import Agent model = TestModel(custom_output_text="test response") @@ -131,7 +131,7 @@ async def test_session_state_checkpoint_enabled_with_store() -> None: When SessionController is constructed with a non-None store, create_session() must set SessionState.checkpoint_enabled=True. """ - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider store = MemoryStorageProvider() pool = _FakePool() @@ -183,8 +183,8 @@ async def test_acp_session_proxy_checkpoint_enabled_propagates() -> None: """ from acp.agent.acp_requests import ACPRequests from acp.schema.capabilities import ClientCapabilities - from agentpool_server.acp_server.handler import _ACPSessionProxy - from agentpool_server.acp_server.input_provider import ACPInputProvider + from wolfharness_server.acp_server.handler import _ACPSessionProxy + from wolfharness_server.acp_server.input_provider import ACPInputProvider # Create a mock client for ACPRequests — we won't actually call it. mock_client = MagicMock() @@ -208,8 +208,8 @@ async def test_acp_session_proxy_checkpoint_disabled_propagates() -> None: """ from acp.agent.acp_requests import ACPRequests from acp.schema.capabilities import ClientCapabilities - from agentpool_server.acp_server.handler import _ACPSessionProxy - from agentpool_server.acp_server.input_provider import ACPInputProvider + from wolfharness_server.acp_server.handler import _ACPSessionProxy + from wolfharness_server.acp_server.input_provider import ACPInputProvider mock_client = MagicMock() requests = ACPRequests(client=mock_client, session_id="test-session") @@ -372,7 +372,7 @@ async def test_acp_event_converter_with_real_event_bus() -> None: and verifies the ACPEventConverter yields a ToolCallStart with elicitation params in field_meta. """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter event_bus = EventBus() session_id = "test-converter-session" diff --git a/tests/elicitation/test_resume_lifecycle.py b/tests/elicitation/test_resume_lifecycle.py index 80ce9938b..9bff707e2 100644 --- a/tests/elicitation/test_resume_lifecycle.py +++ b/tests/elicitation/test_resume_lifecycle.py @@ -31,7 +31,7 @@ Level 1 passes, the bug is in ``RunHandle.start()`` event forwarding or ``EventBus`` delivery. -Refs: https://github.com/Leoyzen/agentpool/issues/107 +Refs: https://github.com/Leoyzen/wolfharness/issues/107 """ from __future__ import annotations @@ -45,22 +45,22 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentContext, AgentRunContext, ConfirmationResult -from agentpool.agents.events.events import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentContext, AgentRunContext, ConfirmationResult +from wolfharness.agents.events.events import ( RichAgentStreamEvent, StreamCompleteEvent, ) -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.lifecycle import MemoryJournal, ProtocolChannel -from agentpool.orchestrator.core import EventBus, EventEnvelope, SessionState -from agentpool.orchestrator.run import RunHandle -from agentpool.sessions.models import ElicitationResumePayload -from agentpool.ui.base import InputProvider +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.lifecycle import MemoryJournal, ProtocolChannel +from wolfharness.orchestrator.core import EventBus, EventEnvelope, SessionState +from wolfharness.orchestrator.run import RunHandle +from wolfharness.sessions.models import ElicitationResumePayload +from wolfharness.ui.base import InputProvider if TYPE_CHECKING: - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) @@ -419,7 +419,7 @@ async def collect_events() -> None: ) # Assert: StreamCompleteEvent(cancelled=True) should be yielded. - from agentpool.agents.events.events import RunErrorEvent + from wolfharness.agents.events.events import RunErrorEvent stream_complete_events = [e for e in events if isinstance(e, StreamCompleteEvent)] run_error_events = [e for e in events if isinstance(e, RunErrorEvent)] diff --git a/tests/elicitation/test_unit_elicitation.py b/tests/elicitation/test_unit_elicitation.py index 0e64b7c4d..9f65bf1af 100644 --- a/tests/elicitation/test_unit_elicitation.py +++ b/tests/elicitation/test_unit_elicitation.py @@ -3,7 +3,7 @@ Covers Tasks 9.1-9.7, 9.14-9.16, 9.18-9.19, 9.21-9.22 from the durable-elicitation-bridge plan. -Refs: https://github.com/Leoyzen/agentpool/issues/107 +Refs: https://github.com/Leoyzen/wolfharness/issues/107 # TODO: L2 migration — test uses complex inline mock_pool + mock_session_pool @@ -22,21 +22,21 @@ from pydantic_ai.tools import DeferredToolRequests, RunContext import pytest -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events.events import ElicitationDeferredEvent -from agentpool.agents.native_agent.elicitation_bridge import ( +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events.events import ElicitationDeferredEvent +from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, create_elicitation_bridge_capability, ) -from agentpool.agents.native_agent.elicitation_strategy import ( +from wolfharness.agents.native_agent.elicitation_strategy import ( CheckpointResolutionStrategy, ElicitationResolutionStrategy, ProtocolResolutionStrategy, ) -from agentpool.sessions.models import ElicitationResumePayload, PendingDeferredCall -from agentpool.tools import CallDeferred -from agentpool.ui.base import InputProvider -from agentpool.ui.elicitation import normalize_elicit_content +from wolfharness.sessions.models import ElicitationResumePayload, PendingDeferredCall +from wolfharness.tools import CallDeferred +from wolfharness.ui.base import InputProvider +from wolfharness.ui.elicitation import normalize_elicit_content # ============================================================================ @@ -173,10 +173,10 @@ async def test_handle_elicitation_durable_true_local( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True @@ -238,8 +238,8 @@ async def test_handle_elicitation_durable_false( @pytest.mark.unit async def test_call_tool_raises_call_deferred(agent_ctx: AgentContext) -> None: """MCPClient.call_tool raises CallDeferred when side-channel is set.""" - from agentpool.mcp_server.client import MCPClient - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness_config.mcp_server import StdioMCPServerConfig agent_ctx._pending_elicitation_deferral = { "message": "Enter credentials", @@ -261,7 +261,7 @@ async def test_call_tool_raises_call_deferred(agent_ctx: AgentContext) -> None: with ( patch( - "agentpool.mcp_server.conversions.from_mcp_content", + "wolfharness.mcp_server.conversions.from_mcp_content", new=AsyncMock(return_value=[]), ), pytest.raises(CallDeferred) as exc_info, @@ -282,8 +282,8 @@ async def test_call_tool_raises_call_deferred(agent_ctx: AgentContext) -> None: @pytest.mark.unit async def test_call_tool_normal_path(agent_ctx: AgentContext) -> None: """MCPClient.call_tool returns normally when no deferral is pending.""" - from agentpool.mcp_server.client import MCPClient - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness_config.mcp_server import StdioMCPServerConfig assert agent_ctx._pending_elicitation_deferral is None config = StdioMCPServerConfig(command="echo", args=["test"]) @@ -300,7 +300,7 @@ async def test_call_tool_normal_path(agent_ctx: AgentContext) -> None: client._client = mock_inner_client with patch( - "agentpool.mcp_server.conversions.from_mcp_content", + "wolfharness.mcp_server.conversions.from_mcp_content", new=AsyncMock(return_value=[]), ): result = await client.call_tool("test_tool", MagicMock(), {"arg": "val"}, agent_ctx) @@ -354,7 +354,7 @@ async def test_bridge_handles_elicitation_and_passthrough(run_ctx: RunContext[An run_ctx.deps.run_ctx.event_bus = mock_bus with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ) as mock_emit: result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -414,7 +414,7 @@ async def test_future_registry_lifecycle() -> None: @pytest.mark.unit def test_acp_input_provider_supports_durable_elicitation() -> None: """ACPInputProvider.supports_durable_elicitation reflects session.checkpoint_enabled.""" - from agentpool_server.acp_server.input_provider import ACPInputProvider + from wolfharness_server.acp_server.input_provider import ACPInputProvider mock_session = MagicMock() mock_session.checkpoint_enabled = True @@ -427,7 +427,7 @@ def test_acp_input_provider_supports_durable_elicitation() -> None: @pytest.mark.unit def test_opencode_input_provider_supports_durable_elicitation() -> None: """OpenCodeInputProvider.supports_durable_elicitation checks session state.""" - from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider + from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider mock_state = MagicMock() mock_session = MagicMock() @@ -458,8 +458,8 @@ async def test_side_channel_cleanup_on_error(agent_ctx: AgentContext) -> None: regardless of success or failure, ensuring no stale handler leaks into the next call. """ - from agentpool.mcp_server.client import MCPClient - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness.mcp_server.client import MCPClient + from wolfharness_config.mcp_server import StdioMCPServerConfig agent_ctx._pending_elicitation_deferral = { "message": "Enter key", @@ -514,7 +514,7 @@ async def test_bridge_positioning_elicitation_before_approval(run_ctx: RunContex run_ctx.deps.run_ctx.event_bus = mock_bus with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ): result = await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -719,10 +719,10 @@ async def test_handle_elicitation_passes_current_messages_to_checkpoint( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True @@ -792,10 +792,10 @@ async def test_handle_elicitation_checkpoint_failure_doesnt_set_checkpointed( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True @@ -856,10 +856,10 @@ async def test_handle_elicitation_updates_session_status_to_checkpointed( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload, SessionData + from wolfharness.sessions.models import ElicitationResumePayload, SessionData provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True @@ -936,10 +936,10 @@ async def test_handle_elicitation_skips_status_update_if_not_active( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload, SessionData + from wolfharness.sessions.models import ElicitationResumePayload, SessionData provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True @@ -1018,10 +1018,10 @@ async def test_handle_elicitation_uses_active_agent_run_messages_for_checkpoint( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True @@ -1082,10 +1082,10 @@ async def test_handle_elicitation_empty_checkpoint_when_no_active_run( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True @@ -1164,10 +1164,10 @@ async def test_handle_elicitation_resume_payload_normalizes_nested_content( """ import asyncio - from agentpool.agents.native_agent.elicitation_bridge import ( + from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, ) - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload provider = MagicMock(spec=InputProvider) provider.supports_durable_elicitation = True diff --git a/tests/fixtures/minimal_pool.py b/tests/fixtures/minimal_pool.py index ade97ad06..be39a19d8 100644 --- a/tests/fixtures/minimal_pool.py +++ b/tests/fixtures/minimal_pool.py @@ -11,7 +11,7 @@ import pytest import yamling -from agentpool import AgentPool, AgentsManifest +from wolfharness import AgentPool, AgentsManifest if TYPE_CHECKING: diff --git a/tests/fixtures/subagent_events.py b/tests/fixtures/subagent_events.py index 8f6d73b57..ac0708dd4 100644 --- a/tests/fixtures/subagent_events.py +++ b/tests/fixtures/subagent_events.py @@ -36,7 +36,7 @@ ) import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent, PartStartEvent, SpawnSessionStart, @@ -109,7 +109,7 @@ def get_tool_error_event( def get_stream_complete_event() -> StreamCompleteEvent[Any]: """Create a stream complete event.""" - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage message = ChatMessage( role="assistant", diff --git a/tests/fixtures/team_mode_pool.py b/tests/fixtures/team_mode_pool.py index 86a867c9e..94b2305ce 100644 --- a/tests/fixtures/team_mode_pool.py +++ b/tests/fixtures/team_mode_pool.py @@ -17,7 +17,7 @@ async def test_something(team_mode_pool): import pytest import yamling -from agentpool import AgentPool, AgentsManifest +from wolfharness import AgentPool, AgentsManifest if TYPE_CHECKING: diff --git a/tests/fixtures/test_resource_cap.py b/tests/fixtures/test_resource_cap.py index 7c24d1915..86a9afa06 100644 --- a/tests/fixtures/test_resource_cap.py +++ b/tests/fixtures/test_resource_cap.py @@ -29,7 +29,7 @@ from pydantic_ai.capabilities import AbstractCapability from pydantic_ai.toolsets import FunctionToolset -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.resource_protocols import ( ResourceEntry, SkillEntry, TextResourceContent, diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index a23d336f1..7ba12964d 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -4,7 +4,7 @@ import pytest -from agentpool.functional import run_agent, run_agent_sync +from wolfharness.functional import run_agent, run_agent_sync pytestmark = pytest.mark.unit diff --git a/tests/hooks/test_hook_aware_turn.py b/tests/hooks/test_hook_aware_turn.py index fc4e309ed..3c5da1877 100644 --- a/tests/hooks/test_hook_aware_turn.py +++ b/tests/hooks/test_hook_aware_turn.py @@ -13,9 +13,9 @@ import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.hooks import AgentHooks, CallableHook, HookResult -from agentpool.orchestrator.turn import HookAwareTurn +from wolfharness.agents.context import AgentRunContext +from wolfharness.hooks import AgentHooks, CallableHook, HookResult +from wolfharness.orchestrator.turn import HookAwareTurn # --------------------------------------------------------------------------- diff --git a/tests/hooks/test_hook_smoke_matrix.py b/tests/hooks/test_hook_smoke_matrix.py index 71a5cc2ac..422c37d2e 100644 --- a/tests/hooks/test_hook_smoke_matrix.py +++ b/tests/hooks/test_hook_smoke_matrix.py @@ -23,13 +23,13 @@ ToolCallStart, TurnCompleteUpdate, ) -from agentpool import Agent -from agentpool.agents.acp_agent.turn import ACPTurn -from agentpool.agents.context import AgentRunContext -from agentpool.hooks import AgentHooks, CallableHook, HookResult -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle +from wolfharness import Agent +from wolfharness.agents.acp_agent.turn import ACPTurn +from wolfharness.agents.context import AgentRunContext +from wolfharness.hooks import AgentHooks, CallableHook, HookResult +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle # --------------------------------------------------------------------------- @@ -96,7 +96,7 @@ def simple_tool() -> str: ) async with agent: run_ctx = AgentRunContext(session_id="smoke-session") - from agentpool.agents.native_agent.turn import NativeTurn + from wolfharness.agents.native_agent.turn import NativeTurn turn = NativeTurn( agent=agent, diff --git a/tests/hooks/test_hooks.py b/tests/hooks/test_hooks.py index ace78201d..e8c028eb3 100644 --- a/tests/hooks/test_hooks.py +++ b/tests/hooks/test_hooks.py @@ -6,15 +6,15 @@ import pytest -from agentpool import Agent -from agentpool.hooks import AgentHooks, CallableHook +from wolfharness import Agent +from wolfharness.hooks import AgentHooks, CallableHook pytestmark = pytest.mark.unit if TYPE_CHECKING: - from agentpool.hooks import HookResult + from wolfharness.hooks import HookResult # Hook state for testing diff --git a/tests/host/test_context.py b/tests/host/test_context.py index 5184f252e..8b7bf2d68 100644 --- a/tests/host/test_context.py +++ b/tests/host/test_context.py @@ -12,8 +12,8 @@ import pytest -from agentpool.host.context import HostContext -from agentpool.host.stubs import CapabilityCache, ModelCache, ModelRegistry +from wolfharness.host.context import HostContext +from wolfharness.host.stubs import CapabilityCache, ModelCache, ModelRegistry pytestmark = pytest.mark.unit diff --git a/tests/host/test_factory.py b/tests/host/test_factory.py index 6b2801e31..16b3aa634 100644 --- a/tests/host/test_factory.py +++ b/tests/host/test_factory.py @@ -7,11 +7,11 @@ import pytest -from agentpool.host.factory import AgentFactory +from wolfharness.host.factory import AgentFactory if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -103,7 +103,7 @@ def test_compile_does_not_register_skills_tools_provider_at_agent_scope( otherwise it gets registered at AGENT scope too, causing duplicate capability entries in get_visible_capabilities() and tool name conflicts. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel skills_provider = MagicMock() skills_provider.__class__ = type("FakeSkillsProvider", (), {}) @@ -134,10 +134,9 @@ def test_compile_registers_config_capabilities_at_agent_scope( minimal_pool: AgentPool, ) -> None: """Config-defined capabilities (e.g. Viking) are registered at AGENT scope.""" - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - # Use our test ResourceAccess capability from tests.fixtures.test_resource_cap import TestResourceAccessCap + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel test_cap = TestResourceAccessCap(read_text="test", read_uri="test://x") @@ -155,7 +154,7 @@ def test_compile_registers_config_capabilities_at_agent_scope( factory = AgentFactory(pool=minimal_pool) - with patch("agentpool.models.agents.NativeAgentConfig", (type(cfg),)): + with patch("wolfharness.models.agents.NativeAgentConfig", (type(cfg),)): factory.compile( manifest=manifest, host_context=_make_host_context(), @@ -164,7 +163,7 @@ def test_compile_registers_config_capabilities_at_agent_scope( agent_scope = Scope(level=ScopeLevel.AGENT, agent_name="test_agent") visible = minimal_pool.extension_registry.get_visible_capabilities(agent_scope) # TestResourceAccessCap should be at AGENT scope - from agentpool.capabilities.resource_protocols import ResourceAccess + from wolfharness.capabilities.resource_protocols import ResourceAccess ra_caps = [c for c in visible if isinstance(c, ResourceAccess)] assert len(ra_caps) == 1 @@ -180,7 +179,7 @@ def test_get_visible_capabilities_no_duplicates_across_scopes( This test documents that behavior so callers know to handle duplicates or avoid cross-scope registration of the same instance. """ - from agentpool.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel cap = MagicMock() reg = minimal_pool.extension_registry @@ -211,7 +210,7 @@ async def test_create_session_agent_native_main_calls_get_agent_with_pool( factory = AgentFactory(pool=pool) - with patch("agentpool.models.agents.NativeAgentConfig", (type(cfg),)): + with patch("wolfharness.models.agents.NativeAgentConfig", (type(cfg),)): result = await factory.create_session_agent( agent_name="test_agent", session_id="sess-1", @@ -236,7 +235,7 @@ async def test_create_session_agent_native_main_calls_aenter(minimal_pool: Agent factory = AgentFactory(pool=minimal_pool) - with patch("agentpool.models.agents.NativeAgentConfig", (type(cfg),)): + with patch("wolfharness.models.agents.NativeAgentConfig", (type(cfg),)): await factory.create_session_agent( agent_name="test_agent", session_id="sess-1", @@ -260,7 +259,7 @@ async def test_create_session_agent_native_main_no_pool_providers(minimal_pool: factory = AgentFactory(pool=minimal_pool) - with patch("agentpool.models.agents.NativeAgentConfig", (type(cfg),)): + with patch("wolfharness.models.agents.NativeAgentConfig", (type(cfg),)): await factory.create_session_agent( agent_name="test_agent", session_id="sess-1", @@ -304,7 +303,7 @@ async def test_create_session_agent_non_native_builds_snapshot_manually( factory = AgentFactory(pool=pool) # empty tuple → isinstance always False - with patch("agentpool.models.agents.NativeAgentConfig", ()): + with patch("wolfharness.models.agents.NativeAgentConfig", ()): result = await factory.create_session_agent( agent_name="acp_agent", session_id="sess-1", @@ -343,7 +342,7 @@ async def test_create_session_agent_fixes_missing_name(minimal_pool: AgentPool) host_context = _make_host_context() factory = AgentFactory(pool=minimal_pool) - with patch("agentpool.models.agents.NativeAgentConfig", (type(new_cfg),)): + with patch("wolfharness.models.agents.NativeAgentConfig", (type(new_cfg),)): await factory.create_session_agent( agent_name="fixed_name", session_id="sess-1", @@ -370,7 +369,7 @@ async def test_create_session_agent_native_main_loads_session(minimal_pool: Agen factory = AgentFactory(pool=minimal_pool) - with patch("agentpool.models.agents.NativeAgentConfig", (type(cfg),)): + with patch("wolfharness.models.agents.NativeAgentConfig", (type(cfg),)): await factory.create_session_agent( agent_name="test_agent", session_id="sess-42", diff --git a/tests/host/test_registry.py b/tests/host/test_registry.py index 7c3f12eea..a951f4159 100644 --- a/tests/host/test_registry.py +++ b/tests/host/test_registry.py @@ -6,7 +6,7 @@ import pytest -from agentpool.host.registry import AgentRegistry +from wolfharness.host.registry import AgentRegistry pytestmark = pytest.mark.unit diff --git a/tests/integration/test_acp_agent_close.py b/tests/integration/test_acp_agent_close.py index 27b182c22..5dca9d4a7 100644 --- a/tests/integration/test_acp_agent_close.py +++ b/tests/integration/test_acp_agent_close.py @@ -8,7 +8,7 @@ import pytest -from agentpool.utils.task_group import ManagedTaskGroup +from wolfharness.utils.task_group import ManagedTaskGroup pytestmark = pytest.mark.integration diff --git a/tests/integration/test_acp_streaming.py b/tests/integration/test_acp_streaming.py index 1761ae388..6255f7cce 100644 --- a/tests/integration/test_acp_streaming.py +++ b/tests/integration/test_acp_streaming.py @@ -36,10 +36,10 @@ ToolCallProgress, ToolCallStart, ) -from agentpool.agents.acp_agent import ACPAgent -from agentpool.agents.acp_agent.session_state import ACPSessionState -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.agents.acp_agent.session_state import ACPSessionState +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( PartDeltaEvent, RunStartedEvent, StreamCompleteEvent, @@ -47,7 +47,7 @@ ToolCallStartEvent, ToolResultMetadataEvent, ) -from agentpool.messaging import ChatMessage +from wolfharness.messaging import ChatMessage pytestmark = pytest.mark.unit @@ -401,7 +401,7 @@ async def test_acp_streaming_event_sequence_with_tool_metadata( ) # Patch acp_to_native_event to allow ToolResultMetadataEvent passthrough - from agentpool.agents.acp_agent import acp_converters as converters_mod + from wolfharness.agents.acp_agent import acp_converters as converters_mod global _original_acp_to_native_event # noqa: PLW0603 _original_acp_to_native_event = converters_mod.acp_to_native_event diff --git a/tests/integration/test_base_server_start_background.py b/tests/integration/test_base_server_start_background.py index 49dfb8187..f6432c51a 100644 --- a/tests/integration/test_base_server_start_background.py +++ b/tests/integration/test_base_server_start_background.py @@ -12,7 +12,7 @@ import pytest -from agentpool_server.base import BaseServer +from wolfharness_server.base import BaseServer pytestmark = pytest.mark.integration diff --git a/tests/integration/test_cross_protocol.py b/tests/integration/test_cross_protocol.py index f493baf5e..386f48654 100644 --- a/tests/integration/test_cross_protocol.py +++ b/tests/integration/test_cross_protocol.py @@ -10,9 +10,9 @@ import pytest -from agentpool.agents.modes import ModeCategory, ModeInfo -from agentpool_server.acp_server.acp_agent import get_agent_role_config_option -from agentpool_server.opencode_server.routes.config_routes import list_modes +from wolfharness.agents.modes import ModeCategory, ModeInfo +from wolfharness_server.acp_server.acp_agent import get_agent_role_config_option +from wolfharness_server.opencode_server.routes.config_routes import list_modes pytestmark = pytest.mark.unit @@ -87,9 +87,9 @@ async def test_agent_role_hidden_when_single_mode(self): async def test_cross_protocol_model_alignment(self): """Both protocols reflect same underlying model state.""" - from agentpool.models.model_configs import StringModelConfig - from agentpool_server.acp_server.provider_router import ProviderRouter - from agentpool_server.shared.model_utils import build_model_state_for_acp + from wolfharness.models.model_configs import StringModelConfig + from wolfharness_server.acp_server.provider_router import ProviderRouter + from wolfharness_server.shared.model_utils import build_model_state_for_acp # Manifest with configured variants manifest = MagicMock() diff --git a/tests/integration/test_event_manager_lifecycle.py b/tests/integration/test_event_manager_lifecycle.py index 26561cd6b..e8f9ee5e0 100644 --- a/tests/integration/test_event_manager_lifecycle.py +++ b/tests/integration/test_event_manager_lifecycle.py @@ -13,7 +13,7 @@ import pytest -from agentpool.messaging.event_manager import EventManager +from wolfharness.messaging.event_manager import EventManager pytestmark = pytest.mark.integration diff --git a/tests/integration/test_permission_denial_sync.py b/tests/integration/test_permission_denial_sync.py index 174c0a7e0..3cb05c0bf 100644 --- a/tests/integration/test_permission_denial_sync.py +++ b/tests/integration/test_permission_denial_sync.py @@ -23,7 +23,7 @@ if TYPE_CHECKING: - from agentpool import AgentContext + from wolfharness import AgentContext @dataclass diff --git a/tests/integration/test_skill_e2e.py b/tests/integration/test_skill_e2e.py index db2e55695..f8c07c9f9 100644 --- a/tests/integration/test_skill_e2e.py +++ b/tests/integration/test_skill_e2e.py @@ -18,24 +18,24 @@ import pytest -from agentpool.capabilities.extension_registry import ( +from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, ) -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.resource_protocols import ( SkillEntry, SkillResource, ) -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.skills.skill import Skill -from agentpool.skills.skill_tool_manager import SkillToolManager -from agentpool.skills.uri_resolver import SkillURIResolver -from agentpool_config.skills import SkillToolConfig +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.skills.skill import Skill +from wolfharness.skills.skill_tool_manager import SkillToolManager +from wolfharness.skills.uri_resolver import SkillURIResolver +from wolfharness_config.skills import SkillToolConfig if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -160,7 +160,7 @@ async def test_skill_uri_resolution_nonexistent_skill() -> None: Given a SkillURIResolver with ExtensionRegistry, When resolving a nonexistent skill URI, Then SkillNotFoundError is raised. """ - from agentpool.skills.exceptions import SkillNotFoundError + from wolfharness.skills.exceptions import SkillNotFoundError skill = make_skill(name="existing-skill", instructions="Content.") cap = SkillManagerCap(local_skills={"existing-skill": skill}) @@ -214,7 +214,7 @@ def test_load_skill_available_in_standalone_agent(minimal_pool: AgentPool) -> No owned by SkillManagerCap (registered at pool scope). _inject_pool_providers no longer injects skills_tools_provider. """ - from agentpool.host.factory import _inject_pool_providers + from wolfharness.host.factory import _inject_pool_providers class FakeAgent: def __init__(self) -> None: @@ -247,7 +247,7 @@ def test_load_skill_available_in_child_session_agent(minimal_pool: AgentPool) -> the agent's _external_capabilities is checked, Then only the MCP aggregating provider is injected (skills come from SkillManagerCap). """ - from agentpool.host.factory import _inject_pool_providers + from wolfharness.host.factory import _inject_pool_providers class FakeAgent: def __init__(self) -> None: @@ -348,7 +348,7 @@ async def test_load_skill_return_includes_tool_status() -> None: display tool import status. We verify that the tool import succeeds and the MCP server config is accessible. """ - from agentpool_config.skills import SkillMcpServerConfig + from wolfharness_config.skills import SkillMcpServerConfig skill = make_skill( name="status-skill", diff --git a/tests/integration/test_skill_resolution.py b/tests/integration/test_skill_resolution.py index 98b57baf9..3c84ca8a9 100644 --- a/tests/integration/test_skill_resolution.py +++ b/tests/integration/test_skill_resolution.py @@ -12,10 +12,10 @@ import pytest from upathtools import UPath -from agentpool import AgentPool -from agentpool.skills.exceptions import SecurityError -from agentpool.skills.uri_resolver import ResolvedSkillURI -from agentpool.tools.exceptions import ToolError +from wolfharness import AgentPool +from wolfharness.skills.exceptions import SecurityError +from wolfharness.skills.uri_resolver import ResolvedSkillURI +from wolfharness.tools.exceptions import ToolError pytestmark = pytest.mark.integration @@ -272,7 +272,7 @@ async def test_positional_argument_substitution( test_skill_with_reference: UPath, ) -> None: """Test $1, $2 argument substitution.""" - from agentpool_toolsets.builtin.skills import _substitute_arguments + from wolfharness_toolsets.builtin.skills import _substitute_arguments instructions = "First: $1, Second: $2" result = _substitute_arguments(instructions, "hello world") @@ -285,7 +285,7 @@ async def test_all_arguments_substitution( test_skill_with_reference: UPath, ) -> None: """Test $@ and $ARGUMENTS substitution.""" - from agentpool_toolsets.builtin.skills import _substitute_arguments + from wolfharness_toolsets.builtin.skills import _substitute_arguments instructions = "All: $@, Also: $ARGUMENTS" result = _substitute_arguments(instructions, "arg1 arg2 arg3") @@ -298,7 +298,7 @@ async def test_mixed_argument_substitution( test_skill_with_reference: UPath, ) -> None: """Test mixed positional and all-arguments substitution.""" - from agentpool_toolsets.builtin.skills import _substitute_arguments + from wolfharness_toolsets.builtin.skills import _substitute_arguments instructions = "First: $1, Rest: $@" result = _substitute_arguments(instructions, "alpha beta gamma") @@ -311,7 +311,7 @@ async def test_no_arguments_substitution( test_skill_with_reference: UPath, ) -> None: """Test behavior when no arguments provided.""" - from agentpool_toolsets.builtin.skills import _substitute_arguments + from wolfharness_toolsets.builtin.skills import _substitute_arguments instructions = "Args: $1, All: $@" result = _substitute_arguments(instructions, None) diff --git a/tests/integration/test_step_usage_e2e.py b/tests/integration/test_step_usage_e2e.py index d8152786c..cd721a99c 100644 --- a/tests/integration/test_step_usage_e2e.py +++ b/tests/integration/test_step_usage_e2e.py @@ -13,15 +13,15 @@ import pytest from acp.schema import UsageUpdate -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events.events import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events.events import ( StepUsageEvent, StreamCompleteEvent, ) -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.orchestrator.event_bus import EventBus -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.orchestrator.event_bus import EventBus +from wolfharness_server.acp_server.event_converter import ACPEventConverter # --------------------------------------------------------------------------- diff --git a/tests/integration/test_v2_message_id_integration.py b/tests/integration/test_v2_message_id_integration.py index fcaa7e458..29376d6f4 100644 --- a/tests/integration/test_v2_message_id_integration.py +++ b/tests/integration/test_v2_message_id_integration.py @@ -30,23 +30,23 @@ import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( PartDeltaEvent, PartStartEvent, StreamCompleteEvent, ) -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( DeliveryMode, Feedback, MemoryJournal, ProtocolChannel, ) -from agentpool.messaging import ChatMessage, MessageHistory -from agentpool.orchestrator.core import EventBus, SessionPool -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.session_controller import SessionState -from agentpool.orchestrator.turn import Turn +from wolfharness.messaging import ChatMessage, MessageHistory +from wolfharness.orchestrator.core import EventBus, SessionPool +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.session_controller import SessionState +from wolfharness.orchestrator.turn import Turn pytestmark = pytest.mark.integration @@ -136,8 +136,8 @@ async def test_steer_revoke_flow() -> None: ``feedback_queue`` when no RunHandle is active. ``SessionState.revoke()`` removes a queued steer message by ID. """ - from agentpool.lifecycle.types import Feedback - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.lifecycle.types import Feedback + from wolfharness.orchestrator.session_controller import SessionState session = SessionState(session_id="test-session", agent_name="test-agent") @@ -187,8 +187,8 @@ async def test_acp_turn_message_id_propagation() -> None: When: The turn constructs ChatMessage objects. Then: All ChatMessages from the same turn share the same message_id. """ - from agentpool.agents.acp_agent.turn import ACPTurn - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.acp_agent.turn import ACPTurn + from wolfharness.agents.context import AgentRunContext # Create a mock ACP client. client = MagicMock() @@ -231,7 +231,7 @@ async def test_acp_event_converter_reads_message_id_from_events() -> None: Then: The returned message_id matches the event's message_id, not a newly generated UUID. """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter converter = ACPEventConverter() @@ -613,7 +613,7 @@ async def test_deprecation_warnings_emitted() -> None: Note: receive_request() deprecation tests removed — the method was deleted in Phase 6.4 of session-debt-cleanup. """ - from agentpool.capabilities.runloop_delegation import RunLoopDelegationService + from wolfharness.capabilities.runloop_delegation import RunLoopDelegationService # --- RunLoopDelegationService.get_available_agents() --- registry = MagicMock() @@ -652,9 +652,9 @@ async def _empty_gen() -> Any: # Mock EventBus for the new EventBus subscription pattern in spawn_subagent import asyncio as _asyncio - from agentpool.agents.events import StreamCompleteEvent - from agentpool.messaging.messagenode import ChatMessage - from agentpool.orchestrator.core import EventEnvelope + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.messaging.messagenode import ChatMessage + from wolfharness.orchestrator.core import EventEnvelope event_queue: _asyncio.Queue[Any] = _asyncio.Queue() done_event = StreamCompleteEvent( diff --git a/tests/lifecycle/test_comm_channel.py b/tests/lifecycle/test_comm_channel.py index 847e287c3..d148eb90e 100644 --- a/tests/lifecycle/test_comm_channel.py +++ b/tests/lifecycle/test_comm_channel.py @@ -7,7 +7,7 @@ import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( MessageReplacementEvent, PartDeltaEvent, PartStartEvent, @@ -15,7 +15,7 @@ StateUpdate, ToolCallUpdateEvent, ) -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( CommChannel, DeliveryMode, DirectChannel, @@ -24,7 +24,7 @@ ProtocolChannel, RunState, ) -from agentpool.orchestrator.event_bus import EventBus +from wolfharness.orchestrator.event_bus import EventBus pytestmark = pytest.mark.unit @@ -80,7 +80,7 @@ def _make_plan_update(tool_call_id: str | None = "tc-plan") -> PlanUpdateEvent: def test_upsert_key_tool_call_update(): """ToolCallUpdateEvent derives key tool_call:{tool_call_id}.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = _make_tool_call_update() assert _derive_upsert_key(event) == "tool_call:tc-123" @@ -88,7 +88,7 @@ def test_upsert_key_tool_call_update(): def test_upsert_key_tool_call_update_empty_id(): """ToolCallUpdateEvent with empty tool_call_id returns None.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = ToolCallUpdateEvent(tool_call_id="", tool_name="bash") assert _derive_upsert_key(event) is None @@ -96,7 +96,7 @@ def test_upsert_key_tool_call_update_empty_id(): def test_upsert_key_state_update(): """StateUpdate derives key state:{session_id}.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = _make_state_update() assert _derive_upsert_key(event) == "state:sess-1" @@ -104,7 +104,7 @@ def test_upsert_key_state_update(): def test_upsert_key_state_update_empty_session(): """StateUpdate with empty session_id returns None.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = StateUpdate(session_id="", state=RunState.IDLE) assert _derive_upsert_key(event) is None @@ -112,7 +112,7 @@ def test_upsert_key_state_update_empty_session(): def test_upsert_key_message_replacement(): """MessageReplacementEvent derives key msg:{message_id}.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = _make_message_replacement() assert _derive_upsert_key(event) == "msg:msg-1" @@ -120,7 +120,7 @@ def test_upsert_key_message_replacement(): def test_upsert_key_message_replacement_empty_id(): """MessageReplacementEvent with empty message_id returns None.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = MessageReplacementEvent(message_id="", content="x") assert _derive_upsert_key(event) is None @@ -128,7 +128,7 @@ def test_upsert_key_message_replacement_empty_id(): def test_upsert_key_plan_update_with_tool_call_id(): """PlanUpdateEvent with tool_call_id derives key plan:{tool_call_id}.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = _make_plan_update(tool_call_id="tc-plan") assert _derive_upsert_key(event) == "plan:tc-plan" @@ -136,7 +136,7 @@ def test_upsert_key_plan_update_with_tool_call_id(): def test_upsert_key_plan_update_without_tool_call_id(): """PlanUpdateEvent with None tool_call_id returns None.""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = _make_plan_update(tool_call_id=None) assert _derive_upsert_key(event) is None @@ -144,7 +144,7 @@ def test_upsert_key_plan_update_without_tool_call_id(): def test_upsert_key_delta_event_returns_none(): """PartDeltaEvent returns None (append semantics).""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = _make_delta_event() assert _derive_upsert_key(event) is None @@ -152,7 +152,7 @@ def test_upsert_key_delta_event_returns_none(): def test_upsert_key_part_start_event_returns_none(): """PartStartEvent returns None (append semantics).""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key event = PartStartEvent.text(index=0, content="start") assert _derive_upsert_key(event) is None @@ -160,7 +160,7 @@ def test_upsert_key_part_start_event_returns_none(): def test_upsert_key_arbitrary_object_returns_none(): """Arbitrary objects return None (append semantics).""" - from agentpool.lifecycle.comm_channel import _derive_upsert_key + from wolfharness.lifecycle.comm_channel import _derive_upsert_key assert _derive_upsert_key("not an event") is None assert _derive_upsert_key(42) is None @@ -583,7 +583,7 @@ def test_protocol_channel_recv_none_after_close(): async def test_protocol_channel_with_real_event_bus(): """ProtocolChannel works with a real EventBus instance.""" - from agentpool.orchestrator.event_bus import EventBus + from wolfharness.orchestrator.event_bus import EventBus bus = EventBus() journal = MemoryJournal() diff --git a/tests/lifecycle/test_config_and_integration.py b/tests/lifecycle/test_config_and_integration.py index 26884f0a1..966f33d58 100644 --- a/tests/lifecycle/test_config_and_integration.py +++ b/tests/lifecycle/test_config_and_integration.py @@ -4,16 +4,16 @@ import pytest -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.event_transport import InProcessTransport -from agentpool.lifecycle.factory import create_dimensions -from agentpool.lifecycle.journal import DurableJournal, MemoryJournal -from agentpool.lifecycle.snapshot_store import ( +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.event_transport import InProcessTransport +from wolfharness.lifecycle.factory import create_dimensions +from wolfharness.lifecycle.journal import DurableJournal, MemoryJournal +from wolfharness.lifecycle.snapshot_store import ( DurableSnapshotStore, MemorySnapshotStore, ) -from agentpool_config.lifecycle import LifecycleConfig -from agentpool_config.nodes import BaseAgentConfig +from wolfharness_config.lifecycle import LifecycleConfig +from wolfharness_config.nodes import BaseAgentConfig pytestmark = pytest.mark.integration diff --git a/tests/lifecycle/test_crash_recovery.py b/tests/lifecycle/test_crash_recovery.py index 9994c39b9..1059b8f02 100644 --- a/tests/lifecycle/test_crash_recovery.py +++ b/tests/lifecycle/test_crash_recovery.py @@ -17,11 +17,11 @@ import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( StreamCompleteEvent, ) -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( DirectChannel, DurableJournal, DurableSnapshotStore, @@ -30,11 +30,11 @@ RunState, ToolExecutionRecord, ) -from agentpool.lifecycle.journal import _detect_inflight_turn, _extract_turn_id -from agentpool.messaging import ChatMessage, MessageHistory -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.session_controller import SessionState -from agentpool.orchestrator.turn import HookAwareTurn, Turn +from wolfharness.lifecycle.journal import _detect_inflight_turn, _extract_turn_id +from wolfharness.messaging import ChatMessage, MessageHistory +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.session_controller import SessionState +from wolfharness.orchestrator.turn import HookAwareTurn, Turn if TYPE_CHECKING: diff --git a/tests/lifecycle/test_deprecation.py b/tests/lifecycle/test_deprecation.py index 077f3018a..8112582e3 100644 --- a/tests/lifecycle/test_deprecation.py +++ b/tests/lifecycle/test_deprecation.py @@ -15,7 +15,7 @@ import pytest -from agentpool.messaging.messagenode import MessageNode +from wolfharness.messaging.messagenode import MessageNode class _TestNode(MessageNode[Any, Any]): diff --git a/tests/lifecycle/test_event_transport.py b/tests/lifecycle/test_event_transport.py index 5b6948d9a..37ddf8017 100644 --- a/tests/lifecycle/test_event_transport.py +++ b/tests/lifecycle/test_event_transport.py @@ -7,7 +7,7 @@ import pytest -from agentpool.lifecycle import EventEnvelope, EventTransport, InProcessTransport +from wolfharness.lifecycle import EventEnvelope, EventTransport, InProcessTransport if TYPE_CHECKING: diff --git a/tests/lifecycle/test_journal.py b/tests/lifecycle/test_journal.py index 2befc5869..482ba78e6 100644 --- a/tests/lifecycle/test_journal.py +++ b/tests/lifecycle/test_journal.py @@ -7,7 +7,7 @@ import pytest -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( DurableJournal, Journal, MemoryJournal, diff --git a/tests/lifecycle/test_recovery_event_delivery.py b/tests/lifecycle/test_recovery_event_delivery.py index 02e4c5eff..fc153502e 100644 --- a/tests/lifecycle/test_recovery_event_delivery.py +++ b/tests/lifecycle/test_recovery_event_delivery.py @@ -28,13 +28,13 @@ import anyio import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent, RunStartedEvent, StateUpdate, StreamCompleteEvent, ) -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( DurableJournal, DurableSnapshotStore, MemoryJournal, @@ -42,9 +42,9 @@ RunState, ToolExecutionRecord, ) -from agentpool.lifecycle.comm_channel import DirectChannel, ProtocolChannel -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.event_bus import EventBus +from wolfharness.lifecycle.comm_channel import DirectChannel, ProtocolChannel +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.event_bus import EventBus pytestmark = [pytest.mark.integration, pytest.mark.anyio] diff --git a/tests/lifecycle/test_run_loop.py b/tests/lifecycle/test_run_loop.py index ab4ddc287..7984da383 100644 --- a/tests/lifecycle/test_run_loop.py +++ b/tests/lifecycle/test_run_loop.py @@ -34,12 +34,12 @@ import pytest -from agentpool.agents.events import StreamCompleteEvent -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.messaging import ChatMessage, MessageHistory -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.session_controller import SessionState -from agentpool.orchestrator.turn import Turn +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.messaging import ChatMessage, MessageHistory +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.session_controller import SessionState +from wolfharness.orchestrator.turn import Turn pytestmark = pytest.mark.unit diff --git a/tests/lifecycle/test_session_migration.py b/tests/lifecycle/test_session_migration.py index fcd179f98..bf5babb86 100644 --- a/tests/lifecycle/test_session_migration.py +++ b/tests/lifecycle/test_session_migration.py @@ -28,25 +28,25 @@ import anyio import pytest -from agentpool.agents.events import ( +from tests._controller_helpers import send_via_controller +from wolfharness.agents.events import ( RunStartedEvent, StreamCompleteEvent, ) -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( DirectChannel, MemoryJournal, ProtocolChannel, ProtocolTrigger, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.event_bus import EventBus -from agentpool.orchestrator.session_controller import ( +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.event_bus import EventBus +from wolfharness.orchestrator.session_controller import ( SessionController, SessionState, ) -from agentpool.orchestrator.session_pool import SessionPool -from agentpool.orchestrator.turn import Turn -from tests._controller_helpers import send_via_controller +from wolfharness.orchestrator.session_pool import SessionPool +from wolfharness.orchestrator.turn import Turn pytestmark = pytest.mark.unit @@ -437,8 +437,8 @@ async def test_set_current_run_id_publishes_state_update_running() -> None: ``comm.on_state_change(RunState.RUNNING)`` and schedules a ``StateUpdate`` event publish. """ - from agentpool.agents.events import StateUpdate - from agentpool.lifecycle.types import RunState + from wolfharness.agents.events import StateUpdate + from wolfharness.lifecycle.types import RunState session = SessionState(session_id="s1", agent_name="test_agent") comm = MagicMock() @@ -470,8 +470,8 @@ async def test_set_current_run_id_publishes_state_update_idle() -> None: ``comm.on_state_change(RunState.IDLE)`` and schedules a ``StateUpdate`` event publish. """ - from agentpool.agents.events import StateUpdate - from agentpool.lifecycle.types import RunState + from wolfharness.agents.events import StateUpdate + from wolfharness.lifecycle.types import RunState session = SessionState(session_id="s1", agent_name="test_agent") comm = MagicMock() diff --git a/tests/lifecycle/test_snapshot_store.py b/tests/lifecycle/test_snapshot_store.py index 4da15e12e..d143a3d92 100644 --- a/tests/lifecycle/test_snapshot_store.py +++ b/tests/lifecycle/test_snapshot_store.py @@ -6,7 +6,7 @@ import pytest -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( DurableSnapshotStore, MemorySnapshotStore, SnapshotStore, diff --git a/tests/lifecycle/test_trigger_source.py b/tests/lifecycle/test_trigger_source.py index f6ed1f744..d6c30b698 100644 --- a/tests/lifecycle/test_trigger_source.py +++ b/tests/lifecycle/test_trigger_source.py @@ -6,7 +6,7 @@ import pytest -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( ChannelTrigger, ImmediateTrigger, Prompt, diff --git a/tests/lifecycle/test_types.py b/tests/lifecycle/test_types.py index bdacfff9d..630973dd5 100644 --- a/tests/lifecycle/test_types.py +++ b/tests/lifecycle/test_types.py @@ -6,13 +6,13 @@ import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( MessageReplacementEvent, RichAgentStreamEvent, StateUpdate, ToolCallUpdateEvent, ) -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( CommChannel, EventEnvelope, EventTransport, diff --git a/tests/manifest/test_loader.py b/tests/manifest/test_loader.py index 6f0a54c96..c1cb6ba60 100644 --- a/tests/manifest/test_loader.py +++ b/tests/manifest/test_loader.py @@ -7,7 +7,7 @@ import pytest -from agentpool import AgentsManifest +from wolfharness import AgentsManifest pytestmark = pytest.mark.unit diff --git a/tests/manifest/test_metadata_fields.py b/tests/manifest/test_metadata_fields.py index 93a2a68be..393e8725c 100644 --- a/tests/manifest/test_metadata_fields.py +++ b/tests/manifest/test_metadata_fields.py @@ -14,9 +14,9 @@ import pytest import yamling -from agentpool import AgentsManifest -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.model_configs import StringModelConfig +from wolfharness import AgentsManifest +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.model_configs import StringModelConfig pytestmark = pytest.mark.unit diff --git a/tests/manifest/test_models.py b/tests/manifest/test_models.py index 497cf6b87..0353f1d66 100644 --- a/tests/manifest/test_models.py +++ b/tests/manifest/test_models.py @@ -7,7 +7,7 @@ from schemez import InlineSchemaDef import yamling -from agentpool import AgentsManifest +from wolfharness import AgentsManifest pytestmark = pytest.mark.unit diff --git a/tests/manifest/test_schema.py b/tests/manifest/test_schema.py index a06f2ee8f..7694652d4 100644 --- a/tests/manifest/test_schema.py +++ b/tests/manifest/test_schema.py @@ -2,7 +2,7 @@ import pytest -from agentpool import AgentsManifest +from wolfharness import AgentsManifest pytestmark = pytest.mark.unit diff --git a/tests/manifest/test_schema_override.py b/tests/manifest/test_schema_override.py index b51109771..23bcbe695 100644 --- a/tests/manifest/test_schema_override.py +++ b/tests/manifest/test_schema_override.py @@ -4,7 +4,7 @@ import pytest -from agentpool.tools.base import Tool +from wolfharness.tools.base import Tool pytestmark = pytest.mark.unit diff --git a/tests/manifest/test_yaml_inheritance.py b/tests/manifest/test_yaml_inheritance.py index 59d838da5..357fd11bb 100644 --- a/tests/manifest/test_yaml_inheritance.py +++ b/tests/manifest/test_yaml_inheritance.py @@ -4,7 +4,7 @@ import pytest -from agentpool import AgentsManifest +from wolfharness import AgentsManifest pytestmark = pytest.mark.unit diff --git a/tests/mcp_client/test_client_conversion.py b/tests/mcp_client/test_client_conversion.py index 00ff8f18f..63648447b 100644 --- a/tests/mcp_client/test_client_conversion.py +++ b/tests/mcp_client/test_client_conversion.py @@ -17,8 +17,8 @@ from pydantic_ai.usage import RunUsage import pytest -from agentpool.mcp_server import MCPClient -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness.mcp_server import MCPClient +from wolfharness_config.mcp_server import StdioMCPServerConfig pytestmark = pytest.mark.unit diff --git a/tests/mcp_client/test_contextual_progress.py b/tests/mcp_client/test_contextual_progress.py index f06b1f8c0..0b21ba535 100644 --- a/tests/mcp_client/test_contextual_progress.py +++ b/tests/mcp_client/test_contextual_progress.py @@ -8,9 +8,9 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentContext # noqa: TC001 -from agentpool.agents.events import ToolCallProgressEvent +from wolfharness import Agent +from wolfharness.agents.context import AgentContext # noqa: TC001 +from wolfharness.agents.events import ToolCallProgressEvent pytestmark = pytest.mark.unit @@ -19,7 +19,7 @@ if TYPE_CHECKING: from typing import Any - from agentpool.agents.events import RichAgentStreamEvent + from wolfharness.agents.events import RichAgentStreamEvent # Constants for test expectations diff --git a/tests/mcp_client/test_mcp_features.py b/tests/mcp_client/test_mcp_features.py index 9b53c3f22..b1677ad60 100644 --- a/tests/mcp_client/test_mcp_features.py +++ b/tests/mcp_client/test_mcp_features.py @@ -1,5 +1,5 @@ # /// script -# dependencies = ["agentpool"] +# dependencies = ["wolfharness"] # /// """Demo: Agent using MCP server with code fixer (sampling + elicitation).""" @@ -10,8 +10,8 @@ import pytest -from agentpool import Agent -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness import Agent +from wolfharness_config.mcp_server import StdioMCPServerConfig pytestmark = pytest.mark.unit diff --git a/tests/mcp_server/test_child_session_acp_fix.py b/tests/mcp_server/test_child_session_acp_fix.py index 27949517d..1e2cb3554 100644 --- a/tests/mcp_server/test_child_session_acp_fix.py +++ b/tests/mcp_server/test_child_session_acp_fix.py @@ -15,12 +15,12 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool.mcp_server.manager import MCPManager -from agentpool_server.acp_server.acp_mcp_manager import ( +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_server.acp_server.acp_mcp_manager import ( AcpMcpConnection, AcpMcpConnectionManager, ) -from agentpool_server.acp_server.acp_mcp_transport import AcpMcpTransport +from wolfharness_server.acp_server.acp_mcp_transport import AcpMcpTransport # --------------------------------------------------------------------------- diff --git a/tests/mcp_server/test_config_snapshot.py b/tests/mcp_server/test_config_snapshot.py index 3cbfeb370..d545c0062 100644 --- a/tests/mcp_server/test_config_snapshot.py +++ b/tests/mcp_server/test_config_snapshot.py @@ -7,8 +7,8 @@ from pydantic import HttpUrl import pytest -from agentpool.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot -from agentpool_config.mcp_server import ( +from wolfharness.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot +from wolfharness_config.mcp_server import ( SSEMCPServerConfig, StdioMCPServerConfig, StreamableHTTPMCPServerConfig, diff --git a/tests/mcp_server/test_e2e_acp_inheritance_function_model.py b/tests/mcp_server/test_e2e_acp_inheritance_function_model.py index 251ffe24b..832be4d40 100644 --- a/tests/mcp_server/test_e2e_acp_inheritance_function_model.py +++ b/tests/mcp_server/test_e2e_acp_inheritance_function_model.py @@ -21,13 +21,13 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool import Agent, AgentPool -from agentpool.mcp_server.manager import MCPManager -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager -from agentpool_server.acp_server.session import ACPSession +from wolfharness import Agent, AgentPool +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness_server.acp_server.session import ACPSession # --------------------------------------------------------------------------- @@ -208,7 +208,7 @@ async def test_initialize_mcp_servers_registers_transport_and_syncs_snapshot() - # Bug 1: transport registered in connection_pool assert ctx.connection_pool is not None - from agentpool_config.mcp_server import AcpMCPServerConfig + from wolfharness_config.mcp_server import AcpMCPServerConfig acp_config = AcpMCPServerConfig(name="test-server", acp_id="test-acp-id") transport = await ctx.connection_pool.get_transport(acp_config) @@ -279,8 +279,8 @@ async def test_child_session_inherits_acp_configs_and_transports() -> None: - ``connection_pool`` with pre-created transport (copied via ``copy_pre_created_transports()``). """ - from agentpool.orchestrator.session_pool import SessionPool - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness.orchestrator.session_pool import SessionPool + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider mcp_manager, _acp_manager, _base_agent, acp_agent, _acp_session, mock_client = ( _build_test_fixture("test3-parent") @@ -343,7 +343,7 @@ async def test_child_session_inherits_acp_configs_and_transports() -> None: # Assert child's connection_pool has pre-created transport assert child_ctx.connection_pool is not None - from agentpool_config.mcp_server import AcpMCPServerConfig + from wolfharness_config.mcp_server import AcpMCPServerConfig acp_config = AcpMCPServerConfig(name="test-server", acp_id="test-acp-id") inherited_transport = await child_ctx.connection_pool.get_transport(acp_config) @@ -371,8 +371,8 @@ async def test_child_get_capabilities_finds_inherited_acp_configs() -> None: calling ``get_capabilities(session_id=child_session_id)`` on the child's MCPManager should return a non-empty list. """ - from agentpool.orchestrator.session_pool import SessionPool - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness.orchestrator.session_pool import SessionPool + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider mcp_manager, _acp_manager, _base_agent, acp_agent, _acp_session, mock_client = ( _build_test_fixture("test4-parent") diff --git a/tests/mcp_server/test_e2e_connection_tracking.py b/tests/mcp_server/test_e2e_connection_tracking.py index 5b805b8cc..c0f3d297e 100644 --- a/tests/mcp_server/test_e2e_connection_tracking.py +++ b/tests/mcp_server/test_e2e_connection_tracking.py @@ -13,11 +13,11 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool.mcp_server.config_snapshot import McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager -from agentpool.orchestrator.session_controller import SessionController, SessionState -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.mcp_server.config_snapshot import McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.orchestrator.session_controller import SessionController, SessionState +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness_server.acp_server.session_manager import ACPSessionManager # ============================================================================ @@ -89,7 +89,7 @@ async def mock_session_close() -> None: try: with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", return_value=mock_session, ): session_id = await acp_manager.create_session( @@ -204,7 +204,7 @@ async def mock_get_agent(sid: str, agent_name: str | None = None) -> MagicMock: mock_session.register_update_callback = MagicMock() with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", return_value=mock_session, ): session_id = await acp_manager.create_session( @@ -340,7 +340,7 @@ async def mock_get_agent(sid: str, agent_name: str | None = None) -> MagicMock: mock_session_2.close = AsyncMock() with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", side_effect=[mock_session_1, mock_session_2], ): session_id_1 = await acp_manager.create_session( diff --git a/tests/mcp_server/test_e2e_mcp_capability.py b/tests/mcp_server/test_e2e_mcp_capability.py index 869b72b64..8ccb5fbb0 100644 --- a/tests/mcp_server/test_e2e_mcp_capability.py +++ b/tests/mcp_server/test_e2e_mcp_capability.py @@ -32,11 +32,11 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager -from agentpool_config.mcp_server import AcpMCPServerConfig -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager -from agentpool_server.acp_server.acp_mcp_transport import AcpMcpTransport +from wolfharness.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_config.mcp_server import AcpMCPServerConfig +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness_server.acp_server.acp_mcp_transport import AcpMcpTransport # --------------------------------------------------------------------------- @@ -244,11 +244,11 @@ async def test_initialize_mcp_servers_full_chain() -> None: - Snapshot with ACP config entry allows ``get_capabilities()`` to find the transport. """ - from agentpool import Agent - from agentpool.delegation import AgentPool - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness import Agent + from wolfharness.delegation import AgentPool + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent mcp_manager, acp_manager = _make_wired_managers() session_id = "g3-session" @@ -351,11 +351,11 @@ async def test_tool_execution_through_get_capabilities_and_acp_transport() -> No If ``_forward_to_client`` is broken, ``initialize()`` hangs forever. """ - from agentpool import Agent - from agentpool.delegation import AgentPool - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness import Agent + from wolfharness.delegation import AgentPool + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent mcp_manager, acp_manager = _make_wired_managers() session_id = "g4-session" diff --git a/tests/mcp_server/test_e2e_session_controller.py b/tests/mcp_server/test_e2e_session_controller.py index 6ed772801..a0f94377d 100644 --- a/tests/mcp_server/test_e2e_session_controller.py +++ b/tests/mcp_server/test_e2e_session_controller.py @@ -19,10 +19,10 @@ import pytest -from agentpool.mcp_server.config_snapshot import McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager -from agentpool.orchestrator.session_controller import SessionController, SessionState -from agentpool.sessions import SessionData +from wolfharness.mcp_server.config_snapshot import McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.orchestrator.session_controller import SessionController, SessionState +from wolfharness.sessions import SessionData # ============================================================================ @@ -38,8 +38,8 @@ def _make_mock_pool() -> MagicMock: so that SessionController.get_or_create_session_agent() can resolve a NativeAgentConfig and call cfg.get_agent(). """ - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest mock_pool: MagicMock = MagicMock() mock_pool.manifest = AgentsManifest( @@ -71,9 +71,9 @@ async def test_full_create_session_chain_populates_mcp_session_context() -> None calls agent.mcp.get_or_create_session() and agent.mcp.update_session_snapshot() so that the agent's MCPManager has a McpSessionContext with a non-None snapshot. """ - from agentpool.agents.native_agent import Agent - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.agents.native_agent import Agent + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest # Build a real manifest with a NativeAgentConfig manifest = AgentsManifest( @@ -157,7 +157,7 @@ async def test_session_controller_close_session_with_real_agent_and_mcp_resource close_session() and verifies the session context is cleaned and the agent context is exited. """ - from agentpool.agents.native_agent import Agent + from wolfharness.agents.native_agent import Agent def simple_callback(message: str) -> str: return f"Test: {message}" @@ -226,12 +226,12 @@ async def test_resume_session_with_real_acpsession_not_patched() -> None: then calls resume_session(). The real ACPSession.__post_init__ must run and wire agent.mcp._acp_mcp_manager to acp_agent._mcp_manager. """ - from agentpool import Agent - from agentpool.delegation import AgentPool - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness import Agent + from wolfharness.delegation import AgentPool + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.session_manager import ACPSessionManager def simple_callback(message: str) -> str: return f"Test: {message}" @@ -296,7 +296,7 @@ async def mock_old_close() -> None: ) # Assert: result is a real ACPSession (not a mock) - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession assert isinstance(result, ACPSession), ( "resume_session() must return a real ACPSession, not a mock" diff --git a/tests/mcp_server/test_global_pool.py b/tests/mcp_server/test_global_pool.py index f15e810a7..a8be934ef 100644 --- a/tests/mcp_server/test_global_pool.py +++ b/tests/mcp_server/test_global_pool.py @@ -8,8 +8,8 @@ from pydantic import HttpUrl import pytest -from agentpool.mcp_server.global_pool import GlobalConnectionPool -from agentpool_config.mcp_server import ( +from wolfharness.mcp_server.global_pool import GlobalConnectionPool +from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, diff --git a/tests/mcp_server/test_global_pool_review_fixes.py b/tests/mcp_server/test_global_pool_review_fixes.py index b9df28e34..5ae6bafae 100644 --- a/tests/mcp_server/test_global_pool_review_fixes.py +++ b/tests/mcp_server/test_global_pool_review_fixes.py @@ -14,7 +14,7 @@ import pytest -from agentpool.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability pytestmark = pytest.mark.integration @@ -42,7 +42,7 @@ async def get_tools(self) -> list[Any]: # in the except block that catches get_tools() failures import inspect - from agentpool.agents.native_agent.agent import Agent + from wolfharness.agents.native_agent.agent import Agent source = inspect.getsource(Agent.get_agentlet) assert "logger.warning" in source or "logger.exception" in source, ( diff --git a/tests/mcp_server/test_global_pool_sharing.py b/tests/mcp_server/test_global_pool_sharing.py index 25f4779a7..1a67cf613 100644 --- a/tests/mcp_server/test_global_pool_sharing.py +++ b/tests/mcp_server/test_global_pool_sharing.py @@ -13,7 +13,7 @@ import pytest -from agentpool.mcp_server.global_pool import GlobalConnectionPool +from wolfharness.mcp_server.global_pool import GlobalConnectionPool pytestmark = pytest.mark.unit @@ -58,7 +58,7 @@ async def test_http_transport_not_shared_directly(self) -> None: pool = GlobalConnectionPool() # Create a fake HTTP config - from agentpool_config.mcp_server import StreamableHTTPMCPServerConfig + from wolfharness_config.mcp_server import StreamableHTTPMCPServerConfig config = StreamableHTTPMCPServerConfig( name="test-http", @@ -94,7 +94,7 @@ async def test_stdio_transport_owner_task_manages_lifecycle(self) -> None: """ pool = GlobalConnectionPool() - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness_config.mcp_server import StdioMCPServerConfig config = StdioMCPServerConfig( name="test-stdio", @@ -125,7 +125,7 @@ async def test_two_sessions_share_stdio_without_duplicate_connect(self) -> None: """ pool = GlobalConnectionPool() - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness_config.mcp_server import StdioMCPServerConfig config = StdioMCPServerConfig( name="test-stdio-shared", diff --git a/tests/mcp_server/test_manager_capability.py b/tests/mcp_server/test_manager_capability.py index ecf8d1ea9..df3068d66 100644 --- a/tests/mcp_server/test_manager_capability.py +++ b/tests/mcp_server/test_manager_capability.py @@ -9,8 +9,8 @@ from pydantic_ai.mcp import MCPToolset import pytest -from agentpool.mcp_server.manager import MCPManager, _make_elicitation_handler -from agentpool_config.mcp_server import ( +from wolfharness.mcp_server.manager import MCPManager, _make_elicitation_handler +from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, diff --git a/tests/mcp_server/test_manager_status.py b/tests/mcp_server/test_manager_status.py index ec0e2f065..20314de99 100644 --- a/tests/mcp_server/test_manager_status.py +++ b/tests/mcp_server/test_manager_status.py @@ -20,12 +20,12 @@ import pytest -from agentpool.agents.base_agent import BaseAgent -from agentpool.capabilities.mcp_server_cap import McpServerCap -from agentpool.common_types import MCPServerStatus -from agentpool.mcp_server.client import MCPClient -from agentpool.mcp_server.manager import MCPManager -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.capabilities.mcp_server_cap import McpServerCap +from wolfharness.common_types import MCPServerStatus +from wolfharness.mcp_server.client import MCPClient +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_config.mcp_server import StdioMCPServerConfig pytestmark = pytest.mark.unit @@ -246,7 +246,7 @@ async def test_setup_server_records_failure() -> None: manager = MCPManager() # Force MCPClient construction to raise by patching the import target. # setup_server() imports MCPClient lazily inside the function body. - import agentpool.mcp_server.client as client_mod + import wolfharness.mcp_server.client as client_mod original = client_mod.MCPClient @@ -278,12 +278,12 @@ async def test_setup_server_clears_error_on_retry() -> None: async def _fake_cap_aenter(self: McpServerCap[Any]) -> McpServerCap[Any]: return self - import agentpool.mcp_server.client as client_mod + import wolfharness.mcp_server.client as client_mod original_client = client_mod.MCPClient client_mod.MCPClient = lambda **_kw: mock_client # type: ignore[assignment] try: - import agentpool.capabilities.mcp_server_cap as cap_mod + import wolfharness.capabilities.mcp_server_cap as cap_mod original_cap_aenter = cap_mod.McpServerCap.__aenter__ cap_mod.McpServerCap.__aenter__ = _fake_cap_aenter # type: ignore[assignment] @@ -461,7 +461,7 @@ async def _fake_setup( self.providers.append(cap) return cap - import agentpool.mcp_server.manager as mgr_mod + import wolfharness.mcp_server.manager as mgr_mod original = mgr_mod.MCPManager.setup_server mgr_mod.MCPManager.setup_server = _fake_setup # type: ignore[assignment] @@ -509,7 +509,7 @@ async def _always_fail( self._setup_errors[config.client_id] = "boom" raise RuntimeError("boom") - import agentpool.mcp_server.manager as mgr_mod + import wolfharness.mcp_server.manager as mgr_mod original = mgr_mod.MCPManager.setup_server mgr_mod.MCPManager.setup_server = _always_fail # type: ignore[assignment] diff --git a/tests/mcp_server/test_mcp_persistence.py b/tests/mcp_server/test_mcp_persistence.py index 3b0277041..829e49942 100644 --- a/tests/mcp_server/test_mcp_persistence.py +++ b/tests/mcp_server/test_mcp_persistence.py @@ -17,9 +17,9 @@ from pydantic_ai.mcp import MCPToolset import pytest -from agentpool.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager -from agentpool_config.mcp_server import StdioMCPServerConfig +from wolfharness.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_config.mcp_server import StdioMCPServerConfig pytestmark = pytest.mark.unit @@ -243,7 +243,7 @@ async def hanging_aexit(exc_type, exc_val, exc_tb): # Use a short timeout for the test to avoid waiting 30s import unittest.mock - with unittest.mock.patch("agentpool.mcp_server.manager._MCP_CLEANUP_TIMEOUT", 0.1): + with unittest.mock.patch("wolfharness.mcp_server.manager._MCP_CLEANUP_TIMEOUT", 0.1): # disconnect_all should complete within the timeout, not hang await asyncio.wait_for(manager.disconnect_all(), timeout=5.0) diff --git a/tests/mcp_server/test_mcp_provider_lifecycle.py b/tests/mcp_server/test_mcp_provider_lifecycle.py index 6565a3645..c43f6528f 100644 --- a/tests/mcp_server/test_mcp_provider_lifecycle.py +++ b/tests/mcp_server/test_mcp_provider_lifecycle.py @@ -23,11 +23,11 @@ from pydantic_ai.usage import RunUsage import pytest -from agentpool.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot -from agentpool.mcp_server.global_pool import GlobalConnectionPool -from agentpool.mcp_server.manager import MCPManager -from agentpool.mcp_server.session_pool import SessionConnectionPool -from agentpool_config.mcp_server import ( +from wolfharness.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot +from wolfharness.mcp_server.global_pool import GlobalConnectionPool +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.mcp_server.session_pool import SessionConnectionPool +from wolfharness_config.mcp_server import ( AcpMCPServerConfig, StdioMCPServerConfig, StreamableHTTPMCPServerConfig, diff --git a/tests/mcp_server/test_review_fixes.py b/tests/mcp_server/test_review_fixes.py index 4f1b591cf..3b678d4cd 100644 --- a/tests/mcp_server/test_review_fixes.py +++ b/tests/mcp_server/test_review_fixes.py @@ -27,9 +27,9 @@ import pytest -from agentpool.mcp_server.manager import MCPManager -from agentpool.sessions import SessionData -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.sessions import SessionData +from wolfharness_server.acp_server.session_manager import ACPSessionManager # ============================================================================ @@ -215,7 +215,7 @@ async def mock_get_agent(sid: str, agent_name: str | None = None) -> MagicMock: mock_new_session.register_update_callback = MagicMock() with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", return_value=mock_new_session, ): result = await acp_manager.resume_session( @@ -295,12 +295,12 @@ async def test_acp_session_wires_acp_mcp_manager() -> None: Fix: In ``__post_init__``, after ``self.agent.env = self.acp_env``, wire the manager for native agents. """ - from agentpool import Agent - from agentpool.delegation import AgentPool - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent - from agentpool_server.acp_server.session import ACPSession + from wolfharness import Agent + from wolfharness.delegation import AgentPool + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.session import ACPSession def simple_callback(message: str) -> str: return f"Test: {message}" diff --git a/tests/mcp_server/test_review_fixes_r3.py b/tests/mcp_server/test_review_fixes_r3.py index 799762585..173c48b4c 100644 --- a/tests/mcp_server/test_review_fixes_r3.py +++ b/tests/mcp_server/test_review_fixes_r3.py @@ -20,12 +20,12 @@ import pytest -from agentpool.mcp_server.config_snapshot import McpConfigSnapshot -from agentpool.orchestrator.session_controller import SessionController, SessionState +from wolfharness.mcp_server.config_snapshot import McpConfigSnapshot +from wolfharness.orchestrator.session_controller import SessionController, SessionState if TYPE_CHECKING: - from agentpool.mcp_server.manager import MCPManager + from wolfharness.mcp_server.manager import MCPManager # ============================================================================ @@ -51,9 +51,9 @@ async def test_get_or_create_session_agent_does_not_recreate_cleaned_parent_sess 6. Call get_or_create_session_agent(child_id). 7. Assert parent_id has NO session context (no phantom created). """ - from agentpool.agents.native_agent import Agent - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.agents.native_agent import Agent + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest def simple_callback(message: str) -> str: return f"Test: {message}" @@ -140,9 +140,9 @@ async def test_get_or_create_session_agent_reads_parent_snapshot_without_leaking This test passes both before and after the fix — it guards against regressions where the parent context is accidentally recreated. """ - from agentpool.agents.native_agent import Agent - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.agents.native_agent import Agent + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest def simple_callback(message: str) -> str: return f"Test: {message}" diff --git a/tests/mcp_server/test_session_close_integration.py b/tests/mcp_server/test_session_close_integration.py index 25a7676c7..ec4fde0fc 100644 --- a/tests/mcp_server/test_session_close_integration.py +++ b/tests/mcp_server/test_session_close_integration.py @@ -9,8 +9,8 @@ import pytest -from agentpool.mcp_server.config_snapshot import McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager +from wolfharness.mcp_server.config_snapshot import McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager @pytest.mark.integration diff --git a/tests/mcp_server/test_session_lifecycle.py b/tests/mcp_server/test_session_lifecycle.py index ee89f56b9..cc0d85005 100644 --- a/tests/mcp_server/test_session_lifecycle.py +++ b/tests/mcp_server/test_session_lifecycle.py @@ -12,8 +12,8 @@ import pytest -from agentpool.mcp_server.config_snapshot import McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager, McpSessionContext +from wolfharness.mcp_server.config_snapshot import McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager, McpSessionContext # --------------------------------------------------------------------------- diff --git a/tests/mcp_server/test_session_pool.py b/tests/mcp_server/test_session_pool.py index 69b714c7a..46e55cb5e 100644 --- a/tests/mcp_server/test_session_pool.py +++ b/tests/mcp_server/test_session_pool.py @@ -9,12 +9,12 @@ from pydantic import HttpUrl import pytest -from agentpool.mcp_server.session_pool import ( +from wolfharness.mcp_server.session_pool import ( SessionConnectionPool, _create_transport, _stdio_owner_task, ) -from agentpool_config.mcp_server import ( +from wolfharness_config.mcp_server import ( AcpMCPServerConfig, SSEMCPServerConfig, StdioMCPServerConfig, @@ -97,7 +97,7 @@ async def test_get_transport_sse_caches( ) -> None: """get_transport creates and caches transport for SSE config.""" fake = _FakeTransport("sse") - with patch("agentpool.mcp_server.session_pool._create_transport", return_value=fake): + with patch("wolfharness.mcp_server.session_pool._create_transport", return_value=fake): t1 = await session_pool.get_transport(sse_config) t2 = await session_pool.get_transport(sse_config) @@ -113,7 +113,7 @@ async def test_get_transport_http_caches( ) -> None: """get_transport creates and caches transport for HTTP config.""" fake = _FakeTransport("http") - with patch("agentpool.mcp_server.session_pool._create_transport", return_value=fake): + with patch("wolfharness.mcp_server.session_pool._create_transport", return_value=fake): t1 = await session_pool.get_transport(http_config) t2 = await session_pool.get_transport(http_config) @@ -127,7 +127,7 @@ async def test_get_transport_stdio( ) -> None: """get_transport for stdio spawns an owner task and waits for ready.""" fake = _FakeTransport("stdio") - with patch("agentpool.mcp_server.session_pool._create_transport", return_value=fake): + with patch("wolfharness.mcp_server.session_pool._create_transport", return_value=fake): transport = await session_pool.get_transport(stdio_config) assert transport is fake @@ -166,7 +166,7 @@ def _create_fake(config: Any) -> _FakeTransport: return fake1 if call_count == 1 else fake2 with patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", side_effect=_create_fake, ): t1 = await session_pool.get_transport(sse_config, skill_name="skill-a") @@ -189,7 +189,7 @@ async def test_same_skill_name_shares_transport( ) -> None: """Same (client_id, skill_name) pair returns cached transport.""" fake = _FakeTransport("shared") - with patch("agentpool.mcp_server.session_pool._create_transport", return_value=fake): + with patch("wolfharness.mcp_server.session_pool._create_transport", return_value=fake): t1 = await session_pool.get_transport(sse_config, skill_name="my-skill") t2 = await session_pool.get_transport(sse_config, skill_name="my-skill") @@ -285,7 +285,7 @@ def _create_fake(config: Any) -> _FakeTransport: return fake1 if call_count == 1 else fake2 with patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", side_effect=_create_fake, ): await session_pool.get_transport(sse_config) @@ -305,7 +305,7 @@ async def test_cleanup_signals_stdio_owner_tasks( ) -> None: """Cleanup signals close_event for stdio owner tasks and waits for completion.""" fake = _FakeTransport("stdio") - with patch("agentpool.mcp_server.session_pool._create_transport", return_value=fake): + with patch("wolfharness.mcp_server.session_pool._create_transport", return_value=fake): await session_pool.get_transport(stdio_config) key = (stdio_config.client_id, None) @@ -326,7 +326,7 @@ async def test_cleanup_idempotent( ) -> None: """Cleanup can be called multiple times safely.""" fake = _FakeTransport("sse") - with patch("agentpool.mcp_server.session_pool._create_transport", return_value=fake): + with patch("wolfharness.mcp_server.session_pool._create_transport", return_value=fake): await session_pool.get_transport(sse_config) await session_pool.cleanup() @@ -353,7 +353,7 @@ def _create_fake(config: Any) -> _FakeTransport: return fake1 if call_count == 1 else fake2 with patch( - "agentpool.mcp_server.session_pool._create_transport", + "wolfharness.mcp_server.session_pool._create_transport", side_effect=_create_fake, ): await session_pool.get_transport(stdio_config) @@ -431,7 +431,7 @@ async def test_copy_pre_created_transports_copies_acp_only( await source.add_transport("acp-1", acp_transport) # Simulate a non-pre-created transport by inserting directly - from agentpool.mcp_server.session_pool import _SessionConnection + from wolfharness.mcp_server.session_pool import _SessionConnection source._connections[("http-1", None)] = _SessionConnection( transport=http_transport, diff --git a/tests/mcp_server/test_session_wiring_integration.py b/tests/mcp_server/test_session_wiring_integration.py index 21f2f14b0..f12336307 100644 --- a/tests/mcp_server/test_session_wiring_integration.py +++ b/tests/mcp_server/test_session_wiring_integration.py @@ -22,10 +22,10 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool.mcp_server.manager import MCPManager -from agentpool.sessions import SessionData -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.sessions import SessionData +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness_server.acp_server.session_manager import ACPSessionManager # ============================================================================ @@ -162,12 +162,12 @@ async def test_acp_session_post_init_wires_acp_mcp_manager() -> None: ``__post_init__`` must wire ``agent.mcp._acp_mcp_manager`` to ``acp_agent._mcp_manager`` so that cleanup delegation works. """ - from agentpool import Agent - from agentpool.delegation import AgentPool - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent - from agentpool_server.acp_server.session import ACPSession + from wolfharness import Agent + from wolfharness.delegation import AgentPool + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.session import ACPSession def simple_callback(message: str) -> str: return f"Test: {message}" @@ -211,12 +211,12 @@ async def test_acp_session_close_full_delegation_chain() -> None: up the session context registry, ``_session_connections``, and the connection's active sessions. """ - from agentpool import Agent - from agentpool.delegation import AgentPool - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent - from agentpool_server.acp_server.session import ACPSession + from wolfharness import Agent + from wolfharness.delegation import AgentPool + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.session import ACPSession mcp_manager, acp_manager = _make_wired_managers() session_id = "test-close-chain-1" @@ -648,7 +648,7 @@ def _capture_session(*args: object, **kwargs: object) -> AsyncMock: return mock_new_session with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", side_effect=_capture_session, ): result = await acp_manager.resume_session( diff --git a/tests/mcp_server/test_session_wiring_integration_part2.py b/tests/mcp_server/test_session_wiring_integration_part2.py index 84a9bde95..f63aaaf1d 100644 --- a/tests/mcp_server/test_session_wiring_integration_part2.py +++ b/tests/mcp_server/test_session_wiring_integration_part2.py @@ -27,16 +27,16 @@ import pytest from acp.schema.mcp import AcpMcpServer -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.mcp_server.manager import MCPManager -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool.sessions import SessionData -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager -from agentpool_server.acp_server.session import ACPSession -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.mcp_server.manager import MCPManager +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness.sessions import SessionData +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnectionManager +from wolfharness_server.acp_server.session import ACPSession +from wolfharness_server.acp_server.session_manager import ACPSessionManager # --------------------------------------------------------------------------- @@ -264,7 +264,7 @@ async def mock_get_agent(sid: str, agent_name: str | None = None) -> MagicMock: mock_new_session.register_update_callback = MagicMock() with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", return_value=mock_new_session, ): result = await acp_manager.resume_session( @@ -536,7 +536,7 @@ async def mock_get_agent(sid: str, agent_name: str | None = None) -> MagicMock: mock_new_session.register_update_callback = MagicMock() with patch( - "agentpool_server.acp_server.session_manager.ACPSession", + "wolfharness_server.acp_server.session_manager.ACPSession", return_value=mock_new_session, ): result = await acp_manager.resume_session( diff --git a/tests/mcp_server/test_stale_mcp_connection.py b/tests/mcp_server/test_stale_mcp_connection.py index c334758bb..306cc3c72 100644 --- a/tests/mcp_server/test_stale_mcp_connection.py +++ b/tests/mcp_server/test_stale_mcp_connection.py @@ -23,9 +23,9 @@ import pytest -from agentpool.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot -from agentpool.mcp_server.manager import MCPManager -from agentpool_config.mcp_server import AcpMCPServerConfig +from wolfharness.mcp_server.config_snapshot import McpConfigEntry, McpConfigSnapshot +from wolfharness.mcp_server.manager import MCPManager +from wolfharness_config.mcp_server import AcpMCPServerConfig # --------------------------------------------------------------------------- @@ -199,7 +199,7 @@ async def test_session_pool_provides_fresh_transport_on_resume() -> None: returns the fresh transport. With per-session contexts, each session's pool is isolated from the others. """ - from agentpool.mcp_server.session_pool import SessionConnectionPool + from wolfharness.mcp_server.session_pool import SessionConnectionPool acp_config = AcpMCPServerConfig(name="scratchpad", acp_id="acp-server-1") client_id = acp_config.client_id diff --git a/tests/mcp_server/test_tool_bridge_conversion.py b/tests/mcp_server/test_tool_bridge_conversion.py index 417b59a90..e5d46176e 100644 --- a/tests/mcp_server/test_tool_bridge_conversion.py +++ b/tests/mcp_server/test_tool_bridge_conversion.py @@ -3,7 +3,7 @@ from pydantic_ai import ToolReturn import pytest -from agentpool.mcp_server.tool_bridge import _convert_to_tool_result +from wolfharness.mcp_server.tool_bridge import _convert_to_tool_result pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_adapters.py b/tests/messaging/test_adapters.py index 4bdb9cbf8..8ec592a7e 100644 --- a/tests/messaging/test_adapters.py +++ b/tests/messaging/test_adapters.py @@ -14,22 +14,22 @@ from pydantic_graph.id_types import ForkStack, NodeID, TaskID import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartStartEvent, RunErrorEvent, RunStartedEvent, StreamCompleteEvent, SubAgentEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.messaging.messagenode import MessageNode -from agentpool.messaging.messages import ChatMessage as Msg -from agentpool.messaging.signal_adapter import SignalEmittingGraphRun -from agentpool.messaging.streaming_adapter import ( +from wolfharness.messaging import ChatMessage +from wolfharness.messaging.messagenode import MessageNode +from wolfharness.messaging.messages import ChatMessage as Msg +from wolfharness.messaging.signal_adapter import SignalEmittingGraphRun +from wolfharness.messaging.streaming_adapter import ( GraphStreamingAdapter, adapt_graph_run, ) -from agentpool.talk import Talk +from wolfharness.talk import Talk pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_agent_piping.py b/tests/messaging/test_agent_piping.py index c5b2fa395..5fdf79a3e 100644 --- a/tests/messaging/test_agent_piping.py +++ b/tests/messaging/test_agent_piping.py @@ -5,9 +5,9 @@ import anyio import pytest -from agentpool import Agent -from agentpool.messaging import AgentResponse -from agentpool.talk.talk import Talk +from wolfharness import Agent +from wolfharness.messaging import AgentResponse +from wolfharness.talk.talk import Talk pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_agent_signals.py b/tests/messaging/test_agent_signals.py index 2a0b06dc2..f0b3b68b1 100644 --- a/tests/messaging/test_agent_signals.py +++ b/tests/messaging/test_agent_signals.py @@ -5,8 +5,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.messaging.message_utils import build_message_index, get_message_chain +from wolfharness import Agent +from wolfharness.messaging.message_utils import build_message_index, get_message_chain pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_compaction.py b/tests/messaging/test_compaction.py index b5efbe771..21c0122c1 100644 --- a/tests/messaging/test_compaction.py +++ b/tests/messaging/test_compaction.py @@ -14,7 +14,7 @@ ) import pytest -from agentpool.messaging.compaction import ( +from wolfharness.messaging.compaction import ( CompactionPipeline, FilterEmptyMessages, FilterRetryPrompts, @@ -29,7 +29,7 @@ balanced_context, minimal_context, ) -from agentpool_config.compaction import ( +from wolfharness_config.compaction import ( CompactionConfig, FilterThinkingConfig, KeepLastMessagesConfig, diff --git a/tests/messaging/test_connection_registry.py b/tests/messaging/test_connection_registry.py index 82eb189b2..20a59d523 100644 --- a/tests/messaging/test_connection_registry.py +++ b/tests/messaging/test_connection_registry.py @@ -3,9 +3,9 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest +from wolfharness import Agent, AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest pytestmark = pytest.mark.integration diff --git a/tests/messaging/test_conversation.py b/tests/messaging/test_conversation.py index 9636a7a24..c8a97b57e 100644 --- a/tests/messaging/test_conversation.py +++ b/tests/messaging/test_conversation.py @@ -2,7 +2,7 @@ import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_debug_taskgroup.py b/tests/messaging/test_debug_taskgroup.py index 8249ed80f..9f9f3a995 100644 --- a/tests/messaging/test_debug_taskgroup.py +++ b/tests/messaging/test_debug_taskgroup.py @@ -8,7 +8,7 @@ import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_event_converter.py b/tests/messaging/test_event_converter.py index 13560f444..2d700e82d 100644 --- a/tests/messaging/test_event_converter.py +++ b/tests/messaging/test_event_converter.py @@ -10,8 +10,8 @@ import pytest from acp.schema import AgentMessageChunk, ToolCallProgress, TurnCompleteUpdate -from agentpool.agents.events import RunFailedEvent, ToolCallStartEvent -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events import RunFailedEvent, ToolCallStartEvent +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = pytest.mark.integration diff --git a/tests/messaging/test_event_manager.py b/tests/messaging/test_event_manager.py index ce2bf0220..2187fda87 100644 --- a/tests/messaging/test_event_manager.py +++ b/tests/messaging/test_event_manager.py @@ -6,8 +6,8 @@ from evented_config import TimeEventConfig import pytest -from agentpool.messaging import EventManager -from agentpool.utils.time_utils import get_now +from wolfharness.messaging import EventManager +from wolfharness.utils.time_utils import get_now pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_event_routing_scenarios.py b/tests/messaging/test_event_routing_scenarios.py index 00d866813..d3ba4afa0 100644 --- a/tests/messaging/test_event_routing_scenarios.py +++ b/tests/messaging/test_event_routing_scenarios.py @@ -7,8 +7,8 @@ import pytest -from agentpool.agents.events import RunStartedEvent, SubAgentEvent -from agentpool.messaging.event_manager import EventManager +from wolfharness.agents.events import RunStartedEvent, SubAgentEvent +from wolfharness.messaging.event_manager import EventManager pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_message_tracker.py b/tests/messaging/test_message_tracker.py index 9d0ab6648..422012f0c 100644 --- a/tests/messaging/test_message_tracker.py +++ b/tests/messaging/test_message_tracker.py @@ -7,9 +7,9 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool, BaseTeam -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest +from wolfharness import Agent, AgentPool, BaseTeam +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest pytestmark = pytest.mark.integration @@ -18,9 +18,9 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool.agents.base_agent import BaseAgent - from agentpool.messaging import ChatMessage - from agentpool.orchestrator.core import SessionPool + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.messaging import ChatMessage + from wolfharness.orchestrator.core import SessionPool def _make_pool() -> AgentPool: diff --git a/tests/messaging/test_messagenode_bind_pool.py b/tests/messaging/test_messagenode_bind_pool.py index 4ae4d4495..23016df26 100644 --- a/tests/messaging/test_messagenode_bind_pool.py +++ b/tests/messaging/test_messagenode_bind_pool.py @@ -6,12 +6,12 @@ import pytest -from agentpool.messaging import ChatMessage -from agentpool.messaging.messagenode import MessageNode +from wolfharness.messaging import ChatMessage +from wolfharness.messaging.messagenode import MessageNode if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool class ConcreteMessageNode(MessageNode[Any, Any]): @@ -66,7 +66,7 @@ def test_bind_pool_does_not_emit_deprecation_warning(minimal_pool: AgentPool) -> @pytest.mark.unit def test_host_context_returns_non_none_after_bind_pool() -> None: """After _bind_pool(), host_context returns a non-None HostContext.""" - from agentpool.delegation.pool import AgentPool + from wolfharness.delegation.pool import AgentPool pool = AgentPool() # type: ignore[type-arg] node = ConcreteMessageNode(name="test_node") diff --git a/tests/messaging/test_messagenode_cleanup.py b/tests/messaging/test_messagenode_cleanup.py index d51b5a17b..c42e32c9b 100644 --- a/tests/messaging/test_messagenode_cleanup.py +++ b/tests/messaging/test_messagenode_cleanup.py @@ -7,8 +7,8 @@ import pytest -from agentpool.messaging.messagenode import MessageNode -from agentpool.talk.stats import AggregatedMessageStats, MessageStats +from wolfharness.messaging.messagenode import MessageNode +from wolfharness.talk.stats import AggregatedMessageStats, MessageStats pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_messagenode_events.py b/tests/messaging/test_messagenode_events.py index 14a4a82c9..efc0d0464 100644 --- a/tests/messaging/test_messagenode_events.py +++ b/tests/messaging/test_messagenode_events.py @@ -4,9 +4,9 @@ from pydantic_ai import TextPartDelta import pytest -from agentpool.agents.events import PartDeltaEvent -from agentpool.messaging import ChatMessage -from agentpool.messaging.messagenode import MessageNode +from wolfharness.agents.events import PartDeltaEvent +from wolfharness.messaging import ChatMessage +from wolfharness.messaging.messagenode import MessageNode pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_minimal_piping.py b/tests/messaging/test_minimal_piping.py index 9db7e7ab1..bbf438b8b 100644 --- a/tests/messaging/test_minimal_piping.py +++ b/tests/messaging/test_minimal_piping.py @@ -2,7 +2,7 @@ import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_parent_id.py b/tests/messaging/test_parent_id.py index 8bb8d3afd..47de826e0 100644 --- a/tests/messaging/test_parent_id.py +++ b/tests/messaging/test_parent_id.py @@ -5,7 +5,7 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, ChatMessage +from wolfharness import Agent, ChatMessage pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_runners.py b/tests/messaging/test_runners.py index 716647fda..ee9d00f15 100644 --- a/tests/messaging/test_runners.py +++ b/tests/messaging/test_runners.py @@ -5,7 +5,7 @@ from pydantic import BaseModel import pytest -from agentpool import AgentPool, AgentsManifest +from wolfharness import AgentPool, AgentsManifest pytestmark = pytest.mark.integration diff --git a/tests/messaging/test_signal_forwarding.py b/tests/messaging/test_signal_forwarding.py index 47b3297ce..5d875840e 100644 --- a/tests/messaging/test_signal_forwarding.py +++ b/tests/messaging/test_signal_forwarding.py @@ -5,7 +5,7 @@ import pytest -from agentpool import Agent, AgentPool, AgentsManifest +from wolfharness import Agent, AgentPool, AgentsManifest pytestmark = pytest.mark.integration diff --git a/tests/messaging/test_source_type.py b/tests/messaging/test_source_type.py index a3dcc1683..3d253330a 100644 --- a/tests/messaging/test_source_type.py +++ b/tests/messaging/test_source_type.py @@ -7,8 +7,8 @@ import pytest -from agentpool.messaging import ChatMessage, MessageNode -from agentpool.messaging.messagenode import SourceType, get_source_type +from wolfharness.messaging import ChatMessage, MessageNode +from wolfharness.messaging.messagenode import SourceType, get_source_type pytestmark = pytest.mark.unit @@ -36,7 +36,7 @@ def test_source_type_literal_values() -> None: @pytest.mark.real_model def test_get_source_type_native_agent() -> None: """Native Agent instances should return 'agent'.""" - from agentpool.agents import Agent + from wolfharness.agents import Agent agent = Agent(name="test_agent", model="openai:gpt-4o-mini") assert get_source_type(agent) == "agent" @@ -45,8 +45,8 @@ def test_get_source_type_native_agent() -> None: @pytest.mark.real_model def test_get_source_type_team() -> None: """Team (parallel) instances should return 'team_parallel'.""" - from agentpool.agents import Agent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents import Agent + from wolfharness.delegation.base_team import BaseTeam agent_a = Agent(name="a", model="openai:gpt-4o-mini") agent_b = Agent(name="b", model="openai:gpt-4o-mini") @@ -57,8 +57,8 @@ def test_get_source_type_team() -> None: @pytest.mark.real_model def test_get_source_type_teamrun() -> None: """TeamRun (sequential) instances should return 'team_sequential'.""" - from agentpool.agents import Agent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents import Agent + from wolfharness.delegation.base_team import BaseTeam agent_a = Agent(name="a", model="openai:gpt-4o-mini") agent_b = Agent(name="b", model="openai:gpt-4o-mini") @@ -69,7 +69,7 @@ def test_get_source_type_teamrun() -> None: def test_get_source_type_unknown_subclass_defaults_to_agent() -> None: """Unknown MessageNode subclasses should default to 'agent' with a warning.""" stub = StubMessageNode(name="stub") - with patch("agentpool.messaging.messagenode.logger"): + with patch("wolfharness.messaging.messagenode.logger"): result = get_source_type(stub) assert result == "agent" # No warning for valid MessageNode subclass — it's just not Team/BaseTeam @@ -79,7 +79,7 @@ def test_get_source_type_unknown_subclass_defaults_to_agent() -> None: @pytest.mark.real_model def test_agent_type_property_on_agent() -> None: """MessageNode.agent_type on a native Agent returns persistence value.""" - from agentpool.agents import Agent + from wolfharness.agents import Agent agent = Agent(name="test_agent", model="openai:gpt-4o-mini") assert agent.agent_type == "agent" @@ -88,8 +88,8 @@ def test_agent_type_property_on_agent() -> None: @pytest.mark.real_model def test_agent_type_property_on_team() -> None: """MessageNode.agent_type on a Team returns the source_type value.""" - from agentpool.agents import Agent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents import Agent + from wolfharness.delegation.base_team import BaseTeam agent_a = Agent(name="a", model="openai:gpt-4o-mini") agent_b = Agent(name="b", model="openai:gpt-4o-mini") @@ -100,8 +100,8 @@ def test_agent_type_property_on_team() -> None: @pytest.mark.real_model def test_agent_type_property_on_teamrun() -> None: """MessageNode.agent_type on a TeamRun returns the source_type value.""" - from agentpool.agents import Agent - from agentpool.delegation.base_team import BaseTeam + from wolfharness.agents import Agent + from wolfharness.delegation.base_team import BaseTeam agent_a = Agent(name="a", model="openai:gpt-4o-mini") agent_b = Agent(name="b", model="openai:gpt-4o-mini") @@ -114,11 +114,11 @@ def test_circular_import_safety() -> None: import importlib # Force re-import of messagenode to verify no circular import - mod = importlib.import_module("agentpool.messaging.messagenode") + mod = importlib.import_module("wolfharness.messaging.messagenode") importlib.reload(mod) # Verify the module still exports the expected symbols assert hasattr(mod, "SourceType") assert hasattr(mod, "get_source_type") - importlib.import_module("agentpool.delegation.base_team") + importlib.import_module("wolfharness.delegation.base_team") diff --git a/tests/messaging/test_talks.py b/tests/messaging/test_talks.py index 338c342d4..c6511a92b 100644 --- a/tests/messaging/test_talks.py +++ b/tests/messaging/test_talks.py @@ -3,8 +3,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.talk.talk import Talk, TeamTalk +from wolfharness import Agent +from wolfharness.talk.talk import Talk, TeamTalk pytestmark = pytest.mark.unit @@ -13,7 +13,7 @@ async def test_basic_single_connection(): """Test basic message forwarding between two agents.""" async with ( - Agent[str](model="test", name="agentpool") as source, + Agent[str](model="test", name="wolfharness") as source, Agent[str](model="test") as target, ): # Create explicit connection diff --git a/tests/messaging/test_thread_hypothesis.py b/tests/messaging/test_thread_hypothesis.py index 2354302b0..c04ef8e0e 100644 --- a/tests/messaging/test_thread_hypothesis.py +++ b/tests/messaging/test_thread_hypothesis.py @@ -3,7 +3,7 @@ from pydantic_ai._utils import disable_threads import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit diff --git a/tests/messaging/test_tool_display_acp_diff.py b/tests/messaging/test_tool_display_acp_diff.py index b04aa3d53..22334770f 100644 --- a/tests/messaging/test_tool_display_acp_diff.py +++ b/tests/messaging/test_tool_display_acp_diff.py @@ -11,8 +11,8 @@ import pytest from acp.schema import FileEditToolCallContent, ToolCallProgress -from agentpool.agents.events import DiffContentItem, ToolCallProgressEvent, ToolCallStartEvent -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events import DiffContentItem, ToolCallProgressEvent, ToolCallStartEvent +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = pytest.mark.integration diff --git a/tests/observability/test_observability.py b/tests/observability/test_observability.py index a739f3691..04158eb7f 100644 --- a/tests/observability/test_observability.py +++ b/tests/observability/test_observability.py @@ -6,8 +6,8 @@ from pydantic import SecretStr import pytest -from agentpool.observability.observability_registry import ObservabilityRegistry -from agentpool_config.observability import ( +from wolfharness.observability.observability_registry import ObservabilityRegistry +from wolfharness_config.observability import ( CustomObservabilityConfig, LangsmithObservabilityConfig, LogfireObservabilityConfig, diff --git a/tests/observability/test_observability_integration.py b/tests/observability/test_observability_integration.py index e90f325c9..415b8b49b 100644 --- a/tests/observability/test_observability_integration.py +++ b/tests/observability/test_observability_integration.py @@ -4,8 +4,8 @@ import pytest -from agentpool import AgentPool, AgentsManifest -from agentpool.observability import registry +from wolfharness import AgentPool, AgentsManifest +from wolfharness.observability import registry pytestmark = pytest.mark.integration diff --git a/tests/observability/test_span_hierarchy.py b/tests/observability/test_span_hierarchy.py index 39d55d160..80a1c230f 100644 --- a/tests/observability/test_span_hierarchy.py +++ b/tests/observability/test_span_hierarchy.py @@ -232,7 +232,7 @@ def test_background_task_span( This simulates the ``with logfire.span()`` inside the ``_safe_background_run()`` nested function in - ``agentpool_toolsets/builtin/subagent_tools.py``. + ``wolfharness_toolsets/builtin/subagent_tools.py``. """ tracer, exporter = in_memory_tracer diff --git a/tests/observability/test_tracing_optimization.py b/tests/observability/test_tracing_optimization.py index cc6ac9416..29f54d2a0 100644 --- a/tests/observability/test_tracing_optimization.py +++ b/tests/observability/test_tracing_optimization.py @@ -21,7 +21,7 @@ class TestOtelLogProcessor: def test_calls_logfire_log_without_creating_span(self): """_otel_log_processor calls logfire.log() but does not create a span.""" - from agentpool.log import _otel_log_processor + from wolfharness.log import _otel_log_processor event_dict: dict[str, object] = { "event": "test message", @@ -29,7 +29,7 @@ def test_calls_logfire_log_without_creating_span(self): "session_id": "s123", } - with patch("agentpool.log.logfire") as mock_logfire: + with patch("wolfharness.log.logfire") as mock_logfire: _otel_log_processor(None, "info", event_dict) mock_logfire.log.assert_called_once() @@ -43,14 +43,14 @@ def test_calls_logfire_log_without_creating_span(self): def test_returns_event_dict_unchanged(self): """_otel_log_processor returns event_dict unchanged for downstream renderers.""" - from agentpool.log import _otel_log_processor + from wolfharness.log import _otel_log_processor event_dict: dict[str, object] = { "event": "test message", "level": "info", } - with patch("agentpool.log.logfire"): + with patch("wolfharness.log.logfire"): result = _otel_log_processor(None, "info", event_dict) # event_dict must still have "event" key for renderer @@ -60,11 +60,11 @@ def test_returns_event_dict_unchanged(self): def test_handles_missing_event_key(self): """_otel_log_processor handles missing 'event' key gracefully.""" - from agentpool.log import _otel_log_processor + from wolfharness.log import _otel_log_processor event_dict: dict[str, object] = {"level": "debug"} - with patch("agentpool.log.logfire") as mock_logfire: + with patch("wolfharness.log.logfire") as mock_logfire: _otel_log_processor(None, "debug", event_dict) # Should fall back to method_name as msg @@ -73,11 +73,11 @@ def test_handles_missing_event_key(self): def test_does_not_raise_on_logfire_error(self): """_otel_log_processor swallows logfire errors to never break logging.""" - from agentpool.log import _otel_log_processor + from wolfharness.log import _otel_log_processor event_dict: dict[str, object] = {"event": "test", "level": "info"} - with patch("agentpool.log.logfire") as mock_logfire: + with patch("wolfharness.log.logfire") as mock_logfire: mock_logfire.log.side_effect = RuntimeError("OTel not configured") # Should not raise result = _otel_log_processor(None, "info", event_dict) @@ -91,7 +91,7 @@ class TestObservabilityConfig: def test_new_fields_exist_with_correct_defaults(self): """BaseObservabilityConfig has instrument_* fields with correct defaults.""" - from agentpool_config.observability import CustomObservabilityConfig + from wolfharness_config.observability import CustomObservabilityConfig config = CustomObservabilityConfig(type="custom", endpoint="http://localhost:4318") assert config.instrument_pydantic_ai is False @@ -100,7 +100,7 @@ def test_new_fields_exist_with_correct_defaults(self): def test_fields_can_be_overridden(self): """instrument_* fields can be overridden via config.""" - from agentpool_config.observability import CustomObservabilityConfig + from wolfharness_config.observability import CustomObservabilityConfig config = CustomObservabilityConfig( type="custom", @@ -120,7 +120,7 @@ class TestObservabilityRegistry: def test_is_configured_returns_false_before_configuration(self): """is_configured() returns False before configure_observability() is called.""" - from agentpool.observability.observability_registry import ( + from wolfharness.observability.observability_registry import ( ObservabilityRegistry, ) @@ -129,7 +129,7 @@ def test_is_configured_returns_false_before_configuration(self): def test_config_returns_none_before_configuration(self): """Config property returns None before configuration.""" - from agentpool.observability.observability_registry import ( + from wolfharness.observability.observability_registry import ( ObservabilityRegistry, ) @@ -138,10 +138,10 @@ def test_config_returns_none_before_configuration(self): def test_conditional_instrumentation(self): """configure_observability respects instrument_* config fields.""" - from agentpool.observability.observability_registry import ( + from wolfharness.observability.observability_registry import ( ObservabilityRegistry, ) - from agentpool_config.observability import ( + from wolfharness_config.observability import ( CustomObservabilityConfig, ObservabilityConfig, ) @@ -156,8 +156,8 @@ def test_conditional_instrumentation(self): registry = ObservabilityRegistry() with ( - patch("agentpool.observability.observability_registry.logfire") as mock_lf, - patch("agentpool.observability.observability_registry._setup_otel_environment"), + patch("wolfharness.observability.observability_registry.logfire") as mock_lf, + patch("wolfharness.observability.observability_registry._setup_otel_environment"), ): registry.configure_observability(config) @@ -172,10 +172,10 @@ def test_conditional_instrumentation(self): def test_conditional_instrumentation_enabled(self): """configure_observability calls instrumentation when enabled.""" - from agentpool.observability.observability_registry import ( + from wolfharness.observability.observability_registry import ( ObservabilityRegistry, ) - from agentpool_config.observability import ( + from wolfharness_config.observability import ( CustomObservabilityConfig, ObservabilityConfig, ) @@ -190,8 +190,8 @@ def test_conditional_instrumentation_enabled(self): registry = ObservabilityRegistry() with ( - patch("agentpool.observability.observability_registry.logfire") as mock_lf, - patch("agentpool.observability.observability_registry._setup_otel_environment"), + patch("wolfharness.observability.observability_registry.logfire") as mock_lf, + patch("wolfharness.observability.observability_registry._setup_otel_environment"), ): registry.configure_observability(config) @@ -210,7 +210,7 @@ class TestPerRunTrace: def test_run_handle_has_run_span_fields(self): """RunHandle has _run_span and _run_context fields (default None).""" - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle handle = RunHandle(run_id="test", session_id="s1", agent_type="native") assert handle._run_span is None @@ -218,14 +218,14 @@ def test_run_handle_has_run_span_fields(self): def test_session_state_no_longer_has_trace_context(self): """SessionState no longer has trace_context field (removed in per-run design).""" - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.orchestrator.session_controller import SessionState state = SessionState(session_id="test", agent_name="test") assert not hasattr(state, "trace_context") def test_session_controller_no_longer_has_get_trace_context(self): """SessionController no longer has get_trace_context method.""" - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController assert not hasattr(SessionController, "get_trace_context") diff --git a/tests/orchestrator/test_aexit_hang.py b/tests/orchestrator/test_aexit_hang.py index bd70b2a1d..334529c98 100644 --- a/tests/orchestrator/test_aexit_hang.py +++ b/tests/orchestrator/test_aexit_hang.py @@ -159,7 +159,7 @@ async def test_mcp_cleanup_session_has_timeout() -> None: If MCP cleanup hangs (due to proxy), cleanup_session should not hang forever. It should timeout and allow the session to close. """ - from agentpool.mcp_server.manager import MCPManager, McpSessionContext + from wolfharness.mcp_server.manager import MCPManager, McpSessionContext manager = MCPManager() @@ -171,7 +171,7 @@ async def test_mcp_cleanup_session_has_timeout() -> None: manager._session_contexts["test-session-hang"] = mock_ctx # Patch the cleanup timeout to 0.5s so the test doesn't wait 30s - import agentpool.mcp_server.manager as mgr_mod + import wolfharness.mcp_server.manager as mgr_mod original_timeout = mgr_mod._MCP_CLEANUP_TIMEOUT mgr_mod._MCP_CLEANUP_TIMEOUT = 0.5 @@ -207,7 +207,7 @@ async def test_wait_for_completion_does_not_hang_forever() -> None: wait_for_completion should have a default timeout to break this chain. """ - from agentpool.orchestrator.session_controller_runs import SessionControllerRunsMixin + from wolfharness.orchestrator.session_controller_runs import SessionControllerRunsMixin # Create a minimal mock that exercises wait_for_completion mock_controller = type("MockController", (), {})() @@ -267,7 +267,7 @@ async def test_close_session_mcp_step_has_timeout() -> None: # Read the source of _close_session_unlocked and check for timeout import inspect - from agentpool.orchestrator.session_controller_close import SessionControllerCloseMixin + from wolfharness.orchestrator.session_controller_close import SessionControllerCloseMixin source = inspect.getsource(SessionControllerCloseMixin._close_session_unlocked) @@ -332,7 +332,7 @@ async def test_cancel_can_break_through_aexit_hang() -> None: """ import inspect - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle source = inspect.getsource(RunHandle.cancel) diff --git a/tests/orchestrator/test_agent_type_detection.py b/tests/orchestrator/test_agent_type_detection.py index 751495b83..e3a387d75 100644 --- a/tests/orchestrator/test_agent_type_detection.py +++ b/tests/orchestrator/test_agent_type_detection.py @@ -12,8 +12,8 @@ if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.orchestrator.core import SessionController + from wolfharness import AgentPool + from wolfharness.orchestrator.core import SessionController pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_auto_compaction.py b/tests/orchestrator/test_auto_compaction.py index 6c1aacb0a..be9eb3672 100644 --- a/tests/orchestrator/test_auto_compaction.py +++ b/tests/orchestrator/test_auto_compaction.py @@ -19,8 +19,8 @@ ) import pytest -from agentpool.agents.native_agent.checkpoint import CheckpointManager -from agentpool.storage.serialization import messages_adapter +from wolfharness.agents.native_agent.checkpoint import CheckpointManager +from wolfharness.storage.serialization import messages_adapter pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_cancellation.py b/tests/orchestrator/test_cancellation.py index deedd2f02..277937258 100644 --- a/tests/orchestrator/test_cancellation.py +++ b/tests/orchestrator/test_cancellation.py @@ -25,22 +25,22 @@ from pydantic_ai.messages import ModelResponse, TextPart import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunErrorEvent, RunFailedEvent, RunStartedEvent, StreamCompleteEvent, ToolCallStartEvent, ) -from agentpool.lifecycle.types import DeliveryMode -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventEnvelope, SessionPool -from agentpool.orchestrator.turn import Turn +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventEnvelope, SessionPool +from wolfharness.orchestrator.turn import Turn if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.agents.context import AgentRunContext + from wolfharness import AgentPool + from wolfharness.agents.context import AgentRunContext # Reusable ModelMessage for tests that need .messages populated on ChatMessage. _ModelResponse = ModelResponse(parts=[TextPart(content="response")]) @@ -1524,7 +1524,7 @@ async def test_drain_prompt_queue_skips_closing_session() -> None: Unit test: directly call _drain_prompt_queue with is_closing=True and verify no task is created. """ - from agentpool.orchestrator.core import SessionController, SessionState + from wolfharness.orchestrator.core import SessionController, SessionState controller = SessionController.__new__(SessionController) controller._background_tasks = set() @@ -1559,7 +1559,7 @@ async def test_drain_prompt_queue_agent_none_preserves_message() -> None: The fix puts the message back into prompt_queue when agent is None, instead of silently dropping it. """ - from agentpool.orchestrator.core import SessionController, SessionState + from wolfharness.orchestrator.core import SessionController, SessionState controller = SessionController.__new__(SessionController) controller._background_tasks = set() diff --git a/tests/orchestrator/test_config_hash.py b/tests/orchestrator/test_config_hash.py index d098ea3ec..536da0497 100644 --- a/tests/orchestrator/test_config_hash.py +++ b/tests/orchestrator/test_config_hash.py @@ -6,14 +6,14 @@ import pytest -from agentpool.agents.native_agent.checkpoint import compute_agent_config_hash +from wolfharness.agents.native_agent.checkpoint import compute_agent_config_hash pytestmark = pytest.mark.unit if TYPE_CHECKING: - from agentpool.tools.base import Tool + from wolfharness.tools.base import Tool def _make_tool( @@ -24,7 +24,7 @@ def _make_tool( description: str = "A test tool", ) -> Tool: """Helper to create a FunctionTool for testing.""" - from agentpool.tools.base import FunctionTool + from wolfharness.tools.base import FunctionTool return FunctionTool( name=name, diff --git a/tests/orchestrator/test_consume_run_event.py b/tests/orchestrator/test_consume_run_event.py index 23744afab..20848c208 100644 --- a/tests/orchestrator/test_consume_run_event.py +++ b/tests/orchestrator/test_consume_run_event.py @@ -13,16 +13,16 @@ import pytest -from agentpool.agents.events import StreamCompleteEvent, UserMessageInsertedEvent -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import SessionController, SessionState +from wolfharness.agents.events import StreamCompleteEvent, UserMessageInsertedEvent +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import SessionController, SessionState if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.orchestrator.run import RunHandle + from wolfharness import AgentPool + from wolfharness.orchestrator.run import RunHandle pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_deferred_timeout.py b/tests/orchestrator/test_deferred_timeout.py index e7d9b1e84..1f5c6773a 100644 --- a/tests/orchestrator/test_deferred_timeout.py +++ b/tests/orchestrator/test_deferred_timeout.py @@ -10,8 +10,8 @@ import pytest -from agentpool.orchestrator.core import SessionController -from agentpool.sessions.models import PendingDeferredCall, SessionData +from wolfharness.orchestrator.core import SessionController +from wolfharness.sessions.models import PendingDeferredCall, SessionData pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_deprecation.py b/tests/orchestrator/test_deprecation.py index 1d71b1c0f..273d9593e 100644 --- a/tests/orchestrator/test_deprecation.py +++ b/tests/orchestrator/test_deprecation.py @@ -19,7 +19,7 @@ import pytest -from agentpool.capabilities.runloop_delegation import RunLoopDelegationService +from wolfharness.capabilities.runloop_delegation import RunLoopDelegationService pytestmark = pytest.mark.unit @@ -33,8 +33,8 @@ @pytest.mark.anyio async def test_delegation_service_spawn_subagent_deprecation() -> None: """RunLoopDelegationService.spawn_subagent() emits DeprecationWarning.""" - from agentpool.agents.events.events import StreamCompleteEvent - from agentpool.orchestrator.core import EventBus + from wolfharness.agents.events.events import StreamCompleteEvent + from wolfharness.orchestrator.core import EventBus registry = MagicMock() registry.exists = MagicMock(return_value=True) @@ -109,8 +109,8 @@ def test_delegation_service_get_available_agents_deprecation() -> None: @pytest.mark.anyio async def test_subagent_capability_uses_run_agent() -> None: """SubagentCapability.spawn_subagent calls run_agent() when session_pool is available.""" - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool.capabilities.subagent_capability import SubagentCapability + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.subagent_capability import SubagentCapability # Build a mock AgentContextDeps with a non-None session_pool. session_pool = MagicMock() @@ -164,8 +164,8 @@ async def _mock_stream(name: str, prompt: str) -> Any: yield "chunk1" yield "chunk2" - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool.capabilities.subagent_capability import SubagentCapability + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.subagent_capability import SubagentCapability host = MagicMock() host.session_pool = None diff --git a/tests/orchestrator/test_e2e.py b/tests/orchestrator/test_e2e.py index 7c49de902..6f79fa3ba 100644 --- a/tests/orchestrator/test_e2e.py +++ b/tests/orchestrator/test_e2e.py @@ -15,23 +15,23 @@ from pydantic_ai import TextPartDelta import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent, RunStartedEvent, StreamCompleteEvent, ToolCallCompleteEvent, ToolCallStartEvent, ) -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, EventEnvelope, SessionPool +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, EventEnvelope, SessionPool if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import AgentPool - from agentpool.agents.context import AgentRunContext + from wolfharness import AgentPool + from wolfharness.agents.context import AgentRunContext pytestmark = [pytest.mark.integration, pytest.mark.anyio] @@ -206,8 +206,8 @@ async def test_multi_turn_same_session_both_complete_via_consume_run( Current behavior: _consume_run breaks on StreamCompleteEvent + aclose() → RunHandle killed after turn 1 → turn 2's followup message is lost. """ - from agentpool.lifecycle.types import DeliveryMode from tests._controller_helpers import send_via_controller + from wolfharness.lifecycle.types import DeliveryMode session_pool = minimal_pool.session_pool assert session_pool is not None diff --git a/tests/orchestrator/test_enqueued_messages_mapping.py b/tests/orchestrator/test_enqueued_messages_mapping.py index 6f69d13ee..43924e72d 100644 --- a/tests/orchestrator/test_enqueued_messages_mapping.py +++ b/tests/orchestrator/test_enqueued_messages_mapping.py @@ -23,8 +23,8 @@ ) import pytest -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool.orchestrator.event_mapper import EventMapper +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness.orchestrator.event_mapper import EventMapper def _make_enqueued_event(content: str = "Hello, steer me!") -> EnqueuedMessagesEvent: diff --git a/tests/orchestrator/test_envelope_integration.py b/tests/orchestrator/test_envelope_integration.py index 25000c7a5..9793a94ae 100644 --- a/tests/orchestrator/test_envelope_integration.py +++ b/tests/orchestrator/test_envelope_integration.py @@ -6,7 +6,7 @@ import pytest -from agentpool.orchestrator.core import EventBus, EventEnvelope +from wolfharness.orchestrator.core import EventBus, EventEnvelope pytestmark = pytest.mark.integration @@ -15,7 +15,7 @@ if TYPE_CHECKING: import asyncio - from agentpool import AgentPool + from wolfharness import AgentPool def _queue_empty(queue: asyncio.Queue) -> bool: @@ -54,8 +54,8 @@ async def test_child_event_routing_source_session_id(self, minimal_pool: AgentPo async def test_transparent_forwarding(self) -> None: """Attribute access transparently forwards to the wrapped event via __getattr__.""" - from agentpool.agents.events import StreamCompleteEvent - from agentpool.messaging import ChatMessage + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.messaging import ChatMessage message = ChatMessage(content="hello world", role="assistant") event = StreamCompleteEvent(message=message) diff --git a/tests/orchestrator/test_event_bus.py b/tests/orchestrator/test_event_bus.py index c7e978505..a455cb223 100644 --- a/tests/orchestrator/test_event_bus.py +++ b/tests/orchestrator/test_event_bus.py @@ -20,7 +20,7 @@ ) import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( CompactionEvent, CustomEvent, PartDeltaEvent, @@ -41,8 +41,8 @@ ToolCallStartEvent, ToolResultMetadataEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import ( # type: ignore[attr-defined] +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import ( # type: ignore[attr-defined] EventBus, EventEnvelope, _is_immediate, diff --git a/tests/orchestrator/test_event_mapper.py b/tests/orchestrator/test_event_mapper.py index 235f378d6..df601deff 100644 --- a/tests/orchestrator/test_event_mapper.py +++ b/tests/orchestrator/test_event_mapper.py @@ -10,12 +10,12 @@ from pydantic_ai.messages import ToolCallPart, ToolReturnPart import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( RunStartedEvent, ToolCallCompleteEvent, ToolCallStartEvent, ) -from agentpool.orchestrator.event_mapper import EventMapper +from wolfharness.orchestrator.event_mapper import EventMapper @pytest.mark.unit diff --git a/tests/orchestrator/test_event_mapper_thinking_normalization.py b/tests/orchestrator/test_event_mapper_thinking_normalization.py index d3264b919..bf27057ba 100644 --- a/tests/orchestrator/test_event_mapper_thinking_normalization.py +++ b/tests/orchestrator/test_event_mapper_thinking_normalization.py @@ -22,8 +22,8 @@ from pydantic_ai.models.openai import _make_raw_content_updater import pytest -from agentpool.agents.events.events import PartDeltaEvent, PartStartEvent -from agentpool.orchestrator.event_mapper import ( +from wolfharness.agents.events.events import PartDeltaEvent, PartStartEvent +from wolfharness.orchestrator.event_mapper import ( EventMapper, _normalize_thinking_event, normalize_thinking_parts_in_messages, diff --git a/tests/orchestrator/test_event_mapper_tool_call_sanitization.py b/tests/orchestrator/test_event_mapper_tool_call_sanitization.py index dccf876b2..f295809d3 100644 --- a/tests/orchestrator/test_event_mapper_tool_call_sanitization.py +++ b/tests/orchestrator/test_event_mapper_tool_call_sanitization.py @@ -11,7 +11,7 @@ from pydantic_ai.messages import ModelResponse, TextPart, ToolCallPart import pytest -from agentpool.orchestrator.event_mapper import ( +from wolfharness.orchestrator.event_mapper import ( _extract_first_json_object, sanitize_tool_call_args_in_messages, ) diff --git a/tests/orchestrator/test_event_mapper_tool_progress.py b/tests/orchestrator/test_event_mapper_tool_progress.py index c729b705a..4b74cac80 100644 --- a/tests/orchestrator/test_event_mapper_tool_progress.py +++ b/tests/orchestrator/test_event_mapper_tool_progress.py @@ -6,11 +6,11 @@ from pydantic_ai.messages import ToolCallPart import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( ToolCallProgressEvent, ToolCallStartEvent, ) -from agentpool.orchestrator.event_mapper import EventMapper +from wolfharness.orchestrator.event_mapper import EventMapper @pytest.mark.unit diff --git a/tests/orchestrator/test_execute_turn.py b/tests/orchestrator/test_execute_turn.py index 4db64c804..0fdc76857 100644 --- a/tests/orchestrator/test_execute_turn.py +++ b/tests/orchestrator/test_execute_turn.py @@ -10,11 +10,11 @@ import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( RunErrorEvent, StreamCompleteEvent, ) -from agentpool.messaging.messagenode import ChatMessage +from wolfharness.messaging.messagenode import ChatMessage def _make_mock_session() -> MagicMock: @@ -36,7 +36,7 @@ async def test_execute_turn_breaks_on_run_error_event() -> None: _execute_turn() breaks on RunErrorEvent and does NOT yield the trailing StreamCompleteEvent. """ - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle error_event = RunErrorEvent( message="Something went wrong", @@ -96,7 +96,7 @@ async def test_execute_turn_breaks_on_stream_complete() -> None: When turn.execute() yields StreamCompleteEvent (no RunErrorEvent), _execute_turn() breaks normally. """ - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle complete_event = StreamCompleteEvent( message=ChatMessage(content="done", role="assistant"), diff --git a/tests/orchestrator/test_integration_redflags.py b/tests/orchestrator/test_integration_redflags.py index 9fab7f194..40e1d8aa6 100644 --- a/tests/orchestrator/test_integration_redflags.py +++ b/tests/orchestrator/test_integration_redflags.py @@ -14,10 +14,10 @@ import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.events import StreamCompleteEvent -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, EventEnvelope, SessionController +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, EventEnvelope, SessionController # ============================================================================ diff --git a/tests/orchestrator/test_multimodal_prompts.py b/tests/orchestrator/test_multimodal_prompts.py index 2a44c8310..4d593e733 100644 --- a/tests/orchestrator/test_multimodal_prompts.py +++ b/tests/orchestrator/test_multimodal_prompts.py @@ -19,13 +19,13 @@ from pydantic_ai.messages import BinaryImage import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import StreamCompleteEvent -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.turn import Turn +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.turn import Turn pytestmark = pytest.mark.unit @@ -149,7 +149,7 @@ async def test_start_accepts_list_prompt() -> None: @pytest.mark.unit async def test_start_accepts_empty_string() -> None: """Empty string produces empty prompts list (no spurious turn).""" - from agentpool.agents.staged_content import StagedContent + from wolfharness.agents.staged_content import StagedContent turn = _CapturingStubTurn([]) agent = MagicMock() @@ -202,7 +202,7 @@ async def test_steer_accepts_list_content() -> None: @pytest.mark.unit async def test_acp_turn_flattens_multimodal_prompts() -> None: """ACPTurn flattens list[str | list[Any]] into list[UserContent] for ACP.""" - from agentpool.agents.acp_agent.turn import ACPTurn + from wolfharness.agents.acp_agent.turn import ACPTurn binary_img = BinaryImage(data=b"\x89PNG", media_type="image/png") @@ -263,7 +263,7 @@ async def get_messages(self, session_id: str) -> Any: @pytest.mark.unit async def test_acp_turn_flattens_all_string_prompts() -> None: """ACPTurn with all string prompts flattens into text blocks.""" - from agentpool.agents.acp_agent.turn import ACPTurn + from wolfharness.agents.acp_agent.turn import ACPTurn prompts: list[Any] = ["hello", "world"] @@ -309,7 +309,7 @@ async def get_messages(self, session_id: str) -> Any: @pytest.mark.unit async def test_acp_turn_empty_prompts() -> None: """ACPTurn with empty prompts sends empty string.""" - from agentpool.agents.acp_agent.turn import ACPTurn + from wolfharness.agents.acp_agent.turn import ACPTurn captured_content: list[Any] = [] diff --git a/tests/orchestrator/test_multimodal_storage.py b/tests/orchestrator/test_multimodal_storage.py index 3659b8a03..efaab099e 100644 --- a/tests/orchestrator/test_multimodal_storage.py +++ b/tests/orchestrator/test_multimodal_storage.py @@ -24,11 +24,11 @@ ) import pytest -from agentpool.agents.native_agent.helpers import ( +from wolfharness.agents.native_agent.helpers import ( _summarize_content_block, extract_text_from_messages, ) -from agentpool.storage.serialization import ( +from wolfharness.storage.serialization import ( deserialize_prompts, serialize_prompts, ) @@ -229,7 +229,7 @@ def test_deserialize_prompts_empty_string_returns_empty() -> None: @pytest.mark.unit def test_snapshot_store_saves_prompts_serialized() -> None: """Snapshot store should save prompts_serialized with full multimodal data.""" - from agentpool.lifecycle import MemorySnapshotStore + from wolfharness.lifecycle import MemorySnapshotStore store = MemorySnapshotStore() img = BinaryImage(data=b"\x89PNG\r\n\x1a\n", media_type="image/png") @@ -319,7 +319,7 @@ def test_opencode_converter_uses_summarize_content_block() -> None: @pytest.mark.unit def test_compaction_extract_text_content_uses_summarize() -> None: """compaction._extract_text_content should include placeholders for non-string items.""" - from agentpool.messaging.compaction import _extract_text_content + from wolfharness.messaging.compaction import _extract_text_content img = BinaryImage(data=b"\x89PNG\r\n\x1a\n", media_type="image/png") msg = ModelRequest( @@ -348,16 +348,16 @@ async def test_multimodal_prompt_round_trip_through_db(tmp_path: Any) -> None: ``get_session_messages()``, and verifies the ``BinaryImage`` is preserved with correct data and media_type. """ - from agentpool.messaging import ChatMessage - from agentpool_config.storage import SQLStorageConfig - from agentpool_storage.sql_provider import SQLModelProvider + from wolfharness.messaging import ChatMessage + from wolfharness_config.storage import SQLStorageConfig + from wolfharness_storage.sql_provider import SQLModelProvider db_path = tmp_path / "test_multimodal.db" config = SQLStorageConfig(url=f"sqlite:///{db_path}") provider = SQLModelProvider(config) # Clear engine cache to avoid cross-test contamination. - from agentpool_config.storage import _engine_cache + from wolfharness_config.storage import _engine_cache _engine_cache.clear() diff --git a/tests/orchestrator/test_native_turn_integration.py b/tests/orchestrator/test_native_turn_integration.py index 8ed03076a..b7a36a4ec 100644 --- a/tests/orchestrator/test_native_turn_integration.py +++ b/tests/orchestrator/test_native_turn_integration.py @@ -27,21 +27,21 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events.events import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events.events import ( StreamCompleteEvent, ) -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.orchestrator.core import EventBus -from agentpool.orchestrator.run import RunHandle -from agentpool.tasks.exceptions import RunAbortedError +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.orchestrator.core import EventBus +from wolfharness.orchestrator.run import RunHandle +from wolfharness.tasks.exceptions import RunAbortedError if TYPE_CHECKING: - from agentpool.agents.events.events import RichAgentStreamEvent + from wolfharness.agents.events.events import RichAgentStreamEvent pytestmark = pytest.mark.unit @@ -68,7 +68,7 @@ async def test_native_turn_events_reach_event_bus_consumer() -> None: event_bus = EventBus() # Simulate SessionState with a turn_lock - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState session = SessionState( session_id="test-integration-session", @@ -192,7 +192,7 @@ async def test_run_aborted_error_yields_cancelled_stream_complete() -> None: events.extend([event async for event in turn.execute()]) # Must yield StreamCompleteEvent(cancelled=True), NOT RunErrorEvent - from agentpool.agents.events.events import RunErrorEvent + from wolfharness.agents.events.events import RunErrorEvent run_errors = [e for e in events if isinstance(e, RunErrorEvent)] stream_complete = [e for e in events if isinstance(e, StreamCompleteEvent)] @@ -281,7 +281,7 @@ def test_native_turn_no_redundant_run_started_event() -> None: RunHandle.start() already publishes RunStartedEvent before calling turn.execute(). Yielding it again causes duplicate events. """ - import agentpool.agents.native_agent.turn as turn_module + import wolfharness.agents.native_agent.turn as turn_module source = inspect.getsource(turn_module.NativeTurn.execute) import re @@ -303,7 +303,7 @@ def test_native_turn_run_error_event_includes_run_id() -> None: Without run_id, error events can't be correlated with the active run. """ - import agentpool.agents.native_agent.turn as turn_module + import wolfharness.agents.native_agent.turn as turn_module source = inspect.getsource(turn_module.NativeTurn.execute) assert "run_id=self._run_ctx.run_id" in source, ( diff --git a/tests/orchestrator/test_performance.py b/tests/orchestrator/test_performance.py index 3d6d088ec..b787be6fb 100644 --- a/tests/orchestrator/test_performance.py +++ b/tests/orchestrator/test_performance.py @@ -15,11 +15,11 @@ import pytest -from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, SessionPool -from agentpool.orchestrator.metrics import MetricsCollector +from wolfharness.agents.events import RunStartedEvent, StreamCompleteEvent +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, SessionPool +from wolfharness.orchestrator.metrics import MetricsCollector pytestmark = pytest.mark.integration @@ -28,7 +28,7 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import AgentPool + from wolfharness import AgentPool # ============================================================================ diff --git a/tests/orchestrator/test_phase6_cleanup.py b/tests/orchestrator/test_phase6_cleanup.py index 1363d35f2..39aa1f227 100644 --- a/tests/orchestrator/test_phase6_cleanup.py +++ b/tests/orchestrator/test_phase6_cleanup.py @@ -23,13 +23,13 @@ import pytest -from agentpool.orchestrator.core import EventBus, SessionController, SessionState -from agentpool.orchestrator.session_pool import SessionPool -from agentpool.orchestrator.session_pool_config import SessionPoolConfig +from wolfharness.orchestrator.core import EventBus, SessionController, SessionState +from wolfharness.orchestrator.session_pool import SessionPool +from wolfharness.orchestrator.session_pool_config import SessionPoolConfig if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.unit] @@ -143,7 +143,7 @@ async def test_close_during_mcp_tool_call_runhandle_cancel_first(minimal_pool: A controller._sessions[session_id] = session # Create a mock RunHandle that completes immediately when closed - from agentpool.orchestrator.run import RunHandle + from wolfharness.orchestrator.run import RunHandle run_handle = MagicMock(spec=RunHandle) run_handle.run_id = "run-mcp-1" @@ -255,8 +255,8 @@ async def test_checkpoint_on_close_failure_preserves_session(minimal_pool: Agent log the error, keep the session in memory, and MCP cleanup should still run. """ - from agentpool.sessions.models import SessionData - from agentpool_storage.protocols import SessionPersistence + from wolfharness.sessions.models import SessionData + from wolfharness_storage.protocols import SessionPersistence # Create a mock store that fails on save_session mock_store = AsyncMock(spec=SessionPersistence) diff --git a/tests/orchestrator/test_receive_request.py b/tests/orchestrator/test_receive_request.py index a8b9aaae9..fe08b9305 100644 --- a/tests/orchestrator/test_receive_request.py +++ b/tests/orchestrator/test_receive_request.py @@ -16,14 +16,14 @@ import pytest -from agentpool.lifecycle.types import DeliveryMode -from agentpool.orchestrator.core import EventBus, SessionController -from agentpool.orchestrator.run import RunHandle from tests._controller_helpers import send_via_controller +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.orchestrator.core import EventBus, SessionController +from wolfharness.orchestrator.run import RunHandle if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -295,7 +295,7 @@ async def test_receive_request_uses_get_or_create_session_agent(minimal_pool: Ag When agent is not yet cached (new top-level sessions), .get() returns None and receive_request silently does nothing. """ - from agentpool.orchestrator.core import SessionController + from wolfharness.orchestrator.core import SessionController controller = SessionController(pool=minimal_pool) event_bus = EventBus() @@ -434,7 +434,7 @@ async def test_wait_for_completion_session_not_found_raises( controller: SessionController, ) -> None: """wait_for_completion() raises SessionNotFoundError for unknown session.""" - from agentpool.orchestrator.session_controller import SessionNotFoundError + from wolfharness.orchestrator.session_controller import SessionNotFoundError with pytest.raises(SessionNotFoundError, match="Session not found"): await controller.wait_for_completion("nonexistent", timeout=1.0) diff --git a/tests/orchestrator/test_receive_request_acp.py b/tests/orchestrator/test_receive_request_acp.py index fc44b8a92..7d410dd02 100644 --- a/tests/orchestrator/test_receive_request_acp.py +++ b/tests/orchestrator/test_receive_request_acp.py @@ -13,16 +13,16 @@ import pytest -from agentpool.agents.acp_agent import ACPAgent -from agentpool.lifecycle import RunState -from agentpool.lifecycle.types import DeliveryMode -from agentpool.orchestrator.core import EventBus, SessionController -from agentpool.orchestrator.run import RunHandle from tests._controller_helpers import send_via_controller +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.lifecycle import RunState +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.orchestrator.core import EventBus, SessionController +from wolfharness.orchestrator.run import RunHandle if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_receive_request_input_provider.py b/tests/orchestrator/test_receive_request_input_provider.py index cbcb0b972..ec136d9c0 100644 --- a/tests/orchestrator/test_receive_request_input_provider.py +++ b/tests/orchestrator/test_receive_request_input_provider.py @@ -17,13 +17,13 @@ import pytest -from agentpool.orchestrator.core import EventBus, SessionController from tests._controller_helpers import send_via_controller +from wolfharness.orchestrator.core import EventBus, SessionController if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.orchestrator.run import RunHandle + from wolfharness import AgentPool + from wolfharness.orchestrator.run import RunHandle pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_recovery_integration.py b/tests/orchestrator/test_recovery_integration.py index fd4d3306e..a7ab8f5b6 100644 --- a/tests/orchestrator/test_recovery_integration.py +++ b/tests/orchestrator/test_recovery_integration.py @@ -7,7 +7,7 @@ Crash recovery is lifecycle-internal and does not depend on model API responses, so these are L2 integration tests (not L3 VCR tests). -See: https://github.com/Leoyzen/agentpool/issues/205 +See: https://github.com/Leoyzen/wolfharness/issues/205 """ from __future__ import annotations @@ -17,7 +17,7 @@ import pytest -from agentpool.lifecycle import ( +from wolfharness.lifecycle import ( DurableJournal, MemoryJournal, MemorySnapshotStore, diff --git a/tests/orchestrator/test_replay_buffer_turn_isolation.py b/tests/orchestrator/test_replay_buffer_turn_isolation.py index 7c2d7f840..cc1a77d70 100644 --- a/tests/orchestrator/test_replay_buffer_turn_isolation.py +++ b/tests/orchestrator/test_replay_buffer_turn_isolation.py @@ -18,14 +18,14 @@ import anyio import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( PartDeltaEvent, RunErrorEvent, RunStartedEvent, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, EventEnvelope +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, EventEnvelope def _make_run_started(session_id: str = "s1") -> RunStartedEvent: diff --git a/tests/orchestrator/test_resume_integration.py b/tests/orchestrator/test_resume_integration.py index d32f2ea4b..73d739186 100644 --- a/tests/orchestrator/test_resume_integration.py +++ b/tests/orchestrator/test_resume_integration.py @@ -12,13 +12,13 @@ import pytest -from agentpool.agents.events.events import SessionResumeEvent -from agentpool.orchestrator.core import SessionBusyError, SessionPool -from agentpool.sessions.models import PendingDeferredCall, SessionData +from wolfharness.agents.events.events import SessionResumeEvent +from wolfharness.orchestrator.core import SessionBusyError, SessionPool +from wolfharness.sessions.models import PendingDeferredCall, SessionData if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool def _stream_empty(queue: asyncio.Queue[Any]) -> bool: @@ -125,7 +125,7 @@ def approvals(self) -> dict[str, Any]: @pytest.fixture async def session_pool(minimal_pool: AgentPool) -> SessionPool: """Return a SessionPool backed by a real AgentPool with MemoryStorageProvider.""" - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider store = MemoryStorageProvider() return SessionPool(pool=minimal_pool, store=store) @@ -141,7 +141,7 @@ async def test_resume_session_raises_session_not_found_error( session_pool: SessionPool, ) -> None: """resume_session raises SessionNotFoundError for non-existent session.""" - from agentpool.orchestrator.core import SessionNotFoundError + from wolfharness.orchestrator.core import SessionNotFoundError results = make_deferred_tool_results(["call-1"]) with pytest.raises(SessionNotFoundError, match="sess-nonexistent"): @@ -158,7 +158,7 @@ async def test_resume_session_raises_busy_error_when_active_run( session_pool: SessionPool, ) -> None: """resume_session raises SessionBusyError when the session has an active run.""" - from agentpool.orchestrator.core import SessionBusyError + from wolfharness.orchestrator.core import SessionBusyError # Create session and fake an active run state, _ = await session_pool.sessions.get_or_create_session("sess-1", agent_name="test-agent") @@ -179,7 +179,7 @@ async def test_resume_session_raises_mismatch_error_missing_results( session_pool: SessionPool, ) -> None: """resume_session raises CheckpointMismatchError when results don't cover all pending calls.""" - from agentpool.orchestrator.core import CheckpointMismatchError + from wolfharness.orchestrator.core import CheckpointMismatchError store = session_pool.sessions.store assert store is not None @@ -202,7 +202,7 @@ async def test_resume_session_raises_mismatch_error_extra_results( session_pool: SessionPool, ) -> None: """resume_session raises CheckpointMismatchError when results include unknown call IDs.""" - from agentpool.orchestrator.core import CheckpointMismatchError + from wolfharness.orchestrator.core import CheckpointMismatchError store = session_pool.sessions.store assert store is not None @@ -275,7 +275,7 @@ async def test_resume_native_agent_loads_checkpoint_and_runs( ) # Create a fake checkpoint - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -341,7 +341,7 @@ async def test_resume_native_agent_clears_pending_after_success( ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -388,7 +388,7 @@ async def test_resume_native_agent_does_not_clear_pending_on_failure( ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -442,7 +442,7 @@ async def test_resume_session_emits_resume_event( ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -511,7 +511,7 @@ async def test_resume_session_transitions_status_to_active( ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -562,7 +562,7 @@ async def test_resume_session_keeps_checkpointed_status_on_failure( ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -621,7 +621,7 @@ async def test_resume_acp_agent_reopens_subprocess( mock_acp.run = AsyncMock(return_value=MagicMock()) mock_acp._resume_session = AsyncMock(return_value=None) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -666,7 +666,7 @@ async def test_resume_session_with_empty_pending_calls( ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], @@ -717,7 +717,7 @@ async def _fail_gen_rc(*args: Any, **kwargs: Any) -> Any: @pytest.fixture async def session_pool_rc(minimal_pool: AgentPool) -> SessionPool: """Return a SessionPool backed by a real AgentPool with MemoryStorageProvider.""" - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider store = MemoryStorageProvider() return SessionPool(pool=minimal_pool, store=store) @@ -801,7 +801,7 @@ async def test_resume_session_concurrent_calls_serialize(session_pool_rc: Sessio metadata={"agent_type": "native"}, ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], pending_calls=[make_pending_call("call-1")] @@ -855,7 +855,7 @@ async def test_resume_session_rejects_second_after_success(session_pool_rc: Sess metadata={"agent_type": "native"}, ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], pending_calls=[make_pending_call("call-1")] @@ -898,7 +898,7 @@ async def test_resume_session_does_not_clear_pending_on_failure( metadata={"agent_type": "native"}, ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], pending_calls=[make_pending_call("call-1")] @@ -944,7 +944,7 @@ async def test_resume_session_allows_retry_after_failure(session_pool_rc: Sessio metadata={"agent_type": "native"}, ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], pending_calls=[make_pending_call("call-1")] @@ -1002,7 +1002,7 @@ async def test_resume_session_status_transitions_checkpointed_to_resuming_to_act metadata={"agent_type": "native"}, ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], pending_calls=[make_pending_call("call-1")] @@ -1051,7 +1051,7 @@ async def test_resume_session_status_reverts_to_checkpointed_on_failure( metadata={"agent_type": "native"}, ) ) - from agentpool.agents.native_agent.checkpoint import CheckpointData + from wolfharness.agents.native_agent.checkpoint import CheckpointData checkpoint_data = CheckpointData( message_history=[], pending_calls=[make_pending_call("call-1")] diff --git a/tests/orchestrator/test_route_message_event.py b/tests/orchestrator/test_route_message_event.py index f7c25ddbf..4eff93659 100644 --- a/tests/orchestrator/test_route_message_event.py +++ b/tests/orchestrator/test_route_message_event.py @@ -12,12 +12,12 @@ import pytest -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool.orchestrator.core import EventBus, SessionController, SessionState +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness.orchestrator.core import EventBus, SessionController, SessionState if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_run_event_agent_name.py b/tests/orchestrator/test_run_event_agent_name.py index cf9118079..db04745b3 100644 --- a/tests/orchestrator/test_run_event_agent_name.py +++ b/tests/orchestrator/test_run_event_agent_name.py @@ -16,16 +16,16 @@ import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunErrorEvent, RunStartedEvent, StreamCompleteEvent, ) -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.turn import Turn +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.turn import Turn pytestmark = pytest.mark.unit @@ -71,7 +71,7 @@ def _make_handle( Returns (handle, event_bus) so the caller can inspect published events. """ - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState agent = MagicMock() agent.name = agent_name diff --git a/tests/orchestrator/test_run_failure_context_redflag.py b/tests/orchestrator/test_run_failure_context_redflag.py index d27ef3a0c..7094e2716 100644 --- a/tests/orchestrator/test_run_failure_context_redflag.py +++ b/tests/orchestrator/test_run_failure_context_redflag.py @@ -13,9 +13,9 @@ import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.lifecycle.types import DeliveryMode +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.lifecycle.types import DeliveryMode pytestmark = pytest.mark.integration diff --git a/tests/orchestrator/test_run_handle.py b/tests/orchestrator/test_run_handle.py index 6660cde0f..c7a91bca4 100644 --- a/tests/orchestrator/test_run_handle.py +++ b/tests/orchestrator/test_run_handle.py @@ -27,19 +27,19 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import ( +from wolfharness import Agent +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import ( RunErrorEvent, StreamCompleteEvent, ) -from agentpool.lifecycle import RunOutcome -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.messaging import ChatMessage, MessageHistory -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle -from agentpool.orchestrator.turn import Turn +from wolfharness.lifecycle import RunOutcome +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.messaging import ChatMessage, MessageHistory +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle +from wolfharness.orchestrator.turn import Turn pytestmark = pytest.mark.unit @@ -190,7 +190,7 @@ async def test_steer_with_agent_run_emits_user_message_inserted_event() -> None: ``source="processed"`` instead of the fire-and-forget ``_schedule_user_message_emission()`` path. """ - from agentpool.agents.events.events import UserMessageInsertedEvent + from wolfharness.agents.events.events import UserMessageInsertedEvent bus = EventBus() handle = _make_run_handle(event_bus=bus) @@ -416,7 +416,7 @@ async def test_start_allows_event_bus_none_with_comm_channel() -> None: Regression test: _initialize_lifecycle_and_recovery() now creates a DirectChannel when event_bus is None, so start() must allow it. """ - from agentpool.lifecycle import DirectChannel, MemoryJournal + from wolfharness.lifecycle import DirectChannel, MemoryJournal agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn()) @@ -784,7 +784,7 @@ async def test_input_provider_contextvar_set_during_turn() -> None: MCP elicitation depends on this ContextVar. Without it, _current_input_provider.get() returns None during turn execution. """ - from agentpool.mcp_server.manager import _current_input_provider + from wolfharness.mcp_server.manager import _current_input_provider captured_provider: list[Any] = [] @@ -849,7 +849,7 @@ def test_start_sets_current_task() -> None: Without this, cancel() in _interrupt() gets None for current_task and cannot interrupt the running turn. """ - import agentpool.orchestrator.run as run_module + import wolfharness.orchestrator.run as run_module source = inspect.getsource(run_module.RunHandle.start) assert "current_task" in source, ( @@ -1036,7 +1036,7 @@ async def test_start_empty_prompt_no_staged_content_terminates_immediately() -> In the per-prompt model, an empty prompt with no staged_content means no turn is executed and the generator returns immediately. """ - from agentpool.agents.staged_content import StagedContent + from wolfharness.agents.staged_content import StagedContent agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn()) @@ -1070,7 +1070,7 @@ async def test_start_empty_list_no_staged_content_terminates_immediately() -> No An empty list is falsy and should be treated the same as an empty string — no turn executes. """ - from agentpool.agents.staged_content import StagedContent + from wolfharness.agents.staged_content import StagedContent agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn()) @@ -1104,7 +1104,7 @@ async def test_start_empty_list_with_staged_content_executes_turn() -> None: consume the staged content. Without this, skill instructions are silently discarded (issue #284). """ - from agentpool.agents.staged_content import StagedContent + from wolfharness.agents.staged_content import StagedContent staged = StagedContent() staged.add_text("IMPORTANT_SKILL_DIRECTIVE") @@ -1354,7 +1354,7 @@ async def test_lifecycle_dimensions_not_closed_when_run_handle_terminates() -> N In the per-prompt model, lifecycle dimensions are session-owned. RunHandle.close() only sets complete_event and clears steer_callback. """ - from agentpool.lifecycle import DirectChannel, MemoryJournal + from wolfharness.lifecycle import DirectChannel, MemoryJournal journal = MemoryJournal() comm_channel = DirectChannel(journal) @@ -1447,8 +1447,8 @@ async def test_standalone_session_without_event_bus_executes_turn() -> None: to fail in _execute_turn(). Now a DirectChannel is created and start() allows event_bus=None when session._comm_channel is set. """ - from agentpool.lifecycle import DirectChannel, MemoryJournal - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.lifecycle import DirectChannel, MemoryJournal + from wolfharness.orchestrator.session_controller import SessionState agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn(events=[_stream_complete_event()])) @@ -1493,8 +1493,8 @@ async def test_feedback_queue_drained_on_new_run_handle_start() -> None: The fix drains feedback_queue in start() before the turn begins, routing messages to queued_steer_messages via self.steer(). """ - from agentpool.lifecycle import DirectChannel, Feedback, MemoryJournal - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.lifecycle import DirectChannel, Feedback, MemoryJournal + from wolfharness.orchestrator.session_controller import SessionState agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn()) @@ -1541,8 +1541,8 @@ async def test_multiple_steer_messages_drained_fifo_from_feedback_queue() -> Non all their steer messages should be enqueued to feedback_queue and drained in FIFO order when the next RunHandle starts. """ - from agentpool.lifecycle import DirectChannel, Feedback, MemoryJournal - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.lifecycle import DirectChannel, Feedback, MemoryJournal + from wolfharness.orchestrator.session_controller import SessionState agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn()) @@ -1604,9 +1604,9 @@ async def test_empty_prompt_drains_feedback_queue_but_messages_unprocessed() -> queued_steer_messages after start() returns and re-enqueue any unprocessed messages back to feedback_queue for the next RunHandle. """ - from agentpool.agents.staged_content import StagedContent - from agentpool.lifecycle import DirectChannel, Feedback, MemoryJournal - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.agents.staged_content import StagedContent + from wolfharness.lifecycle import DirectChannel, Feedback, MemoryJournal + from wolfharness.orchestrator.session_controller import SessionState agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn()) @@ -1659,8 +1659,8 @@ async def test_feedback_queue_drains_multimodal_content_blocks() -> None: text), the Feedback object has content_blocks set instead of content. The draining code should handle both paths. """ - from agentpool.lifecycle import DirectChannel, Feedback, MemoryJournal - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.lifecycle import DirectChannel, Feedback, MemoryJournal + from wolfharness.orchestrator.session_controller import SessionState agent = MagicMock() agent.create_turn = MagicMock(return_value=_StubTurn()) diff --git a/tests/orchestrator/test_run_lifecycle.py b/tests/orchestrator/test_run_lifecycle.py index ffc0a5140..2c7c79887 100644 --- a/tests/orchestrator/test_run_lifecycle.py +++ b/tests/orchestrator/test_run_lifecycle.py @@ -14,13 +14,13 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool -from agentpool.agents.base_agent import _current_run_ctx_var -from agentpool.agents.context import AgentRunContext -from agentpool.lifecycle import RunOutcome -from agentpool.orchestrator.core import SessionPool -from agentpool.orchestrator.metrics import MetricsCollector -from agentpool.orchestrator.run import RunHandle +from wolfharness import Agent, AgentPool +from wolfharness.agents.base_agent import _current_run_ctx_var +from wolfharness.agents.context import AgentRunContext +from wolfharness.lifecycle import RunOutcome +from wolfharness.orchestrator.core import SessionPool +from wolfharness.orchestrator.metrics import MetricsCollector +from wolfharness.orchestrator.run import RunHandle pytestmark = [pytest.mark.unit, pytest.mark.anyio] diff --git a/tests/orchestrator/test_run_status.py b/tests/orchestrator/test_run_status.py index e2aa23c95..5e7d6583a 100644 --- a/tests/orchestrator/test_run_status.py +++ b/tests/orchestrator/test_run_status.py @@ -1,4 +1,4 @@ -"""Tests for the RunState and RunOutcome enums in agentpool.lifecycle.types.""" +"""Tests for the RunState and RunOutcome enums in wolfharness.lifecycle.types.""" from __future__ import annotations @@ -6,7 +6,7 @@ import pytest -from agentpool.lifecycle import RunOutcome, RunState +from wolfharness.lifecycle import RunOutcome, RunState pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_runhandle_emission.py b/tests/orchestrator/test_runhandle_emission.py index 1b68c5e58..e1e95738e 100644 --- a/tests/orchestrator/test_runhandle_emission.py +++ b/tests/orchestrator/test_runhandle_emission.py @@ -27,12 +27,12 @@ import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.orchestrator.core import SessionState -from agentpool.orchestrator.run import RunHandle +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.orchestrator.core import SessionState +from wolfharness.orchestrator.run import RunHandle pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_session_controller.py b/tests/orchestrator/test_session_controller.py index 127d6eca4..1a037ae27 100644 --- a/tests/orchestrator/test_session_controller.py +++ b/tests/orchestrator/test_session_controller.py @@ -13,19 +13,19 @@ import pytest -from agentpool.lifecycle.types import DeliveryMode -from agentpool.orchestrator.core import ( +from tests._controller_helpers import send_via_controller +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.orchestrator.core import ( DEFAULT_SESSION_TTL_SECONDS, EventBus, SessionController, SessionState, ) -from agentpool.orchestrator.run import RunHandle -from tests._controller_helpers import send_via_controller +from wolfharness.orchestrator.run import RunHandle if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -116,7 +116,7 @@ async def test_list_sessions_returns_session_info( controller: SessionController, ) -> None: """list_sessions returns SessionInfo DTOs for all active sessions.""" - from agentpool_server.opencode_server.models.session_info import SessionInfo + from wolfharness_server.opencode_server.models.session_info import SessionInfo await controller.get_or_create_session("sess-1", agent_name="agent-a") await controller.get_or_create_session("sess-2", agent_name="agent-b") @@ -452,7 +452,7 @@ async def test_steer_followup_inside_request_lock(minimal_pool: AgentPool) -> No Without this, current_run_id can be cleared between the check and the steer()/followup() call, causing silent message drops. """ - from agentpool.orchestrator.core import SessionController + from wolfharness.orchestrator.core import SessionController controller = SessionController(pool=minimal_pool) controller._event_bus = EventBus() @@ -498,7 +498,7 @@ def test_background_task_strong_reference() -> None: Without a strong reference, Python's GC can destroy the task mid-execution. """ - import agentpool.orchestrator.core as core_module + import wolfharness.orchestrator.core as core_module source = inspect.getsource(core_module.SessionController._start_run_handle) assert "_background_tasks" in source, ( @@ -543,7 +543,7 @@ def test_background_tasks_initialized_in_init(minimal_pool: AgentPool) -> None: Without early initialization, the first call to _start_run_handle hits a hasattr check that could mask bugs. """ - from agentpool.orchestrator.core import SessionController + from wolfharness.orchestrator.core import SessionController controller = SessionController(pool=minimal_pool) assert hasattr(controller, "_background_tasks"), ( @@ -559,7 +559,7 @@ def test_background_task_callback_is_named_function() -> None: """ import re - import agentpool.orchestrator.core as core_module + import wolfharness.orchestrator.core as core_module source = inspect.getsource(core_module.SessionController._start_run_handle) # Should NOT contain the lambda tuple pattern diff --git a/tests/orchestrator/test_session_lifecycle.py b/tests/orchestrator/test_session_lifecycle.py index 7556b9495..45d7c4b55 100644 --- a/tests/orchestrator/test_session_lifecycle.py +++ b/tests/orchestrator/test_session_lifecycle.py @@ -16,23 +16,23 @@ import pytest -from agentpool.agents.events import RunStartedEvent -from agentpool.orchestrator.core import ( +from wolfharness.agents.events import RunStartedEvent +from wolfharness.orchestrator.core import ( EventBus, SessionController, SessionLifecyclePolicy, SessionPool, SessionState, ) -from agentpool.orchestrator.run import RunHandle -from agentpool.sessions.models import PendingDeferredCall, SessionData +from wolfharness.orchestrator.run import RunHandle +from wolfharness.sessions.models import PendingDeferredCall, SessionData if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import AgentPool - from agentpool.agents.context import AgentRunContext + from wolfharness import AgentPool + from wolfharness.agents.context import AgentRunContext pytestmark = pytest.mark.unit @@ -303,7 +303,7 @@ async def _patched_acquire(self: asyncio.Lock, *args: Any, **kwargs: Any) -> boo if TYPE_CHECKING: - from agentpool.agents.context import AgentRunContext + from wolfharness.agents.context import AgentRunContext @pytest.fixture @@ -380,7 +380,7 @@ async def test_close_session_releases_lock_on_cancelled(minimal_pool: AgentPool) Without try/finally, CancelledError during complete_event.wait() skips the lock release, leaving the session permanently locked. """ - from agentpool.orchestrator.core import SessionController + from wolfharness.orchestrator.core import SessionController controller_cs = SessionController(pool=minimal_pool) controller_cs._event_bus = EventBus() @@ -435,9 +435,9 @@ async def test_close_session_after_cancel(minimal_pool: AgentPool) -> None: _turn_complete_event, and the turn completes — releasing turn_lock. close_session() should acquire turn_lock quickly and return. """ - from agentpool.agents.events import StreamCompleteEvent - from agentpool.messaging import ChatMessage - from agentpool.orchestrator.turn import Turn + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.messaging import ChatMessage + from wolfharness.orchestrator.turn import Turn class _BlockingTurn(Turn): """Turn that blocks until run_ctx.cancelled, then returns.""" diff --git a/tests/orchestrator/test_session_pool.py b/tests/orchestrator/test_session_pool.py index c144c199f..8a867fdd7 100644 --- a/tests/orchestrator/test_session_pool.py +++ b/tests/orchestrator/test_session_pool.py @@ -10,12 +10,12 @@ import pytest -from agentpool.messaging import ChatMessage +from wolfharness.messaging import ChatMessage if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.orchestrator.core import SessionPool + from wolfharness import AgentPool + from wolfharness.orchestrator.core import SessionPool pytestmark = pytest.mark.unit @@ -364,8 +364,8 @@ async def test_cache_copy_messages_invalidates_target_cache( # RunHandle delegation tests (feature-flag gated) # --------------------------------------------------------------------------- -from agentpool.lifecycle import RunState # noqa: E402 -from agentpool.orchestrator.run import RunHandle # noqa: E402 +from wolfharness.lifecycle import RunState # noqa: E402 +from wolfharness.orchestrator.run import RunHandle # noqa: E402 def _make_mock_agent() -> MagicMock: diff --git a/tests/orchestrator/test_session_pool_input_provider.py b/tests/orchestrator/test_session_pool_input_provider.py index 5b0ac5808..3853aad04 100644 --- a/tests/orchestrator/test_session_pool_input_provider.py +++ b/tests/orchestrator/test_session_pool_input_provider.py @@ -12,17 +12,17 @@ import pytest -from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent -from agentpool.messaging import ChatMessage -from agentpool.ui.base import InputProvider +from wolfharness.agents.events import RunStartedEvent, StreamCompleteEvent +from wolfharness.messaging import ChatMessage +from wolfharness.ui.base import InputProvider if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import AgentPool - from agentpool.agents.context import AgentRunContext - from agentpool.orchestrator.core import SessionPool + from wolfharness import AgentPool + from wolfharness.agents.context import AgentRunContext + from wolfharness.orchestrator.core import SessionPool pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_session_pool_public_api.py b/tests/orchestrator/test_session_pool_public_api.py index 3aa7eea18..8725e332c 100644 --- a/tests/orchestrator/test_session_pool_public_api.py +++ b/tests/orchestrator/test_session_pool_public_api.py @@ -11,15 +11,15 @@ import pytest -from agentpool.agents.events import RunErrorEvent, StreamCompleteEvent -from agentpool.lifecycle.types import DeliveryMode -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.session_controller import SessionNotFoundError +from wolfharness.agents.events import RunErrorEvent, StreamCompleteEvent +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.session_controller import SessionNotFoundError if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.orchestrator.core import SessionPool + from wolfharness import AgentPool + from wolfharness.orchestrator.core import SessionPool pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_sessionpool_integration.py b/tests/orchestrator/test_sessionpool_integration.py index 5ce95649e..cf9d92e06 100644 --- a/tests/orchestrator/test_sessionpool_integration.py +++ b/tests/orchestrator/test_sessionpool_integration.py @@ -51,22 +51,22 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import SpawnSessionStart, StreamCompleteEvent -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool.hooks import AgentHooks, CallableHook, HookResult -from agentpool.lifecycle.types import DeliveryMode -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle -from agentpool.tools.base import Tool -from agentpool_server.opencode_server.models.parts import ( +from wolfharness import Agent, AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import SpawnSessionStart, StreamCompleteEvent +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.hooks import AgentHooks, CallableHook, HookResult +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle +from wolfharness.tools.base import Tool +from wolfharness_server.opencode_server.models.parts import ( ToolPart, ToolStateCompleted, ToolStateRunning, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -74,7 +74,7 @@ if TYPE_CHECKING: from collections.abc import Sequence - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill class MockServerState: @@ -96,7 +96,7 @@ async def broadcast_event(self, event: Any) -> None: self.events.append(event) def resolve_default_model_info(self) -> tuple[str, str]: - return "default", "agentpool" + return "default", "wolfharness" class MockMCPCapability(FunctionToolsetCapability): @@ -216,7 +216,7 @@ async def test_agent_level_mcp_not_inherited_by_child() -> None: # We test at the config level — we don't need to actually connect. # The key assertion is that the parent's MCPManager is dedicated # (not pool.mcp), and the child's IS pool.mcp. - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness_config.mcp_server import StdioMCPServerConfig parent_config = NativeAgentConfig( name="parent_agent", @@ -458,7 +458,7 @@ async def test_toolset_cache_exists_on_mcp_manager() -> None: so repeated calls reuse the same underlying connection. The ``MCP`` wrapper instances remain distinct. """ - from agentpool.mcp_server.manager import MCPManager + from wolfharness.mcp_server.manager import MCPManager manager = MCPManager(name="test") assert hasattr(manager, "_toolset_cache") @@ -474,8 +474,8 @@ async def test_get_capabilities_caches_toolset_by_client_id() -> None: with the same underlying ``MCPToolset`` instance (cached by ``client_id``). The MCP wrappers themselves are distinct. """ - from agentpool.mcp_server.manager import MCPManager - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness.mcp_server.manager import MCPManager + from wolfharness_config.mcp_server import StdioMCPServerConfig manager = MCPManager( servers=[ @@ -730,7 +730,7 @@ async def test_e2e_reasoning_events_through_sessionpool() -> None: ) event_types = [type(e).__name__ for e in server_state.events] print(f"Broadcast events: {event_types}") - from agentpool_server.opencode_server.models import PartUpdatedEvent + from wolfharness_server.opencode_server.models import PartUpdatedEvent part_events = [e for e in server_state.events if isinstance(e, PartUpdatedEvent)] assert len(part_events) > 0, ( @@ -796,7 +796,7 @@ def __init__(self): async def get_or_create_session(self, session_id, agent_name=None, **metadata): if session_id not in self._sessions: - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState state = SessionState(session_id=session_id, agent_name=agent_name or "default") self._sessions[session_id] = state @@ -1095,7 +1095,7 @@ def _fn(**kwargs: Any) -> HookResult: def _make_run_handle(agent: Agent[Any, Any], run_ctx: AgentRunContext) -> RunHandle: """Create a RunHandle wired for the SessionPool path.""" - from agentpool.lifecycle import DirectChannel, MemoryJournal + from wolfharness.lifecycle import DirectChannel, MemoryJournal event_bus = EventBus() session = SessionState(session_id="test-session", agent_name="test-agent") @@ -1177,7 +1177,7 @@ async def _consume() -> None: await consumer_task pre_turn_count = sum((1 for name, _ in hook_calls if name == "pre_turn")) assert pre_turn_count == 1, "pre_turn must fire in turn 1" - from agentpool.agents.native_agent.turn import NativeTurn + from wolfharness.agents.native_agent.turn import NativeTurn turn = NativeTurn( agent=agent, prompts=["second prompt"], run_ctx=run_ctx, message_history=[], hooks=hooks diff --git a/tests/orchestrator/test_sessionpool_reasoning_redflag.py b/tests/orchestrator/test_sessionpool_reasoning_redflag.py index c868bc1ce..b80ec52e4 100644 --- a/tests/orchestrator/test_sessionpool_reasoning_redflag.py +++ b/tests/orchestrator/test_sessionpool_reasoning_redflag.py @@ -15,10 +15,10 @@ ) import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunStartedEvent, ) -from agentpool.orchestrator.core import EventBus +from wolfharness.orchestrator.core import EventBus pytestmark = pytest.mark.unit @@ -149,7 +149,7 @@ async def test_eventbus_with_subagent_wrapping(): reasoning events that can be extracted. """ - from agentpool.agents.events import SubAgentEvent + from wolfharness.agents.events import SubAgentEvent event_bus = EventBus() parent_session = "parent" diff --git a/tests/orchestrator/test_staged_content_integration.py b/tests/orchestrator/test_staged_content_integration.py index 4bff93d9f..f120ebc62 100644 --- a/tests/orchestrator/test_staged_content_integration.py +++ b/tests/orchestrator/test_staged_content_integration.py @@ -25,18 +25,18 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events.events import StreamCompleteEvent -from agentpool.agents.native_agent.turn import NativeTurn -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle from tests._controller_helpers import send_via_controller +from wolfharness import Agent, AgentPool +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events.events import StreamCompleteEvent +from wolfharness.agents.native_agent.turn import NativeTurn +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle if TYPE_CHECKING: - from agentpool.agents.events.events import RichAgentStreamEvent + from wolfharness.agents.events.events import RichAgentStreamEvent pytestmark = pytest.mark.unit @@ -194,7 +194,7 @@ async def test_receive_request_empty_list_not_converted_to_string(minimal_pool: followup()/steer() without stringification. The initial prompt is routed through followup() (D17), and start() is called with "". """ - from agentpool.orchestrator.core import SessionController + from wolfharness.orchestrator.core import SessionController controller = SessionController(pool=minimal_pool) event_bus = EventBus() diff --git a/tests/orchestrator/test_steer_background_acp_integration.py b/tests/orchestrator/test_steer_background_acp_integration.py index a278856bd..d8b95a854 100644 --- a/tests/orchestrator/test_steer_background_acp_integration.py +++ b/tests/orchestrator/test_steer_background_acp_integration.py @@ -13,12 +13,12 @@ import pytest -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness_server.acp_server.event_converter import ACPEventConverter if TYPE_CHECKING: - from agentpool.delegation.pool import AgentPool + from wolfharness.delegation.pool import AgentPool pytestmark = [pytest.mark.integration] @@ -51,8 +51,8 @@ async def test_steer_from_background_task_acp_converter( async def _blocking_create_turn(*args: Any, **kwargs: Any) -> Any: await release.wait() - from agentpool.agents.events.events import StreamCompleteEvent - from agentpool.messaging.messagenode import ChatMessage + from wolfharness.agents.events.events import StreamCompleteEvent + from wolfharness.messaging.messagenode import ChatMessage return StreamCompleteEvent(message=ChatMessage(role="assistant", content="done")) diff --git a/tests/orchestrator/test_steer_message_loss.py b/tests/orchestrator/test_steer_message_loss.py index 70e9992e4..26ee08ca9 100644 --- a/tests/orchestrator/test_steer_message_loss.py +++ b/tests/orchestrator/test_steer_message_loss.py @@ -24,14 +24,14 @@ import anyio import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent -from agentpool.lifecycle import Feedback -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import RunStartedEvent, StreamCompleteEvent +from wolfharness.lifecycle import Feedback +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle pytestmark = [pytest.mark.unit] @@ -271,7 +271,7 @@ async def test_start_drain_feedback_queue_to_queued_steer_messages() -> None: The messages in queued_steer_messages are later drained by NativeTurn.execute() via drain_queued_steer_messages(). """ - from agentpool.orchestrator.turn import Turn + from wolfharness.orchestrator.turn import Turn class _CapturingStubTurn(Turn): """Stub turn that captures prompts and simulates drain.""" diff --git a/tests/orchestrator/test_steer_queue_events.py b/tests/orchestrator/test_steer_queue_events.py index da0d77768..fcbb481ca 100644 --- a/tests/orchestrator/test_steer_queue_events.py +++ b/tests/orchestrator/test_steer_queue_events.py @@ -21,13 +21,13 @@ import anyio import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import RunErrorEvent, RunStartedEvent, StreamCompleteEvent -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import RunErrorEvent, RunStartedEvent, StreamCompleteEvent +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle pytestmark = [pytest.mark.integration, pytest.mark.anyio] diff --git a/tests/orchestrator/test_steer_user_message.py b/tests/orchestrator/test_steer_user_message.py index cf4b38d09..7897b92d8 100644 --- a/tests/orchestrator/test_steer_user_message.py +++ b/tests/orchestrator/test_steer_user_message.py @@ -19,12 +19,12 @@ import pytest -from agentpool.agents.context import AgentRunContext -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.orchestrator.core import SessionState -from agentpool.orchestrator.run import RunHandle +from wolfharness.agents.context import AgentRunContext +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.orchestrator.core import SessionState +from wolfharness.orchestrator.run import RunHandle pytestmark = pytest.mark.unit diff --git a/tests/orchestrator/test_streaming_redflag_tool_calls.py b/tests/orchestrator/test_streaming_redflag_tool_calls.py index 43b5e1558..cce7f9daf 100644 --- a/tests/orchestrator/test_streaming_redflag_tool_calls.py +++ b/tests/orchestrator/test_streaming_redflag_tool_calls.py @@ -18,14 +18,14 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.base_agent import _in_turn_context -from agentpool.agents.events import ( +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.base_agent import _in_turn_context +from wolfharness.agents.events import ( StreamCompleteEvent, ToolCallCompleteEvent, ToolCallStartEvent, ) -from agentpool.tools import Tool +from wolfharness.tools import Tool if TYPE_CHECKING: @@ -105,7 +105,7 @@ async def test_tool_call_only_response_has_no_text_deltas() -> None: # Categorise events for analysis event_types = [type(e).__name__ for e in events] - # Native agent emits pydantic-ai PartDeltaEvent directly (not agentpool's subclass) + # Native agent emits pydantic-ai PartDeltaEvent directly (not wolfharness's subclass) text_deltas = [e for e in events if isinstance(e, PyAIPartDeltaEvent)] tool_call_starts = [e for e in events if isinstance(e, ToolCallStartEvent)] tool_call_completes = [e for e in events if isinstance(e, ToolCallCompleteEvent)] @@ -232,7 +232,7 @@ async def test_text_response_yields_deltas() -> None: event_types = [type(e).__name__ for e in events] print(f"\nEvents with text response: {event_types}") - # Native agent emits pydantic-ai PartDeltaEvent directly (not agentpool's subclass) + # Native agent emits pydantic-ai PartDeltaEvent directly (not wolfharness's subclass) text_deltas = [e for e in events if isinstance(e, PyAIPartDeltaEvent)] stream_completes = [e for e in events if isinstance(e, StreamCompleteEvent)] diff --git a/tests/orchestrator/test_subagent_events.py b/tests/orchestrator/test_subagent_events.py index a75e80ebd..8c0e3ccf9 100644 --- a/tests/orchestrator/test_subagent_events.py +++ b/tests/orchestrator/test_subagent_events.py @@ -23,21 +23,21 @@ import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.events import ( +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.events import ( RunErrorEvent, SpawnSessionStart, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventEnvelope -from agentpool_server.opencode_server.models.parts import ( +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness_server.opencode_server.models.parts import ( ToolPart, ToolStateCompleted, ToolStateError, ToolStateRunning, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -84,7 +84,7 @@ async def broadcast_event(self, event: Any) -> None: self.events.append(event) def resolve_default_model_info(self) -> tuple[str, str]: - return "default", "agentpool" + return "default", "wolfharness" def _get_last_assistant_message(state: MockServerState, session_id: str) -> Any | None: diff --git a/tests/orchestrator/test_user_message_display_integration.py b/tests/orchestrator/test_user_message_display_integration.py index 07b3bff96..761033117 100644 --- a/tests/orchestrator/test_user_message_display_integration.py +++ b/tests/orchestrator/test_user_message_display_integration.py @@ -27,13 +27,13 @@ from pydantic_ai.models.function import AgentInfo, FunctionModel import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.events.events import ( +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.events.events import ( StreamCompleteEvent, UserMessageInsertedEvent, ) -from agentpool.lifecycle.types import DeliveryMode -from agentpool.orchestrator.event_bus import EventEnvelope +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.orchestrator.event_bus import EventEnvelope if TYPE_CHECKING: diff --git a/tests/orchestrator/test_user_message_inserted_integration.py b/tests/orchestrator/test_user_message_inserted_integration.py index 1e4770271..d9a174dda 100644 --- a/tests/orchestrator/test_user_message_inserted_integration.py +++ b/tests/orchestrator/test_user_message_inserted_integration.py @@ -15,21 +15,21 @@ import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunStartedEvent, StreamCompleteEvent, ToolCallStartEvent, UserMessageInsertedEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.event_bus import EventEnvelope -from agentpool.orchestrator.turn import Turn -from agentpool.utils.identifiers import ascending +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.event_bus import EventEnvelope +from wolfharness.orchestrator.turn import Turn +from wolfharness.utils.identifiers import ascending if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.agents.context import AgentRunContext + from wolfharness import AgentPool + from wolfharness.agents.context import AgentRunContext pytestmark = [pytest.mark.integration, pytest.mark.anyio] diff --git a/tests/phase8_merge_queue_removal_test.py b/tests/phase8_merge_queue_removal_test.py index 73fca95c6..e90412a58 100644 --- a/tests/phase8_merge_queue_removal_test.py +++ b/tests/phase8_merge_queue_removal_test.py @@ -10,7 +10,7 @@ import pytest -from agentpool.utils.streams import FileChange, FileOpsTracker +from wolfharness.utils.streams import FileChange, FileOpsTracker def test_merge_queue_into_iterator_raises_import_error() -> None: @@ -22,7 +22,7 @@ def test_merge_queue_into_iterator_raises_import_error() -> None: - Other stream utilities should still work """ with pytest.raises(ImportError): - from agentpool.utils.streams import merge_queue_into_iterator # noqa: F401 + from wolfharness.utils.streams import merge_queue_into_iterator # noqa: F401 # Verify other utilities still work assert FileChange is not None diff --git a/tests/phase8_shutdown_race_condition_test.py b/tests/phase8_shutdown_race_condition_test.py index cf75fb1f8..cb82b1b14 100644 --- a/tests/phase8_shutdown_race_condition_test.py +++ b/tests/phase8_shutdown_race_condition_test.py @@ -9,7 +9,7 @@ import anyio import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig @pytest.mark.asyncio diff --git a/tests/phase8_subagent_cascade_test.py b/tests/phase8_subagent_cascade_test.py index fa5134aa4..dbeaafb5a 100644 --- a/tests/phase8_subagent_cascade_test.py +++ b/tests/phase8_subagent_cascade_test.py @@ -9,7 +9,7 @@ import anyio import pytest -from agentpool import AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig @pytest.mark.asyncio diff --git a/tests/prompts/test_instructions_types.py b/tests/prompts/test_instructions_types.py index ea3aa56f6..462a3e8a1 100644 --- a/tests/prompts/test_instructions_types.py +++ b/tests/prompts/test_instructions_types.py @@ -8,7 +8,7 @@ import pytest # Test that all types can be imported (will fail if they don't exist) -from agentpool.prompts.instructions import ( +from wolfharness.prompts.instructions import ( AgentContextInstruction, BothContextsInstruction, InstructionFunc, @@ -26,7 +26,7 @@ from pydantic_ai import RunContext - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext class TestSimpleInstruction: diff --git a/tests/prompts/test_prompt_manager.py b/tests/prompts/test_prompt_manager.py index ae7df4c51..b16b50a6d 100644 --- a/tests/prompts/test_prompt_manager.py +++ b/tests/prompts/test_prompt_manager.py @@ -3,8 +3,8 @@ import pytest import yaml -from agentpool import AgentsManifest -from agentpool.prompts.manager import PromptManager +from wolfharness import AgentsManifest +from wolfharness.prompts.manager import PromptManager pytestmark = pytest.mark.unit diff --git a/tests/prompts/test_sys_prompts.py b/tests/prompts/test_sys_prompts.py index 578e6e857..deed12eba 100644 --- a/tests/prompts/test_sys_prompts.py +++ b/tests/prompts/test_sys_prompts.py @@ -3,7 +3,7 @@ from pydantic import BaseModel import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit diff --git a/tests/providers/test_multimodal.py b/tests/providers/test_multimodal.py index 400ec2fd3..bfb34c5cb 100644 --- a/tests/providers/test_multimodal.py +++ b/tests/providers/test_multimodal.py @@ -5,7 +5,7 @@ from pydantic_ai import ImageUrl import pytest -from agentpool import Agent +from wolfharness import Agent @pytest.mark.flaky(reruns=2) diff --git a/tests/providers/test_structured.py b/tests/providers/test_structured.py index 2f143747a..9b2d930a4 100644 --- a/tests/providers/test_structured.py +++ b/tests/providers/test_structured.py @@ -4,7 +4,7 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit diff --git a/tests/repomap/test_repomap_imports.py b/tests/repomap/test_repomap_imports.py index fd1a1dd67..363341547 100644 --- a/tests/repomap/test_repomap_imports.py +++ b/tests/repomap/test_repomap_imports.py @@ -15,13 +15,13 @@ REPOMAP_MODULES = [ - "agentpool.repomap.context", - "agentpool.repomap.core", - "agentpool.repomap.languages", - "agentpool.repomap.outline", - "agentpool.repomap.tags", - "agentpool.repomap.types", - "agentpool.repomap.utils", + "wolfharness.repomap.context", + "wolfharness.repomap.core", + "wolfharness.repomap.languages", + "wolfharness.repomap.outline", + "wolfharness.repomap.tags", + "wolfharness.repomap.types", + "wolfharness.repomap.utils", ] diff --git a/tests/resources/test_loading_resources.py b/tests/resources/test_loading_resources.py index 583bdad3a..249632c2f 100644 --- a/tests/resources/test_loading_resources.py +++ b/tests/resources/test_loading_resources.py @@ -4,8 +4,8 @@ from upathtools.filesystems import UnionFileSystem import yamling -from agentpool import AgentPool -from agentpool.models import AgentsManifest +from wolfharness import AgentPool +from wolfharness.models import AgentsManifest pytestmark = pytest.mark.integration diff --git a/tests/running/test_delegation.py b/tests/running/test_delegation.py index 4127cd548..328d03b58 100644 --- a/tests/running/test_delegation.py +++ b/tests/running/test_delegation.py @@ -6,16 +6,16 @@ import pytest -from agentpool.agents import Agent -from agentpool.agents.base_agent import BaseAgent # noqa: TC001 -from agentpool.running import NodeInjectionError, with_nodes +from wolfharness.agents import Agent +from wolfharness.agents.base_agent import BaseAgent # noqa: TC001 +from wolfharness.running import NodeInjectionError, with_nodes pytestmark = pytest.mark.unit if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool async def test_basic_injection(pool: AgentPool): diff --git a/tests/running/test_discovery.py b/tests/running/test_discovery.py index 200ffae2d..89eb4bd71 100644 --- a/tests/running/test_discovery.py +++ b/tests/running/test_discovery.py @@ -2,7 +2,7 @@ import pytest -from agentpool.running import NodeFunction, node_function +from wolfharness.running import NodeFunction, node_function pytestmark = pytest.mark.unit diff --git a/tests/running/test_executor.py b/tests/running/test_executor.py index 9527a4860..a0b5f6c66 100644 --- a/tests/running/test_executor.py +++ b/tests/running/test_executor.py @@ -6,9 +6,9 @@ import anyio import pytest -from agentpool import Agent # noqa: TC001 -from agentpool.running.discovery import node_function -from agentpool.running.executor import ( +from wolfharness import Agent # noqa: TC001 +from wolfharness.running.discovery import node_function +from wolfharness.running.executor import ( ExecutionError, _group_parallel, _sort_functions, diff --git a/tests/security/test_skill_security.py b/tests/security/test_skill_security.py index 4041fa9dd..1faa01ad8 100644 --- a/tests/security/test_skill_security.py +++ b/tests/security/test_skill_security.py @@ -17,8 +17,8 @@ import pytest from upathtools import UPath -from agentpool.skills.exceptions import SecurityError -from agentpool.skills.uri_resolver import ResolvedSkillURI +from wolfharness.skills.exceptions import SecurityError +from wolfharness.skills.uri_resolver import ResolvedSkillURI pytestmark = pytest.mark.unit @@ -31,7 +31,7 @@ def _make_skill(skill_dir): """Create a Skill object from a filesystem directory.""" - from agentpool.skills.skill import Skill + from wolfharness.skills.skill import Skill return Skill( name="test-skill", @@ -92,7 +92,7 @@ def local_skill(skill_with_references): @pytest.mark.security async def test_local_path_traversal_absolute_path(local_skill): """Test path traversal with absolute path attempt: /etc/passwd.""" - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises(SecurityError): await _load_reference_content(local_skill, "/etc/passwd") @@ -102,7 +102,7 @@ async def test_local_path_traversal_absolute_path(local_skill): @pytest.mark.security async def test_local_path_traversal_basic_dotdot(local_skill): """Test basic path traversal: ../../../etc/passwd.""" - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises(SecurityError) as exc_info: await _load_reference_content(local_skill, "../../../etc/passwd") @@ -114,7 +114,7 @@ async def test_local_path_traversal_basic_dotdot(local_skill): @pytest.mark.security async def test_local_path_traversal_embedded(local_skill): """Test embedded path traversal: subdir/../../../etc/passwd.""" - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises(SecurityError) as exc_info: await _load_reference_content(local_skill, "subdir/../../../etc/passwd") @@ -126,7 +126,7 @@ async def test_local_path_traversal_embedded(local_skill): @pytest.mark.security async def test_local_path_traversal_multiple_dotdot(local_skill): """Test multiple .. sequences: ../../../../../../../etc/passwd.""" - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises(SecurityError) as exc_info: await _load_reference_content(local_skill, "../../../../../../../etc/passwd") @@ -138,7 +138,7 @@ async def test_local_path_traversal_multiple_dotdot(local_skill): @pytest.mark.security async def test_local_path_traversal_leading_dotdot(local_skill): """Test leading .. sequence: ../etc/passwd.""" - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises(SecurityError) as exc_info: await _load_reference_content(local_skill, "../etc/passwd") @@ -150,8 +150,8 @@ async def test_local_path_traversal_leading_dotdot(local_skill): @pytest.mark.security async def test_local_path_traversal_mixed_separators(local_skill): r"""Test path traversal with mixed separators: ..\..\..\etc\passwd.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content # On Unix, backslash is treated as literal character # This test verifies the path is rejected @@ -168,8 +168,8 @@ async def test_local_path_traversal_mixed_separators(local_skill): @pytest.mark.security async def test_local_url_encoded_traversal_percent_2f(local_skill): """Test URL-encoded path traversal: ..%2f..%2f..%2fetc%2fpasswd.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content # _load_reference_content does not URL-decode before checking # This should fail as file not found (path still blocked) @@ -181,8 +181,8 @@ async def test_local_url_encoded_traversal_percent_2f(local_skill): @pytest.mark.security async def test_local_url_encoded_traversal_lowercase(local_skill): """Test URL-encoded with lowercase: ..%2f..%2fetc%2fpasswd.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises((SecurityError, ReferenceNotFoundError)): await _load_reference_content(local_skill, "..%2f..%2fetc%2fpasswd") @@ -192,8 +192,8 @@ async def test_local_url_encoded_traversal_lowercase(local_skill): @pytest.mark.security async def test_local_url_encoded_traversal_uppercase(local_skill): """Test URL-encoded with uppercase: ..%2F..%2Fetc%2Fpasswd.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises((SecurityError, ReferenceNotFoundError)): await _load_reference_content(local_skill, "..%2F..%2Fetc%2Fpasswd") @@ -203,8 +203,8 @@ async def test_local_url_encoded_traversal_uppercase(local_skill): @pytest.mark.security async def test_local_url_encoded_dot(local_skill): """Test URL-encoded dot: %2e%2e/%2e%2e/%2e%2e/etc/passwd.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content # %2e is encoded dot, but _load_reference_content doesn't URL-decode with pytest.raises((SecurityError, ReferenceNotFoundError)): @@ -220,8 +220,8 @@ async def test_local_url_encoded_dot(local_skill): @pytest.mark.security async def test_local_null_byte_injection(local_skill): r"""Test null byte injection: file\x00.txt.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises((SecurityError, ReferenceNotFoundError)): await _load_reference_content(local_skill, "file\x00.txt") @@ -231,8 +231,8 @@ async def test_local_null_byte_injection(local_skill): @pytest.mark.security async def test_local_null_byte_injection_with_path(local_skill): r"""Test null byte injection with path: subdir/file\x00.txt.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises((SecurityError, ReferenceNotFoundError)): await _load_reference_content(local_skill, "subdir/file\x00.txt") @@ -242,8 +242,8 @@ async def test_local_null_byte_injection_with_path(local_skill): @pytest.mark.security async def test_local_null_byte_at_start(local_skill): r"""Test null byte at start of path: \x00file.txt.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content with pytest.raises((SecurityError, ReferenceNotFoundError)): await _load_reference_content(local_skill, "\x00file.txt") @@ -263,8 +263,8 @@ async def test_local_symlink_to_outside_directory(skill_with_references): the references directory. The _load_reference_content should resolve the symlink and reject the path. """ - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content refs_dir = skill_with_references / "references" @@ -295,8 +295,8 @@ async def test_local_symlink_chain_traversal(skill_with_references): Creates a chain of symlinks where the final target is outside the allowed directory. """ - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content refs_dir = skill_with_references / "references" subdir = refs_dir / "subdir" @@ -478,8 +478,8 @@ def test_uri_multiple_null_bytes(): @pytest.mark.security async def test_local_empty_path(local_skill): """Test empty path handling.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content # Empty path resolves to the skill directory itself — read_text fails # because it's a directory, not a file. @@ -504,8 +504,8 @@ def test_uri_empty_path(): @pytest.mark.security async def test_local_single_dot(local_skill): """Test single dot path: ./file.txt.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content # Single dot should be allowed (refers to current directory) # But file doesn't exist, so ReferenceNotFoundError @@ -517,7 +517,7 @@ async def test_local_single_dot(local_skill): @pytest.mark.security async def test_local_dot_slash_prefix(local_skill): """Test dot slash prefix: ./references/guide.md.""" - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness_toolsets.builtin.skills import _load_reference_content # This should work - single dot is not traversal content = await _load_reference_content(local_skill, "references/guide.md") @@ -528,8 +528,8 @@ async def test_local_dot_slash_prefix(local_skill): @pytest.mark.security async def test_local_path_with_special_chars(local_skill): """Test path with special characters that are NOT traversal.""" - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content # These should be treated as literal filenames (which don't exist) with pytest.raises(ReferenceNotFoundError): @@ -569,8 +569,8 @@ async def test_all_attacks_raise_security_error_or_blocked(local_skill): 3. Null byte injection 4. Symlink-based attacks """ - from agentpool.skills.exceptions import ReferenceNotFoundError - from agentpool_toolsets.builtin.skills import _load_reference_content + from wolfharness.skills.exceptions import ReferenceNotFoundError + from wolfharness_toolsets.builtin.skills import _load_reference_content attacks_blocked = [] @@ -619,7 +619,7 @@ def test_security_considerations_documented(): This test checks that SecurityError has appropriate docstrings and is properly exported from the exceptions module. """ - from agentpool.skills.exceptions import SecurityError + from wolfharness.skills.exceptions import SecurityError # Verify SecurityError can be instantiated error = SecurityError("Test security violation") @@ -627,7 +627,7 @@ def test_security_considerations_documented(): assert "Test security violation" in str(error) # Verify it's a proper exception hierarchy - from agentpool.skills.exceptions import SkillError + from wolfharness.skills.exceptions import SkillError assert issubclass(SecurityError, SkillError) diff --git a/tests/server/agui/test_skill_tools.py b/tests/server/agui/test_skill_tools.py index 88bdcf458..0d2cab42c 100644 --- a/tests/server/agui/test_skill_tools.py +++ b/tests/server/agui/test_skill_tools.py @@ -9,9 +9,9 @@ import pytest from upathtools import UPath -from agentpool.skills.command import SkillCommand -from agentpool.skills.skill import Skill -from agentpool_server.agui_server.skill_tools import ( +from wolfharness.skills.command import SkillCommand +from wolfharness.skills.skill import Skill +from wolfharness_server.agui_server.skill_tools import ( AGUISkillBridge, AGUISkillToolAdapter, ) diff --git a/tests/servers/acp_server/conftest.py b/tests/servers/acp_server/conftest.py index 94592e27d..581379a4e 100644 --- a/tests/servers/acp_server/conftest.py +++ b/tests/servers/acp_server/conftest.py @@ -8,11 +8,11 @@ from acp import ClientCapabilities, DefaultACPClient, FileSystemCapability from acp.agent.implementations import TestAgent -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent @pytest.fixture diff --git a/tests/servers/acp_server/test_acp_agent_integration.py b/tests/servers/acp_server/test_acp_agent_integration.py index 8c8e70d03..d204c7e91 100644 --- a/tests/servers/acp_server/test_acp_agent_integration.py +++ b/tests/servers/acp_server/test_acp_agent_integration.py @@ -1,6 +1,6 @@ -"""Integration tests for ACPAgent connecting to real agentpool ACP server. +"""Integration tests for ACPAgent connecting to real wolfharness ACP server. -These tests spawn an actual agentpool serve-acp process with a TestModel +These tests spawn an actual wolfharness serve-acp process with a TestModel and connect to it using ACPAgent, testing the full roundtrip. Note: These are slow integration tests that spawn real subprocesses. @@ -16,12 +16,12 @@ import anyio import pytest -from agentpool.agents.acp_agent import ACPAgent -from agentpool.models.acp_agents import ACPAgentConfig +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.models.acp_agents import ACPAgentConfig if TYPE_CHECKING: - from agentpool.agents.events import RichAgentStreamEvent + from wolfharness.agents.events import RichAgentStreamEvent # Mark all tests in this module as slow/integration @@ -53,12 +53,12 @@ def test_config_file(test_agent_config_yaml: str, tmp_path: Path) -> Path: @pytest.fixture def acp_agent_config(test_config_file: Path) -> ACPAgentConfig: - """Create ACPAgentConfig that spawns agentpool serve-acp.""" + """Create ACPAgentConfig that spawns wolfharness serve-acp.""" return ACPAgentConfig( command="uv", args=[ "run", - "agentpool", + "wolfharness", "serve-acp", str(test_config_file), "--agent", @@ -137,7 +137,7 @@ async def test_acp_agent_file_operations(tmp_path: Path, test_config_file: Path) command="uv", args=[ "run", - "agentpool", + "wolfharness", "serve-acp", str(test_config_file), "--agent", @@ -160,7 +160,7 @@ async def test_acp_agent_terminal_operations(tmp_path: Path, test_config_file: P command="uv", args=[ "run", - "agentpool", + "wolfharness", "serve-acp", str(test_config_file), "--agent", @@ -207,7 +207,7 @@ async def test_acp_agent_stats(acp_agent_config: ACPAgentConfig): async def test_acp_agent_with_input_provider(acp_agent_config: ACPAgentConfig): """Test ACPAgent with custom input provider.""" - from agentpool.ui.stdlib_provider import StdlibInputProvider + from wolfharness.ui.stdlib_provider import StdlibInputProvider input_provider = StdlibInputProvider() @@ -234,7 +234,7 @@ async def test_acp_agent_with_input_provider(acp_agent_config: ACPAgentConfig): async def test_acp_agent_input_provider_in_run_stream(acp_agent_config: ACPAgentConfig): """Test input_provider parameter in run_stream method.""" - from agentpool.ui.stdlib_provider import StdlibInputProvider + from wolfharness.ui.stdlib_provider import StdlibInputProvider input_provider = StdlibInputProvider() diff --git a/tests/servers/acp_server/test_acp_available_commands.py b/tests/servers/acp_server/test_acp_available_commands.py index 39ae53e52..ca578bbdc 100644 --- a/tests/servers/acp_server/test_acp_available_commands.py +++ b/tests/servers/acp_server/test_acp_available_commands.py @@ -13,15 +13,15 @@ import pytest from acp.schema import AvailableCommand -from agentpool import Agent, AgentPool -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.session import ACPSession +from wolfharness import Agent, AgentPool +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.session import ACPSession def _make_pool_and_agent() -> tuple[AgentPool, Agent]: """Create a simple pool with one agent.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) diff --git a/tests/servers/acp_server/test_acp_cancel_then_prompt.py b/tests/servers/acp_server/test_acp_cancel_then_prompt.py index 67f66b921..06736124c 100644 --- a/tests/servers/acp_server/test_acp_cancel_then_prompt.py +++ b/tests/servers/acp_server/test_acp_cancel_then_prompt.py @@ -19,16 +19,16 @@ import pytest -from agentpool.agents.events import RunFailedEvent, RunStartedEvent, StreamCompleteEvent -from agentpool.lifecycle import DirectChannel, MemoryJournal -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventEnvelope -from agentpool.orchestrator.turn import Turn +from wolfharness.agents.events import RunFailedEvent, RunStartedEvent, StreamCompleteEvent +from wolfharness.lifecycle import DirectChannel, MemoryJournal +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness.orchestrator.turn import Turn if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.agents.context import AgentRunContext + from wolfharness import AgentPool + from wolfharness.agents.context import AgentRunContext pytestmark = [pytest.mark.unit, pytest.mark.anyio] diff --git a/tests/servers/acp_server/test_acp_elicitation_resume.py b/tests/servers/acp_server/test_acp_elicitation_resume.py index 3af817eaf..b6d0b253c 100644 --- a/tests/servers/acp_server/test_acp_elicitation_resume.py +++ b/tests/servers/acp_server/test_acp_elicitation_resume.py @@ -20,14 +20,14 @@ import pytest -from agentpool.agents.events.events import ElicitationDeferredEvent -from agentpool_server.acp_server.event_converter import ACPEventConverter -from agentpool_server.acp_server.handler import ACPProtocolHandler -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.agents.events.events import ElicitationDeferredEvent +from wolfharness_server.acp_server.event_converter import ACPEventConverter +from wolfharness_server.acp_server.handler import ACPProtocolHandler +from wolfharness_server.acp_server.session_manager import ACPSessionManager if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -130,7 +130,7 @@ async def mock_resume(sid: str, **kwargs: object) -> None: "client", ), patch( - "agentpool_server.acp_server.handler.ACPRequests", + "wolfharness_server.acp_server.handler.ACPRequests", ) as mock_acp_requests_class, ): mock_acp_requests = MagicMock() @@ -177,7 +177,7 @@ async def mock_resume(sid: str, **kwargs: object) -> None: minimal_pool.session_pool.resume_session = mock_resume # type: ignore[assignment] with patch( - "agentpool_server.acp_server.handler.ACPRequests", + "wolfharness_server.acp_server.handler.ACPRequests", ) as mock_acp_requests_class: mock_acp_requests = MagicMock() mock_acp_requests.elicitation_create = AsyncMock( diff --git a/tests/servers/acp_server/test_acp_integration.py b/tests/servers/acp_server/test_acp_integration.py index 3c0546d00..a5b27e962 100644 --- a/tests/servers/acp_server/test_acp_integration.py +++ b/tests/servers/acp_server/test_acp_integration.py @@ -10,10 +10,10 @@ from acp import ClientCapabilities # Add another agent to the pool for switching -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool_server.acp_server import ACPServer -from agentpool_server.acp_server.session import ACPSession +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness_server.acp_server import ACPServer +from wolfharness_server.acp_server.session import ACPSession pytestmark = pytest.mark.integration @@ -22,8 +22,8 @@ @pytest.fixture async def agent_pool(): """Create a real agent pool from config.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -47,8 +47,8 @@ async def test_acp_server_creation(agent_pool: AgentPool): async def test_agent_switching_workflow(agent_pool: AgentPool, mock_acp_agent): """Test the complete agent switching workflow.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest config1 = NativeAgentConfig(name="agent1", model="test") config2 = NativeAgentConfig(name="agent2", model="test") diff --git a/tests/servers/acp_server/test_acp_load.py b/tests/servers/acp_server/test_acp_load.py index e005892b6..747d0730a 100644 --- a/tests/servers/acp_server/test_acp_load.py +++ b/tests/servers/acp_server/test_acp_load.py @@ -21,13 +21,13 @@ import pytest from acp.schema import LoadSessionRequest, LoadSessionResponse -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.sessions.models import PendingDeferredCall -from agentpool.storage.manager import StorageManager -from agentpool.storage.serialization import serialize_messages -from agentpool_config.storage import MemoryStorageConfig, StorageConfig -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.sessions.models import PendingDeferredCall +from wolfharness.storage.manager import StorageManager +from wolfharness.storage.serialization import serialize_messages +from wolfharness_config.storage import MemoryStorageConfig, StorageConfig +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent # ── Helpers ───────────────────────────────────────────────────────────── @@ -90,7 +90,7 @@ def _make_chat_messages_from_model_messages( Returns objects compatible with what ACPSession expects after agent.load_session() populates conversation.chat_messages. """ - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage return [ ChatMessage[Any]( @@ -119,8 +119,8 @@ def mock_agent_pool_with_agent() -> tuple[AgentPool, Agent]: def simple_callback(message: str) -> str: return f"Test response: {message}" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) diff --git a/tests/servers/acp_server/test_acp_mcp_transport_reuse.py b/tests/servers/acp_server/test_acp_mcp_transport_reuse.py index 71406b2b2..6c5f67cad 100644 --- a/tests/servers/acp_server/test_acp_mcp_transport_reuse.py +++ b/tests/servers/acp_server/test_acp_mcp_transport_reuse.py @@ -13,8 +13,8 @@ import pytest -from agentpool_server.acp_server.acp_mcp_manager import AcpMcpConnection -from agentpool_server.acp_server.acp_mcp_transport import AcpMcpTransport +from wolfharness_server.acp_server.acp_mcp_manager import AcpMcpConnection +from wolfharness_server.acp_server.acp_mcp_transport import AcpMcpTransport pytestmark = pytest.mark.integration diff --git a/tests/servers/acp_server/test_acp_protocol_handler_cancel.py b/tests/servers/acp_server/test_acp_protocol_handler_cancel.py index 11e14b452..6af101e67 100644 --- a/tests/servers/acp_server/test_acp_protocol_handler_cancel.py +++ b/tests/servers/acp_server/test_acp_protocol_handler_cancel.py @@ -15,13 +15,13 @@ import pytest -from agentpool_server.acp_server.event_converter import ACPEventConverter -from agentpool_server.acp_server.handler import ACPProtocolHandler -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness_server.acp_server.event_converter import ACPEventConverter +from wolfharness_server.acp_server.handler import ACPProtocolHandler +from wolfharness_server.acp_server.session_manager import ACPSessionManager if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit diff --git a/tests/servers/acp_server/test_acp_protocol_handler_input_provider.py b/tests/servers/acp_server/test_acp_protocol_handler_input_provider.py index 474ef8ca9..2eb21872c 100644 --- a/tests/servers/acp_server/test_acp_protocol_handler_input_provider.py +++ b/tests/servers/acp_server/test_acp_protocol_handler_input_provider.py @@ -19,10 +19,10 @@ import pytest from acp.schema import TextContentBlock -from agentpool.orchestrator.core import EventEnvelope -from agentpool.orchestrator.run import RunHandle -from agentpool_server.acp_server.handler import ACPProtocolHandler, _ACPSessionProxy -from agentpool_server.acp_server.input_provider import ACPInputProvider +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness.orchestrator.run import RunHandle +from wolfharness_server.acp_server.handler import ACPProtocolHandler, _ACPSessionProxy +from wolfharness_server.acp_server.input_provider import ACPInputProvider pytestmark = pytest.mark.unit @@ -281,7 +281,7 @@ async def test_event_consumer_passes_turn_complete_true( ) -> None: """When client supports turn_complete, converter is created with flag=True.""" from acp.schema.capabilities import ClientCapabilities - from agentpool_server.acp_server.handler import ACPEventConverter + from wolfharness_server.acp_server.handler import ACPEventConverter handler = ACPProtocolHandler( host_context=mock_pool, @@ -460,7 +460,7 @@ async def test_event_consumer_defaults_turn_complete_when_no_capabilities( mock_client: MagicMock, ) -> None: """When client_capabilities is None, converter defaults to flag=False.""" - from agentpool_server.acp_server.handler import ACPEventConverter + from wolfharness_server.acp_server.handler import ACPEventConverter handler = ACPProtocolHandler( host_context=mock_pool, @@ -510,8 +510,8 @@ async def mock_convert(event): mock_converter.subagent_meta = {} handler._converters["parent-sid"] = mock_converter - from agentpool.agents.events import StreamCompleteEvent - from agentpool.messaging import ChatMessage + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.messaging import ChatMessage # Child event wrapped in EventEnvelope with source_session_id event = StreamCompleteEvent( @@ -556,8 +556,8 @@ async def mock_convert(event): mock_converter.subagent_meta = {} handler._converters["parent-sid"] = mock_converter - from agentpool.agents.events import StreamCompleteEvent - from agentpool.messaging import ChatMessage + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.messaging import ChatMessage # Event without session_id wrapped in EventEnvelope with empty source_session_id event = StreamCompleteEvent( diff --git a/tests/servers/acp_server/test_acp_resume.py b/tests/servers/acp_server/test_acp_resume.py index 0349614e4..3b319104a 100644 --- a/tests/servers/acp_server/test_acp_resume.py +++ b/tests/servers/acp_server/test_acp_resume.py @@ -20,10 +20,10 @@ import pytest from acp.schema import ClientCapabilities, ResumeSessionRequest -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.sessions.models import SessionData -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.sessions.models import SessionData +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent # --------------------------------------------------------------------------- @@ -44,8 +44,8 @@ def mock_agent_pool_with_agent(): def simple_callback(message: str) -> str: return f"Test response: {message}" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -273,8 +273,8 @@ async def test_resume_loads_agent_state(mocked_acp_agent): @pytest.mark.unit async def test_handle_prompt_checkpointed_session_resumes(mocked_acp_agent, mock_connection): """handle_prompt should resume a checkpointed session instead of creating a new one.""" - from agentpool_server.acp_server.event_converter import ACPEventConverter - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.handler import ACPProtocolHandler session_data = _make_session_data(status="checkpointed") @@ -330,8 +330,8 @@ async def test_handle_prompt_checkpointed_session_resumes(mocked_acp_agent, mock @pytest.mark.unit async def test_handle_prompt_active_session_uses_create_session(mocked_acp_agent, mock_connection): """handle_prompt for a non-checkpointed (active/new) session should call create_session.""" - from agentpool_server.acp_server.event_converter import ACPEventConverter - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.handler import ACPProtocolHandler session_data = _make_session_data(status="active") @@ -532,8 +532,8 @@ async def test_resume_then_handle_prompt_no_duplicate_agent_creation( mocked_acp_agent, mock_connection ): from acp.schema.content_blocks import TextContentBlock - from agentpool_server.acp_server.event_converter import ACPEventConverter - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.handler import ACPProtocolHandler session_data = _make_session_data() mock_session = _make_mock_session() diff --git a/tests/servers/acp_server/test_acp_resume_integration.py b/tests/servers/acp_server/test_acp_resume_integration.py index bbd97246c..87f104c24 100644 --- a/tests/servers/acp_server/test_acp_resume_integration.py +++ b/tests/servers/acp_server/test_acp_resume_integration.py @@ -15,13 +15,13 @@ import pytest from acp.schema import LoadSessionRequest, ResumeSessionRequest -from agentpool.sessions.models import SessionData -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness.sessions.models import SessionData +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.session_manager import ACPSessionManager if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool # --------------------------------------------------------------------------- @@ -124,7 +124,7 @@ async def test_create_then_resume_preserves_conversation_history( # Arrange: store session data session_manager.session_store.load_session = AsyncMock(return_value=known_session_data) # type: ignore[union-attr] - with patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls: + with patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls: mock_session_instance = MagicMock() mock_session_instance.session_id = "sess-abc-123" mock_session_instance.initialize = AsyncMock() @@ -162,7 +162,7 @@ async def test_resume_preserves_created_at_timestamp( # Arrange session_manager.session_store.load_session = AsyncMock(return_value=known_session_data) # type: ignore[union-attr] - with patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls: + with patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls: mock_session_instance = MagicMock() mock_session_instance.initialize = AsyncMock() mock_session_instance.initialize_mcp_servers = AsyncMock() @@ -197,7 +197,7 @@ async def test_resume_preserves_status_field( # Arrange session_manager.session_store.load_session = AsyncMock(return_value=checkpointed_session_data) # type: ignore[union-attr] - with patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls: + with patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls: mock_session_instance = MagicMock() mock_session_instance.initialize = AsyncMock() mock_session_instance.initialize_mcp_servers = AsyncMock() @@ -232,7 +232,7 @@ async def test_resume_with_mcp_servers_initializes_connections( session_manager.session_store.load_session = AsyncMock(return_value=known_session_data) # type: ignore[union-attr] mock_mcp_server = MagicMock() - with patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls: + with patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls: mock_session_instance = MagicMock() mock_session_instance.initialize = AsyncMock() mock_session_instance.initialize_mcp_servers = AsyncMock() @@ -327,7 +327,7 @@ async def test_no_duplicate_load_session_call_on_resume( # Arrange session_manager.session_store.load_session = AsyncMock(return_value=known_session_data) # type: ignore[union-attr] - with patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls: + with patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls: mock_session_instance = MagicMock() mock_session_instance.initialize = AsyncMock() mock_session_instance.initialize_mcp_servers = AsyncMock() diff --git a/tests/servers/acp_server/test_acp_session_load.py b/tests/servers/acp_server/test_acp_session_load.py index f47fafd54..68c94988f 100644 --- a/tests/servers/acp_server/test_acp_session_load.py +++ b/tests/servers/acp_server/test_acp_session_load.py @@ -14,10 +14,10 @@ SessionModelState, SessionModeState, ) -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.messaging import ChatMessage -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.messaging import ChatMessage +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent @pytest.fixture @@ -33,8 +33,8 @@ def mock_agent_pool_with_agent(): def simple_callback(message: str) -> str: return f"Test response: {message}" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -252,7 +252,7 @@ async def test_load_session_with_nested_acp_agent( mock_acp_agent, mock_session, load_session_request ): """Test load_session with nested ACP agent populates models/modes from agent state.""" - from agentpool.agents.acp_agent import ACPAgent + from wolfharness.agents.acp_agent import ACPAgent nested_agent = MagicMock(spec=ACPAgent) nested_agent._state = MagicMock() diff --git a/tests/servers/acp_server/test_acp_session_manager_child_session.py b/tests/servers/acp_server/test_acp_session_manager_child_session.py index 4b7300a79..58981917c 100644 --- a/tests/servers/acp_server/test_acp_session_manager_child_session.py +++ b/tests/servers/acp_server/test_acp_session_manager_child_session.py @@ -7,12 +7,12 @@ import pytest -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.orchestrator.core import SessionPool -from agentpool.sessions import SessionData -from agentpool_server.acp_server.session_manager import ACPSessionManager -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.orchestrator.core import SessionPool +from wolfharness.sessions import SessionData +from wolfharness_server.acp_server.session_manager import ACPSessionManager +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider pytestmark = pytest.mark.unit @@ -20,8 +20,8 @@ def _make_pool_with_sessions() -> tuple[AgentPool, Agent, SessionPool, MemoryStorageProvider]: """Create a pool with a real SessionPool backed by MemoryStorageProvider.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -48,7 +48,7 @@ def _make_acp_session_manager(pool: AgentPool) -> ACPSessionManager: # Mock out ACPSession creation and initialization to avoid needing # a real ACP client and all the initialization machinery. - with patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls: + with patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls: mock_session = MagicMock() mock_session.session_id = "session_top_001" mock_session.register_update_callback = MagicMock() @@ -68,8 +68,8 @@ async def test_top_level_session_has_no_parent(): mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -97,7 +97,7 @@ async def test_child_session_inherits_parent_project_id(): pool, agent, _sessions, store = _make_pool_with_sessions() # Create a parent session in the store first - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id parent_cwd = tempfile.gettempdir() parent_project_id = compute_project_id(parent_cwd) @@ -114,8 +114,8 @@ async def test_child_session_inherits_parent_project_id(): mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -149,7 +149,7 @@ async def test_child_session_uses_effective_cwd_for_acp_session(): pool, agent, _sessions, store = _make_pool_with_sessions() parent_cwd = tempfile.gettempdir() - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id parent_project_id = compute_project_id(parent_cwd) parent_data = SessionData( @@ -165,8 +165,8 @@ async def test_child_session_uses_effective_cwd_for_acp_session(): mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -198,8 +198,8 @@ async def test_no_parent_session_id_preserves_existing_behavior(): mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -226,8 +226,8 @@ async def test_no_parent_session_id_preserves_existing_behavior(): async def test_child_session_without_pool_sessions_falls_back_to_top_level(): """When pool.sessions is None but parent_session_id is provided, should fall back to....""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -247,8 +247,8 @@ def simple_callback(message: str) -> str: mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() diff --git a/tests/servers/acp_server/test_acp_session_process_prompt_turn_complete.py b/tests/servers/acp_server/test_acp_session_process_prompt_turn_complete.py index c43f6d9f7..15afa1171 100644 --- a/tests/servers/acp_server/test_acp_session_process_prompt_turn_complete.py +++ b/tests/servers/acp_server/test_acp_session_process_prompt_turn_complete.py @@ -14,9 +14,9 @@ from acp.schema import TextContentBlock from acp.schema.capabilities import ClientCapabilities -from agentpool import Agent, AgentPool -from agentpool_server.acp_server.event_converter import ACPEventConverter -from agentpool_server.acp_server.session import ACPSession +from wolfharness import Agent, AgentPool +from wolfharness_server.acp_server.event_converter import ACPEventConverter +from wolfharness_server.acp_server.session import ACPSession pytestmark = pytest.mark.unit @@ -33,8 +33,8 @@ def agent_pool() -> AgentPool: def simple_callback(message: str) -> str: return f"Response: {message}" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) diff --git a/tests/servers/acp_server/test_acp_session_resume.py b/tests/servers/acp_server/test_acp_session_resume.py index a45b2addf..51d8354b6 100644 --- a/tests/servers/acp_server/test_acp_session_resume.py +++ b/tests/servers/acp_server/test_acp_session_resume.py @@ -8,13 +8,13 @@ from acp.schema import LoadSessionRequest, ResumeSessionRequest, ResumeSessionResponse from acp.schema.mcp import StdioMcpServer -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.orchestrator.core import SessionPool -from agentpool.sessions import SessionData -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent -from agentpool_server.acp_server.session_manager import ACPSessionManager -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.orchestrator.core import SessionPool +from wolfharness.sessions import SessionData +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.session_manager import ACPSessionManager +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider @pytest.fixture @@ -30,8 +30,8 @@ def mock_agent_pool_with_agent(): def simple_callback(message: str) -> str: return f"Test response: {message}" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -189,8 +189,8 @@ async def test_resume_session_exception_returns_empty_response( @pytest.mark.unit async def test_resume_session_passes_mcp_servers_to_constructor(): """Test that resume_session passes mcp_servers to the ACPSession constructor.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -219,8 +219,8 @@ def _callback(message: str) -> str: mock_server = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -246,8 +246,8 @@ def _callback(message: str) -> str: @pytest.mark.unit async def test_resume_session_initializes_mcp_servers(): """Test that resume_session calls initialize_mcp_servers on the session.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -275,8 +275,8 @@ def _callback(message: str) -> str: mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -298,8 +298,8 @@ def _callback(message: str) -> str: @pytest.mark.unit async def test_resume_session_with_none_mcp_servers_calls_initialize(): """Test that resume_session still calls initialize_mcp_servers when mcp_servers is None....""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -327,8 +327,8 @@ def _callback(message: str) -> str: mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -356,8 +356,8 @@ async def test_resume_session_does_not_call_load_session(): get_or_create_session_agent(), so resume_session no longer calls agent.load_session() directly. """ - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -385,8 +385,8 @@ def _callback(message: str) -> str: mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() @@ -408,8 +408,8 @@ def _callback(message: str) -> str: @pytest.mark.unit async def test_resume_session_closes_old_and_recreates(): """Test resume_session closes old session and creates a fresh one.""" - from agentpool.models.agents import NativeAgentConfig - from agentpool.models.manifest import AgentsManifest + from wolfharness.models.agents import NativeAgentConfig + from wolfharness.models.manifest import AgentsManifest manifest = AgentsManifest(agents={"test_agent": NativeAgentConfig(model="test")}) @@ -437,8 +437,8 @@ def _callback(message: str) -> str: mock_acp_agent = MagicMock() with ( - patch("agentpool_server.acp_server.session_manager.ACPSession") as mock_session_cls, - patch("agentpool_server.acp_server.session_manager.ClientCapabilities"), + patch("wolfharness_server.acp_server.session_manager.ACPSession") as mock_session_cls, + patch("wolfharness_server.acp_server.session_manager.ClientCapabilities"), ): mock_session = MagicMock() mock_session.register_update_callback = MagicMock() diff --git a/tests/servers/acp_server/test_acp_skills_red_flags.py b/tests/servers/acp_server/test_acp_skills_red_flags.py index 5bdd0eefe..0e2ca8e5b 100644 --- a/tests/servers/acp_server/test_acp_skills_red_flags.py +++ b/tests/servers/acp_server/test_acp_skills_red_flags.py @@ -13,8 +13,8 @@ import pytest from upathtools import UPath -from agentpool.skills.manager import SkillsManager -from agentpool_config.skills import SkillsConfig +from wolfharness.skills.manager import SkillsManager +from wolfharness_config.skills import SkillsConfig pytestmark = pytest.mark.unit @@ -54,12 +54,12 @@ def test_skills_config_include_default_true(self) -> None: assert UPath("./custom-skills/") in paths, "Custom path missing" assert len(paths) > 1, "Default paths not included when include_default=true" - def test_agentpool_acp_agent_load_skills_defaults_to_none(self) -> None: + def test_wolfharness_acp_agent_load_skills_defaults_to_none(self) -> None: """AgentPoolACPAgent.load_skills defaults to None. None means "use manifest's include_default setting". """ - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent assert AgentPoolACPAgent.load_skills is None, ( "AgentPoolACPAgent.load_skills should default to None, " @@ -73,9 +73,9 @@ def test_acp_server_from_config_uses_manifest_include_default(self) -> None: use manifest.skills.include_default as the default. """ # Create a manifest with include_default=False - from agentpool.models.manifest import AgentsManifest - from agentpool_config.skills import SkillsConfig - from agentpool_server.acp_server.server import ACPServer + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.skills import SkillsConfig + from wolfharness_server.acp_server.server import ACPServer manifest = AgentsManifest( skills=SkillsConfig( @@ -97,9 +97,9 @@ def test_acp_server_from_config_uses_manifest_include_default(self) -> None: def test_acp_server_from_config_explicit_load_skills_overrides_manifest(self) -> None: """Explicit load_skills argument must override manifest's include_default.""" - from agentpool.models.manifest import AgentsManifest - from agentpool_config.skills import SkillsConfig - from agentpool_server.acp_server.server import ACPServer + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.skills import SkillsConfig + from wolfharness_server.acp_server.server import ACPServer manifest = AgentsManifest( skills=SkillsConfig( @@ -133,8 +133,8 @@ async def test_init_client_skills_respects_none_load_skills(self) -> None: # Create a real AgentPool with include_default=False import yamling - from agentpool import AgentPool, AgentsManifest - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness import AgentPool, AgentsManifest + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent config = """\ agents: @@ -175,7 +175,7 @@ def test_serve_acp_cli_load_skills_defaults_to_none(self) -> None: """ import inspect - from agentpool_cli.serve_acp import acp_command + from wolfharness_cli.serve_acp import acp_command sig = inspect.signature(acp_command) load_skills_param = sig.parameters.get("load_skills") @@ -191,8 +191,8 @@ def test_manifest_include_default_controls_acp_skill_loading(self) -> None: When skills.include_default=false in manifest and no explicit CLI override, ACP server should NOT load .claude/skills/. """ - from agentpool.models.manifest import AgentsManifest - from agentpool_config.skills import SkillsConfig + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.skills import SkillsConfig manifest = AgentsManifest( skills=SkillsConfig( @@ -202,7 +202,7 @@ def test_manifest_include_default_controls_acp_skill_loading(self) -> None: ) # Without explicit override, load_skills should follow manifest - from agentpool_server.acp_server.server import ACPServer + from wolfharness_server.acp_server.server import ACPServer server = ACPServer.from_config(manifest) assert server.load_skills is False, ( @@ -212,9 +212,9 @@ def test_manifest_include_default_controls_acp_skill_loading(self) -> None: def test_manifest_include_default_true_loads_skills(self) -> None: """Manifest's include_default=True must enable ACP skill loading.""" - from agentpool.models.manifest import AgentsManifest - from agentpool_config.skills import SkillsConfig - from agentpool_server.acp_server.server import ACPServer + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.skills import SkillsConfig + from wolfharness_server.acp_server.server import ACPServer manifest = AgentsManifest( skills=SkillsConfig( @@ -238,10 +238,10 @@ async def test_local_resource_provider_respects_include_default_false(self, tmp_ """ import logging - from agentpool_config.skills import SkillsConfig + from wolfharness_config.skills import SkillsConfig # Enable debug logging - logging.getLogger("agentpool.skills").setLevel(logging.DEBUG) + logging.getLogger("wolfharness.skills").setLevel(logging.DEBUG) # Create a custom skill directory with one skill custom_skills_dir = tmp_path / "custom-skills" diff --git a/tests/servers/acp_server/test_acp_v2_extensions.py b/tests/servers/acp_server/test_acp_v2_extensions.py index d4ede8f01..b787732f1 100644 --- a/tests/servers/acp_server/test_acp_v2_extensions.py +++ b/tests/servers/acp_server/test_acp_v2_extensions.py @@ -11,9 +11,9 @@ import pytest from acp.schema.session_state import SessionInfo -from agentpool.sessions.models import PendingDeferredCall, SessionData -from agentpool_server.acp_server.converters import to_session_info -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.sessions.models import PendingDeferredCall, SessionData +from wolfharness_server.acp_server.converters import to_session_info +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = pytest.mark.integration @@ -135,7 +135,7 @@ async def test_close_session_preserves_checkpointed_with_pending_calls( pending deferred calls, the session should be marked as 'checkpointed' and NOT deleted from storage. """ - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider store = MemoryStorageProvider() @@ -154,7 +154,7 @@ async def test_close_session_deletes_when_no_pending_calls( Normal close (no deferred calls) should delete the session from storage. """ - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider store = MemoryStorageProvider() diff --git a/tests/servers/acp_server/test_agent_role.py b/tests/servers/acp_server/test_agent_role.py index 5b5a673af..f67b2d983 100644 --- a/tests/servers/acp_server/test_agent_role.py +++ b/tests/servers/acp_server/test_agent_role.py @@ -9,8 +9,8 @@ from acp.exceptions import RequestError from acp.schema import SessionConfigOption -from agentpool import AgentPool, AgentsManifest -from agentpool_server.acp_server.acp_agent import ( +from wolfharness import AgentPool, AgentsManifest +from wolfharness_server.acp_server.acp_agent import ( AgentPoolACPAgent, get_agent_role_config_option, ) diff --git a/tests/servers/acp_server/test_claude_acp_toolset_integration.py b/tests/servers/acp_server/test_claude_acp_toolset_integration.py index 0c10b4403..a426ab14c 100644 --- a/tests/servers/acp_server/test_claude_acp_toolset_integration.py +++ b/tests/servers/acp_server/test_claude_acp_toolset_integration.py @@ -12,10 +12,10 @@ import pytest -from agentpool import AgentPool -from agentpool.agents.acp_agent import ACPAgent -from agentpool.models.acp_agents.base import ACPAgentConfig -from agentpool_config.toolsets import SubagentToolsetConfig +from wolfharness import AgentPool +from wolfharness.agents.acp_agent import ACPAgent +from wolfharness.models.acp_agents.base import ACPAgentConfig +from wolfharness_config.toolsets import SubagentToolsetConfig if not shutil.which("claude-code-acp"): @@ -51,7 +51,7 @@ async def test_claude_acp_tool_bridge_mcp_config(claude_config_with_subagent: AC async def test_claude_acp_multiple_toolsets(): """Test Claude ACP agent with multiple toolsets.""" - from agentpool_config.toolsets import DebugToolsetConfig + from wolfharness_config.toolsets import DebugToolsetConfig tools = [SubagentToolsetConfig(), DebugToolsetConfig()] config = ACPAgentConfig( diff --git a/tests/servers/acp_server/test_client_filesystem_tools.py b/tests/servers/acp_server/test_client_filesystem_tools.py index 29c61590b..800a7bb94 100644 --- a/tests/servers/acp_server/test_client_filesystem_tools.py +++ b/tests/servers/acp_server/test_client_filesystem_tools.py @@ -5,9 +5,9 @@ from exxec import MockExecutionEnvironment import pytest -from agentpool import Agent, AgentContext -from agentpool.tools.base import ToolResult -from agentpool_toolsets.fsspec_toolset import FSSpecTools +from wolfharness import Agent, AgentContext +from wolfharness.tools.base import ToolResult +from wolfharness_toolsets.fsspec_toolset import FSSpecTools pytestmark = pytest.mark.integration diff --git a/tests/servers/acp_server/test_command_bridge_streaming.py b/tests/servers/acp_server/test_command_bridge_streaming.py index 7558065eb..86c61c3cb 100644 --- a/tests/servers/acp_server/test_command_bridge_streaming.py +++ b/tests/servers/acp_server/test_command_bridge_streaming.py @@ -12,8 +12,8 @@ from slashed import Command from acp import ClientCapabilities -from agentpool import Agent, AgentPool -from agentpool_server.acp_server.session import ACPSession +from wolfharness import Agent, AgentPool +from wolfharness_server.acp_server.session import ACPSession pytestmark = pytest.mark.integration @@ -176,7 +176,7 @@ async def capture_message( sent_messages.append(message) async def capture_ext_notification(method: str, params: dict): - if method == "_agentpool/toast": + if method == "_wolfharness/toast": toast_messages.append(params.get("message", "")) session.notifications.send_agent_text = capture_message # type: ignore[method-assign] diff --git a/tests/servers/acp_server/test_dedup.py b/tests/servers/acp_server/test_dedup.py index 63c59870a..374cc256c 100644 --- a/tests/servers/acp_server/test_dedup.py +++ b/tests/servers/acp_server/test_dedup.py @@ -15,8 +15,8 @@ import pytest from acp.schema import UserMessageChunk -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = pytest.mark.unit diff --git a/tests/servers/acp_server/test_deferred_acp.py b/tests/servers/acp_server/test_deferred_acp.py index 6d192f485..88e73f6c1 100644 --- a/tests/servers/acp_server/test_deferred_acp.py +++ b/tests/servers/acp_server/test_deferred_acp.py @@ -16,8 +16,8 @@ from acp.schema import ToolCallStart from acp.tool_call_reporter import ToolCallReporter from acp.tool_call_state import ToolCallState -from agentpool.agents.events.events import ToolCallDeferredEvent -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events.events import ToolCallDeferredEvent +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = pytest.mark.integration diff --git a/tests/servers/acp_server/test_elicitation_task_isolation.py b/tests/servers/acp_server/test_elicitation_task_isolation.py index 8f90c0b40..82320193f 100644 --- a/tests/servers/acp_server/test_elicitation_task_isolation.py +++ b/tests/servers/acp_server/test_elicitation_task_isolation.py @@ -19,7 +19,7 @@ def _make_handler() -> Any: """Create a minimal ACPProtocolHandler-like object with the elicitation task dict.""" - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.handler import ACPProtocolHandler host_context = MagicMock() host_context.session_pool = MagicMock() diff --git a/tests/servers/acp_server/test_enum_elicitation.py b/tests/servers/acp_server/test_enum_elicitation.py index b4796220a..b24e31b35 100644 --- a/tests/servers/acp_server/test_enum_elicitation.py +++ b/tests/servers/acp_server/test_enum_elicitation.py @@ -6,7 +6,7 @@ import pytest from acp import RequestPermissionResponse -from agentpool_server.acp_server.input_provider import ( +from wolfharness_server.acp_server.input_provider import ( _create_array_enum_elicitation_options, _create_oneof_elicitation_options, _handle_array_enum_elicitation_response, diff --git a/tests/servers/acp_server/test_event_converter_tool_call_fix.py b/tests/servers/acp_server/test_event_converter_tool_call_fix.py index 2d21a4c5a..b538004bb 100644 --- a/tests/servers/acp_server/test_event_converter_tool_call_fix.py +++ b/tests/servers/acp_server/test_event_converter_tool_call_fix.py @@ -14,11 +14,11 @@ import pytest from acp.schema import ToolCallProgress, ToolCallStart -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( ToolCallProgressEvent, ToolCallStartEvent, ) -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = [pytest.mark.unit, pytest.mark.anyio] diff --git a/tests/servers/acp_server/test_event_converter_user_message.py b/tests/servers/acp_server/test_event_converter_user_message.py index 2d132358e..95e4d7ad2 100644 --- a/tests/servers/acp_server/test_event_converter_user_message.py +++ b/tests/servers/acp_server/test_event_converter_user_message.py @@ -13,8 +13,8 @@ import pytest from acp.schema import UserMessageChunk -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool_server.acp_server.event_converter import ( +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness_server.acp_server.event_converter import ( ACPEventConverter, ACPUserMessageMeta, ) diff --git a/tests/servers/acp_server/test_event_converter_user_message_integration.py b/tests/servers/acp_server/test_event_converter_user_message_integration.py index ec9b52e0d..152653f1a 100644 --- a/tests/servers/acp_server/test_event_converter_user_message_integration.py +++ b/tests/servers/acp_server/test_event_converter_user_message_integration.py @@ -20,8 +20,8 @@ import pytest from acp.schema import TextContentBlock, UserMessageChunk -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = [pytest.mark.integration, pytest.mark.anyio] diff --git a/tests/servers/acp_server/test_get_session_red_flag.py b/tests/servers/acp_server/test_get_session_red_flag.py index 2a1b4f6af..e96e74d47 100644 --- a/tests/servers/acp_server/test_get_session_red_flag.py +++ b/tests/servers/acp_server/test_get_session_red_flag.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -26,7 +26,7 @@ def _make_manager_with_controller(pool: AgentPool) -> tuple[object, object]: Uses a real AgentPool — the SessionController returns None for non-existent sessions naturally. """ - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness_server.acp_server.session_manager import ACPSessionManager manager = ACPSessionManager(pool=pool) controller = pool.session_pool.sessions if pool.session_pool else None @@ -39,7 +39,7 @@ def _make_manager_without_controller(pool: AgentPool) -> object: Sets pool._session_pool = None to simulate no SessionPool. Caller is responsible for restoring pool._session_pool after the test. """ - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness_server.acp_server.session_manager import ACPSessionManager pool._session_pool = None return ACPSessionManager(pool=pool) diff --git a/tests/servers/acp_server/test_handle_prompt_delivery.py b/tests/servers/acp_server/test_handle_prompt_delivery.py index 47139c36e..0913b4416 100644 --- a/tests/servers/acp_server/test_handle_prompt_delivery.py +++ b/tests/servers/acp_server/test_handle_prompt_delivery.py @@ -15,7 +15,7 @@ import pytest -from agentpool.lifecycle.types import DeliveryMode +from wolfharness.lifecycle.types import DeliveryMode pytestmark = [pytest.mark.unit, pytest.mark.anyio] @@ -29,7 +29,7 @@ async def test_meta_delivery_ster_routes_as_asap() -> None: Then: ``handle_prompt`` receives ``delivery="steer"`` and ``send_message`` is called with ``mode=DeliveryMode.STEER``. """ - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.handler import ACPProtocolHandler host_context = MagicMock() session_pool = MagicMock() @@ -59,7 +59,7 @@ async def test_meta_delivery_ster_routes_as_asap() -> None: handler._ensure_event_consumer = AsyncMock() with patch( - "agentpool_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", + "wolfharness_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", return_value=[], ): await handler.handle_prompt( @@ -80,7 +80,7 @@ async def test_meta_delivery_followup_routes_as_when_idle() -> None: When: ``handle_prompt(delivery="followup")`` is called. Then: ``send_message`` is called with ``mode=DeliveryMode.QUEUE``. """ - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.handler import ACPProtocolHandler host_context = MagicMock() session_pool = MagicMock() @@ -110,7 +110,7 @@ async def test_meta_delivery_followup_routes_as_when_idle() -> None: handler._ensure_event_consumer = AsyncMock() with patch( - "agentpool_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", + "wolfharness_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", return_value=[], ): await handler.handle_prompt( @@ -131,7 +131,7 @@ async def test_no_meta_delivery_defaults_to_queue() -> None: When: ``handle_prompt(delivery=None)`` is called. Then: ``send_message`` is called with ``mode=DeliveryMode.QUEUE``. """ - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.handler import ACPProtocolHandler host_context = MagicMock() session_pool = MagicMock() @@ -161,7 +161,7 @@ async def test_no_meta_delivery_defaults_to_queue() -> None: handler._ensure_event_consumer = AsyncMock() with patch( - "agentpool_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", + "wolfharness_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", return_value=[], ): await handler.handle_prompt( @@ -182,7 +182,7 @@ async def test_message_id_generated_and_passed_through() -> None: When: The prompt is processed. Then: ``send_message`` is called with a non-None ``message_id`` UUID string. """ - from agentpool_server.acp_server.handler import ACPProtocolHandler + from wolfharness_server.acp_server.handler import ACPProtocolHandler host_context = MagicMock() session_pool = MagicMock() @@ -212,7 +212,7 @@ async def test_message_id_generated_and_passed_through() -> None: handler._ensure_event_consumer = AsyncMock() with patch( - "agentpool_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", + "wolfharness_server.acp_server.handler.ACPEventConverter.build_user_message_chunks", return_value=[], ): await handler.handle_prompt( diff --git a/tests/servers/acp_server/test_input_provider_elicitation.py b/tests/servers/acp_server/test_input_provider_elicitation.py index a6b800391..6202b02c7 100644 --- a/tests/servers/acp_server/test_input_provider_elicitation.py +++ b/tests/servers/acp_server/test_input_provider_elicitation.py @@ -7,7 +7,7 @@ from mcp.types import ElicitResult import pytest -from agentpool_server.acp_server.input_provider import ACPInputProvider +from wolfharness_server.acp_server.input_provider import ACPInputProvider class _FakeElicitationResponse: diff --git a/tests/servers/acp_server/test_mcp_integration.py b/tests/servers/acp_server/test_mcp_integration.py index 676d81a2b..662dedee5 100644 --- a/tests/servers/acp_server/test_mcp_integration.py +++ b/tests/servers/acp_server/test_mcp_integration.py @@ -9,13 +9,13 @@ import pytest from acp import EnvVariable, StdioMcpServer -from agentpool import Agent -from agentpool.delegation import AgentPool -from agentpool.log import get_logger -from agentpool.tools.base import Tool -from agentpool_server.acp_server.converters import convert_acp_mcp_server_to_config -from agentpool_server.acp_server.session import ACPSession -from agentpool_server.acp_server.session_manager import ACPSessionManager +from wolfharness import Agent +from wolfharness.delegation import AgentPool +from wolfharness.log import get_logger +from wolfharness.tools.base import Tool +from wolfharness_server.acp_server.converters import convert_acp_mcp_server_to_config +from wolfharness_server.acp_server.session import ACPSession +from wolfharness_server.acp_server.session_manager import ACPSessionManager pytestmark = pytest.mark.integration @@ -23,7 +23,7 @@ if TYPE_CHECKING: from acp import ClientCapabilities - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent logger = get_logger(__name__) @@ -116,7 +116,7 @@ def simple_callback(message: str) -> str: mcp_servers = [StdioMcpServer(name="tools", command="echo", args=["tools"], env=[])] async with agent_pool: # Register agent config in runtime registry so create_session() can find it - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig agent_pool.session_pool.sessions.runtime_registry.register( "test_agent", NativeAgentConfig(name="test_agent", model="test:") diff --git a/tests/servers/acp_server/test_model_state.py b/tests/servers/acp_server/test_model_state.py index ca93eb878..b182a2bb2 100644 --- a/tests/servers/acp_server/test_model_state.py +++ b/tests/servers/acp_server/test_model_state.py @@ -7,8 +7,8 @@ import pytest from acp.schema import SessionModelState -from agentpool_server.acp_server.provider_router import ProviderRouter -from agentpool_server.shared.model_utils import build_model_state_for_acp +from wolfharness_server.acp_server.provider_router import ProviderRouter +from wolfharness_server.shared.model_utils import build_model_state_for_acp pytestmark = pytest.mark.unit @@ -55,7 +55,7 @@ def create_toko_model(model_id, name, description="", provider=""): @pytest.fixture def manifest_with_variants(): """Manifest with configured variants.""" - from agentpool.models.model_configs import StringModelConfig + from wolfharness.models.model_configs import StringModelConfig variants = { "fast_gpt": StringModelConfig(identifier="openai:gpt-4o-mini"), diff --git a/tests/servers/acp_server/test_process_tools_integration.py b/tests/servers/acp_server/test_process_tools_integration.py index b2dbbfcad..0c2d7497f 100644 --- a/tests/servers/acp_server/test_process_tools_integration.py +++ b/tests/servers/acp_server/test_process_tools_integration.py @@ -7,9 +7,9 @@ from exxec.models import ExecutionResult import pytest -from agentpool import Agent, AgentContext -from agentpool.agents.context import AgentRunContext -from agentpool_toolsets.builtin.execution_environment import ProcessManagementTools +from wolfharness import Agent, AgentContext +from wolfharness.agents.context import AgentRunContext +from wolfharness_toolsets.builtin.execution_environment import ProcessManagementTools pytestmark = pytest.mark.integration @@ -218,7 +218,7 @@ async def test_execute_command( test_agent: Agent[None], ): """Test executing a command directly.""" - from agentpool.tool_impls.bash import create_bash_tool + from wolfharness.tool_impls.bash import create_bash_tool bash_tool = create_bash_tool(env=mock_env) diff --git a/tests/servers/acp_server/test_prompt_meta_delivery.py b/tests/servers/acp_server/test_prompt_meta_delivery.py index 1bdbdc54f..572d5c33b 100644 --- a/tests/servers/acp_server/test_prompt_meta_delivery.py +++ b/tests/servers/acp_server/test_prompt_meta_delivery.py @@ -33,7 +33,7 @@ def _make_agent() -> MagicMock: async def _call_prompt(agent: MagicMock, meta: dict | None) -> None: """Call agent.prompt() with a PromptRequest carrying the given _meta.""" - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent request = PromptRequest( session_id="test-session", diff --git a/tests/servers/acp_server/test_provider_router.py b/tests/servers/acp_server/test_provider_router.py index 518a41499..b6d571150 100644 --- a/tests/servers/acp_server/test_provider_router.py +++ b/tests/servers/acp_server/test_provider_router.py @@ -6,8 +6,8 @@ import pytest -from agentpool.models.model_configs import StringModelConfig -from agentpool_server.acp_server.provider_router import ProviderRouter +from wolfharness.models.model_configs import StringModelConfig +from wolfharness_server.acp_server.provider_router import ProviderRouter pytestmark = pytest.mark.integration diff --git a/tests/servers/acp_server/test_raw_input_mode.py b/tests/servers/acp_server/test_raw_input_mode.py index 064978259..e9d7acc1b 100644 --- a/tests/servers/acp_server/test_raw_input_mode.py +++ b/tests/servers/acp_server/test_raw_input_mode.py @@ -13,11 +13,11 @@ import pytest from acp.schema import ToolCallProgress, ToolCallStart -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( ToolCallProgressEvent, ToolCallStartEvent, ) -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = [pytest.mark.unit, pytest.mark.anyio] diff --git a/tests/servers/acp_server/test_steer_event_pipeline.py b/tests/servers/acp_server/test_steer_event_pipeline.py index daa686b94..958543b0a 100644 --- a/tests/servers/acp_server/test_steer_event_pipeline.py +++ b/tests/servers/acp_server/test_steer_event_pipeline.py @@ -18,13 +18,13 @@ import pytest from acp.schema import UserMessageChunk -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool.lifecycle.comm_channel import DirectChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.messaging import MessageHistory -from agentpool.orchestrator.core import EventBus, SessionState -from agentpool.orchestrator.run import RunHandle -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness.lifecycle.comm_channel import DirectChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.messaging import MessageHistory +from wolfharness.orchestrator.core import EventBus, SessionState +from wolfharness.orchestrator.run import RunHandle +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = [pytest.mark.integration, pytest.mark.anyio] diff --git a/tests/servers/acp_server/test_step_usage_converter.py b/tests/servers/acp_server/test_step_usage_converter.py index d1d3e2c25..922d950db 100644 --- a/tests/servers/acp_server/test_step_usage_converter.py +++ b/tests/servers/acp_server/test_step_usage_converter.py @@ -11,12 +11,12 @@ import pytest from acp.schema import UsageUpdate -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( StepUsageEvent, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage -from agentpool_server.acp_server.event_converter import ACPEventConverter +from wolfharness.messaging import ChatMessage +from wolfharness_server.acp_server.event_converter import ACPEventConverter pytestmark = [pytest.mark.integration, pytest.mark.anyio] diff --git a/tests/servers/acp_server/test_subagent_events.py b/tests/servers/acp_server/test_subagent_events.py index cf1f59aac..7c6a5e838 100644 --- a/tests/servers/acp_server/test_subagent_events.py +++ b/tests/servers/acp_server/test_subagent_events.py @@ -16,17 +16,17 @@ from acp.schema import ClientCapabilities from acp.schema.notifications import SessionNotification -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( PartDeltaEvent, RunErrorEvent, SpawnSessionStart, StreamCompleteEvent, ToolCallStartEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, EventEnvelope -from agentpool_server.acp_server.event_converter import ACPEventConverter -from agentpool_server.acp_server.handler import ACPProtocolHandler +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, EventEnvelope +from wolfharness_server.acp_server.event_converter import ACPEventConverter +from wolfharness_server.acp_server.handler import ACPProtocolHandler pytestmark = [pytest.mark.unit, pytest.mark.anyio] @@ -239,13 +239,13 @@ async def test_notify_completed_called_when_done_event_set( Then: _notify_completed sends a ToolCallProgress completion notification. """ # Set up parent converter in zed mode so build_subagent_completed yields - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter zed_converter = ACPEventConverter(subagent_display_mode="zed") zed_converter._current_message_id = "test-msg" acp_handler._converters["parent-ses"] = zed_converter # Seed the converter's _subagent_tool_call_ids map - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart spawn = SpawnSessionStart( child_session_id="child-ses", @@ -296,12 +296,12 @@ async def test_done_event_none_race_immediate_notification( When: _on_spawn_session_start processes a SpawnSessionStart. Then: _notify_completed is called immediately (no closure spawned). """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter zed_converter = ACPEventConverter(subagent_display_mode="zed") zed_converter._current_message_id = "test-msg" acp_handler._converters["parent-ses"] = zed_converter - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart spawn = SpawnSessionStart( child_session_id="child-race", @@ -321,7 +321,7 @@ async def test_done_event_none_race_immediate_notification( # Restore the real _on_spawn_session_start (fixture overrides it with AsyncMock) import types - from agentpool_server.acp_server.handler import ACPProtocolHandler as _HandlerCls + from wolfharness_server.acp_server.handler import ACPProtocolHandler as _HandlerCls acp_handler._on_spawn_session_start = types.MethodType( # type: ignore[assignment] _HandlerCls._on_spawn_session_start, acp_handler @@ -358,12 +358,12 @@ async def test_concurrent_children_each_get_completion_notification( When: Both done_events are set. Then: _notify_completed is called for each child with the correct tool_call_id. """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter zed_converter = ACPEventConverter(subagent_display_mode="zed") zed_converter._current_message_id = "test-msg" acp_handler._converters["parent-ses"] = zed_converter - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart # Seed converter with two spawn events for i, child_sid in enumerate(["child-a", "child-b"]): @@ -420,12 +420,12 @@ async def test_closure_error_logged_not_swallowed( When: _await_child_and_notify completes (done_event set). Then: The closure does NOT re-raise the ValueError (caught by generic except). """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter zed_converter = ACPEventConverter(subagent_display_mode="zed") zed_converter._current_message_id = "test-msg" acp_handler._converters["parent-ses"] = zed_converter - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart spawn = SpawnSessionStart( child_session_id="child-err", @@ -471,12 +471,12 @@ async def test_consumer_task_refs_cleanup_after_closure( When: The closure task completes (done_event set). Then: The task is removed from _consumer_task_refs. """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter zed_converter = ACPEventConverter(subagent_display_mode="zed") zed_converter._current_message_id = "test-msg" acp_handler._converters["parent-ses"] = zed_converter - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart spawn = SpawnSessionStart( child_session_id="child-ref", @@ -522,12 +522,12 @@ async def test_parent_of_cleanup_on_child_exit( When: _await_child_and_notify's done_event is set (simulating child exit). Then: _parent_of[child_sid] is removed. """ - from agentpool_server.acp_server.event_converter import ACPEventConverter + from wolfharness_server.acp_server.event_converter import ACPEventConverter zed_converter = ACPEventConverter(subagent_display_mode="zed") zed_converter._current_message_id = "test-msg" acp_handler._converters["parent-ses"] = zed_converter - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart spawn = SpawnSessionStart( child_session_id="child-cleanup", @@ -761,7 +761,7 @@ async def test_on_spawn_creates_child_converter_with_subagent_context( """ import types - from agentpool_server.acp_server.handler import ACPProtocolHandler as _HandlerCls + from wolfharness_server.acp_server.handler import ACPProtocolHandler as _HandlerCls # Restore real method (fixture overrides it with AsyncMock) acp_handler._on_spawn_session_start = types.MethodType( # type: ignore[assignment] @@ -824,7 +824,7 @@ async def test_handle_event_stamps_field_meta_on_child_notification( When: A PartDeltaEvent is handled for that child session. Then: The resulting SessionNotification has the correct field_meta dict. """ - from agentpool_server.acp_server.event_converter import SubagentContext + from wolfharness_server.acp_server.event_converter import SubagentContext child_converter = ACPEventConverter( subagent_context=SubagentContext(parent_tool_call_id="tc-123", subagent_type="coder"), @@ -877,7 +877,7 @@ async def test_nested_subagents_each_have_own_context( When: Inspecting their subagent_context values. Then: Each converter has its own context pointing to its parent_tool_call_id. """ - from agentpool_server.acp_server.event_converter import SubagentContext + from wolfharness_server.acp_server.event_converter import SubagentContext child_converter = ACPEventConverter( subagent_context=SubagentContext( diff --git a/tests/servers/acp_server/test_tool_call_snapshots.py b/tests/servers/acp_server/test_tool_call_snapshots.py index 837b6b41c..4c89e0176 100644 --- a/tests/servers/acp_server/test_tool_call_snapshots.py +++ b/tests/servers/acp_server/test_tool_call_snapshots.py @@ -11,9 +11,9 @@ from exxec.models import ExecutionResult import pytest from syrupy.extensions.json import JSONSnapshotExtension +from wolfharness_config.wolfharness_tools import BashToolConfig, ExecuteCodeToolConfig -from agentpool_config.agentpool_tools import BashToolConfig, ExecuteCodeToolConfig -from agentpool_config.toolsets import FSSpecToolsetConfig +from wolfharness_config.toolsets import FSSpecToolsetConfig from .tool_call_harness import ToolCallTestHarness @@ -305,7 +305,7 @@ async def test_mcp_tool_with_progress( """Test MCP tool with progress notifications produces expected messages.""" from pathlib import Path - from agentpool_config.mcp_server import StdioMCPServerConfig + from wolfharness_config.mcp_server import StdioMCPServerConfig server_path = Path(__file__).parent.parent.parent / "mcp_server" / "server.py" mcp_server = StdioMCPServerConfig( diff --git a/tests/servers/acp_server/test_turn_completion.py b/tests/servers/acp_server/test_turn_completion.py index 3783aef4e..c3bf7dbc8 100644 --- a/tests/servers/acp_server/test_turn_completion.py +++ b/tests/servers/acp_server/test_turn_completion.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent pytestmark = pytest.mark.unit diff --git a/tests/servers/acp_server/tool_call_harness.py b/tests/servers/acp_server/tool_call_harness.py index 5c198ad8f..7bfe723cb 100644 --- a/tests/servers/acp_server/tool_call_harness.py +++ b/tests/servers/acp_server/tool_call_harness.py @@ -32,19 +32,19 @@ async def test_my_tool(harness: ToolCallTestHarness, snapshot): from acp import ClientCapabilities from acp.client.implementations import HeadlessACPClient from acp.schema import TextContentBlock -from agentpool import AgentsManifest -from agentpool.delegation import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.model_configs import TestModelConfig -from agentpool_server.acp_server.session import ACPSession +from wolfharness import AgentsManifest +from wolfharness.delegation import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.model_configs import TestModelConfig +from wolfharness_server.acp_server.session import ACPSession if TYPE_CHECKING: from collections.abc import Sequence from acp.schema import SessionNotification - from agentpool_config import AnyToolConfig - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness_config import AnyToolConfig + from wolfharness_config.mcp_server import MCPServerConfig class RecordingACPClient(HeadlessACPClient): diff --git a/tests/servers/opencode_server/conftest.py b/tests/servers/opencode_server/conftest.py index da34e72b9..cf7391833 100644 --- a/tests/servers/opencode_server/conftest.py +++ b/tests/servers/opencode_server/conftest.py @@ -23,25 +23,25 @@ from httpx import ASGITransport, AsyncClient import pytest -from agentpool.models.manifest import AgentsManifest -from agentpool.storage import StorageManager -from agentpool.utils.streams import FileOpsTracker -from agentpool.utils.time_utils import now_ms -from agentpool.utils.todos import TodoTracker -from agentpool_server.opencode_server.dependencies import get_state -from agentpool_server.opencode_server.models import Session -from agentpool_server.opencode_server.models.common import TimeCreatedUpdated -from agentpool_server.opencode_server.routes import agent_router, file_router, session_router -from agentpool_server.opencode_server.routes.global_routes import router as global_router -from agentpool_server.opencode_server.routes.message_routes import router as message_router -from agentpool_server.opencode_server.state import ServerState +from wolfharness.models.manifest import AgentsManifest +from wolfharness.storage import StorageManager +from wolfharness.utils.streams import FileOpsTracker +from wolfharness.utils.time_utils import now_ms +from wolfharness.utils.todos import TodoTracker +from wolfharness_server.opencode_server.dependencies import get_state +from wolfharness_server.opencode_server.models import Session +from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated +from wolfharness_server.opencode_server.routes import agent_router, file_router, session_router +from wolfharness_server.opencode_server.routes.global_routes import router as global_router +from wolfharness_server.opencode_server.routes.message_routes import router as message_router +from wolfharness_server.opencode_server.state import ServerState if TYPE_CHECKING: from collections.abc import AsyncIterator, Iterator - from agentpool.sessions.models import SessionData - from agentpool_server.opencode_server.models.message import MessageRequest, MessageWithParts + from wolfharness.sessions.models import SessionData + from wolfharness_server.opencode_server.models.message import MessageRequest, MessageWithParts def _make_functional_event_bus() -> Mock: @@ -97,7 +97,7 @@ async def _publish(session_id: str, event: Any) -> None: async def _check_event_stream_for_failure(event_stream: Any, failure_type: type) -> bool: """Check an event stream for a failure event. Returns True if found.""" - from agentpool.orchestrator.core import drain_and_merge + from wolfharness.orchestrator.core import drain_and_merge async for event in drain_and_merge(event_stream): if isinstance(event.event, failure_type): @@ -128,7 +128,7 @@ async def run_message_phases( Returns the ``_MessageRunContext``. """ - from agentpool_server.opencode_server.routes.message_routes import ( + from wolfharness_server.opencode_server.routes.message_routes import ( _route_message_locked, ) @@ -178,7 +178,7 @@ async def run_message_phases( # Drain early event stream: feed events to adapter and check for RunFailedEvent if early_event_stream is not None: - from agentpool.agents.events.events import RunFailedEvent + from wolfharness.agents.events.events import RunFailedEvent with contextlib.suppress(Exception): found_failure = await _drain_events_and_check_failure( @@ -227,7 +227,7 @@ async def _finalize_assistant_inline( Note: EventBus events should already be drained and fed to the adapter by the caller (run_message_phases via _drain_events_and_check_failure). """ - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageAbortedError, MessageAbortedErrorData, @@ -277,7 +277,7 @@ async def _finalize_assistant_inline( if sp_session_pool is not None and hasattr(sp_session_pool, "sessions"): sp_session = sp_session_pool.sessions.get_session(session_id) if sp_session is not None and getattr(sp_session, "agent", None) is not None: - from agentpool_server.opencode_server.routes.message_routes import ( + from wolfharness_server.opencode_server.routes.message_routes import ( opencode_to_chat_message, ) @@ -382,7 +382,7 @@ def storage_manager() -> StorageManager: Uses MemoryStorageProvider so session CRUD, message storage, etc. all work without any external dependencies or I/O. """ - from agentpool_config.storage import MemoryStorageConfig, StorageConfig + from wolfharness_config.storage import MemoryStorageConfig, StorageConfig config = StorageConfig(providers=[MemoryStorageConfig()]) return StorageManager(config=config) @@ -450,7 +450,7 @@ async def _mock_create_session( ) -> Mock: from datetime import datetime - from agentpool.sessions.models import SessionData + from wolfharness.sessions.models import SessionData data = SessionData( session_id=session_id, @@ -551,7 +551,7 @@ def mock_agent(mock_env: Mock, mock_pool: Mock, storage_manager: StorageManager) """ agent = Mock() agent.name = "test-agent" - agent.model_name = None # resolve_default_model_info() falls back to ("default", "agentpool") + agent.model_name = None # resolve_default_model_info() falls back to ("default", "wolfharness") agent.env = mock_env agent._input_provider = None agent.run = AsyncMock(return_value=Mock(data="test response")) @@ -638,7 +638,7 @@ async def _mock_route_message( input_provider: Any | None = None, **kwargs: Any, ) -> Any: - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode # D14: Store the canonical assistant_msg_id (or fallback to # message_id) so _before_consumer_loop can reuse it instead of @@ -682,16 +682,16 @@ async def _mock_route_message( if message_id is not None: import time as _time - from agentpool_server.opencode_server.event_processor import ( + from wolfharness_server.opencode_server.event_processor import ( OpenCodeUserMessageMeta, _deserialize_part, ) - from agentpool_server.opencode_server.models.common import TimeCreated - from agentpool_server.opencode_server.models.message import ( + from wolfharness_server.opencode_server.models.common import TimeCreated + from wolfharness_server.opencode_server.models.message import ( MessageWithParts, UserMessage, ) - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( append_message_to_session, ) @@ -723,7 +723,7 @@ async def _mock_route_message( # session_controller is present, but ensure it's initialized for cases # where the mock pool's event_bus isn't available at construction time. if state.event_bridge is None and state._pool is not None: - from agentpool_server.opencode_server.event_bridge import OpenCodeEventBridge + from wolfharness_server.opencode_server.event_bridge import OpenCodeEventBridge session_pool = getattr(state._pool, "session_pool", None) if session_pool is not None: diff --git a/tests/servers/opencode_server/test_agent_model_propagation.py b/tests/servers/opencode_server/test_agent_model_propagation.py index 88b76d23c..0753d85cc 100644 --- a/tests/servers/opencode_server/test_agent_model_propagation.py +++ b/tests/servers/opencode_server/test_agent_model_propagation.py @@ -2,24 +2,24 @@ Regression tests for the bug where the OpenCode TUI showed correct agent name and model in the input box, but after execution started, both agent and model -became 'default'/'agentpool' for the lead agent AND sub-agents. +became 'default'/'wolfharness' for the lead agent AND sub-agents. Root cause: ``_before_consumer_loop`` and ``_reconstruct_tool_parts_from_checkpoint`` -hardcoded ``agent_name="agentpool"``, ``model_id="default"``, -``provider_id="agentpool"`` when creating the assistant message for the async +hardcoded ``agent_name="wolfharness"``, ``model_id="default"``, +``provider_id="wolfharness"`` when creating the assistant message for the async path. The sync path (``_process_message_locked``) correctly used values from the HTTP request, but the TUI uses ``prompt_async`` which goes through the async path. These tests verify that: 1. ``_before_consumer_loop`` uses ``agent_name`` from the session state - (not the hardcoded "agentpool"). + (not the hardcoded "wolfharness"). 2. ``_before_consumer_loop`` uses model info from pending metadata when - provided by the REST handler (not the hardcoded "default"/"agentpool"). -3. ``_before_consumer_loop`` falls back to "default"/"agentpool" only when + provided by the REST handler (not the hardcoded "default"/"wolfharness"). +3. ``_before_consumer_loop`` falls back to "default"/"wolfharness" only when no model info is available. 4. ``_reconstruct_tool_parts_from_checkpoint`` uses ``agent_name`` from the - session state (not the hardcoded "agentpool"). + session state (not the hardcoded "wolfharness"). """ from __future__ import annotations @@ -29,7 +29,7 @@ import pytest -from agentpool.orchestrator.session_controller import SessionState +from wolfharness.orchestrator.session_controller import SessionState pytestmark = pytest.mark.unit @@ -49,7 +49,7 @@ def _make_integration( server_state: Mock | None = None, ) -> Any: """Build an OpenCodeSessionPoolIntegration with mocked deps.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -58,7 +58,7 @@ def _make_integration( if server_state is None: server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.model_variants = {} return OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -72,8 +72,8 @@ class TestBeforeConsumerLoopAgentName: """_before_consumer_loop must use agent_name from session state.""" @pytest.mark.asyncio - async def test_uses_session_agent_name_not_agentpool(self) -> None: - """Assistant message should carry the session's agent_name, not 'agentpool'.""" + async def test_uses_session_agent_name_not_wolfharness(self) -> None: + """Assistant message should carry the session's agent_name, not 'wolfharness'.""" session_pool = Mock() session_pool.sessions = Mock() session_pool.sessions.get_session = Mock( @@ -111,8 +111,8 @@ def _get_session(session_id: str) -> SessionState: assert ctx_l.assistant_msg.info.agent == "logician" @pytest.mark.asyncio - async def test_falls_back_to_agentpool_when_no_session_state(self) -> None: - """When session state is unavailable, fall back to 'agentpool' (graceful degradation).""" + async def test_falls_back_to_wolfharness_when_no_session_state(self) -> None: + """When session state is unavailable, fall back to 'wolfharness' (graceful degradation).""" session_pool = Mock() session_pool.sessions = Mock() session_pool.sessions.get_session = Mock(return_value=None) @@ -125,7 +125,7 @@ async def test_falls_back_to_agentpool_when_no_session_state(self) -> None: assert ctx is not None # Graceful fallback — should still create a message, just with the # default agent_name. - assert ctx.assistant_msg.info.agent == "agentpool" + assert ctx.assistant_msg.info.agent == "wolfharness" # ============================================================================= @@ -167,7 +167,7 @@ async def test_uses_pending_model_id_and_provider_id(self) -> None: @pytest.mark.asyncio async def test_falls_back_to_default_when_no_pending_model(self) -> None: - """Without pending model metadata, fall back to 'default'/'agentpool'.""" + """Without pending model metadata, fall back to 'default'/'wolfharness'.""" session_pool = Mock() session_pool.sessions = Mock() session_pool.sessions.get_session = Mock( @@ -184,7 +184,7 @@ async def test_falls_back_to_default_when_no_pending_model(self) -> None: assert ctx.assistant_msg.info.agent == "engineer" # model falls back to defaults assert ctx.assistant_msg.info.model_id == "default" - assert ctx.assistant_msg.info.provider_id == "agentpool" + assert ctx.assistant_msg.info.provider_id == "wolfharness" @pytest.mark.asyncio async def test_pending_message_id_still_works_with_metadata(self) -> None: @@ -238,7 +238,7 @@ def _get_session(session_id: str) -> SessionState: ctx = integration._contexts.get(child_id) assert ctx is not None assert ctx.assistant_msg.info.agent == "researcher" - assert ctx.assistant_msg.info.agent != "agentpool" + assert ctx.assistant_msg.info.agent != "wolfharness" @pytest.mark.asyncio async def test_child_session_uses_child_agent_model(self) -> None: @@ -323,7 +323,7 @@ class TestReconstructToolPartsAgentName: @pytest.mark.asyncio async def test_uses_session_agent_name(self) -> None: """Checkpoint recovery should preserve the session's agent_name on the assistant message.""" - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( _reconstruct_tool_parts_from_checkpoint, ) @@ -338,7 +338,7 @@ async def test_uses_session_agent_name(self) -> None: server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.model_variants = {} server_state.pool_or_none = pool server_state.messages = {} @@ -359,12 +359,12 @@ async def test_uses_session_agent_name(self) -> None: assert len(messages) == 1 assistant_msg = messages[0] assert assistant_msg.info.agent == "visionary" - assert assistant_msg.info.agent != "agentpool" + assert assistant_msg.info.agent != "wolfharness" @pytest.mark.asyncio - async def test_falls_back_to_agentpool_when_no_session_state(self) -> None: - """When session state is unavailable, fall back to 'agentpool'.""" - from agentpool_server.opencode_server.opencode_message_bridge import ( + async def test_falls_back_to_wolfharness_when_no_session_state(self) -> None: + """When session state is unavailable, fall back to 'wolfharness'.""" + from wolfharness_server.opencode_server.opencode_message_bridge import ( _reconstruct_tool_parts_from_checkpoint, ) @@ -377,7 +377,7 @@ async def test_falls_back_to_agentpool_when_no_session_state(self) -> None: server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.model_variants = {} server_state.pool_or_none = pool server_state.messages = {} @@ -395,12 +395,12 @@ async def test_falls_back_to_agentpool_when_no_session_state(self) -> None: messages = server_state.messages.get("test-session", []) assert len(messages) == 1 - assert messages[0].info.agent == "agentpool" + assert messages[0].info.agent == "wolfharness" @pytest.mark.asyncio async def test_no_op_when_no_pending_calls(self) -> None: """Function should be a no-op when pending_calls is empty.""" - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( _reconstruct_tool_parts_from_checkpoint, ) diff --git a/tests/servers/opencode_server/test_auto_resume_message_redflag.py b/tests/servers/opencode_server/test_auto_resume_message_redflag.py index 0daba393b..95a368899 100644 --- a/tests/servers/opencode_server/test_auto_resume_message_redflag.py +++ b/tests/servers/opencode_server/test_auto_resume_message_redflag.py @@ -34,19 +34,19 @@ ) import pytest -from agentpool.agents.events import StreamCompleteEvent -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import SessionPool -from agentpool_server.opencode_server.models import ( +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import SessionPool +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageUpdatedEvent, PartUpdatedEvent, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, get_messages_for_session, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -59,8 +59,8 @@ @pytest.fixture def mock_agent_pool() -> Mock: """Create a mock AgentPool for SessionPool construction.""" - from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent - from agentpool.messaging.messages import ChatMessage + from wolfharness.agents.events import RunStartedEvent, StreamCompleteEvent + from wolfharness.messaging.messages import ChatMessage pool = Mock() pool.main_agent = Mock() diff --git a/tests/servers/opencode_server/test_cancelled_message.py b/tests/servers/opencode_server/test_cancelled_message.py index 6d39b736b..1d40e83c7 100644 --- a/tests/servers/opencode_server/test_cancelled_message.py +++ b/tests/servers/opencode_server/test_cancelled_message.py @@ -22,9 +22,10 @@ import pytest -from agentpool.utils import identifiers as identifier -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageRequest, MessageUpdatedEvent, @@ -32,12 +33,11 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( MessageAbortedError, MessageWithParts, ) -from agentpool_server.opencode_server.state import ServerState -from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -62,7 +62,7 @@ def __init__(self) -> None: self.model_name = "test-model" self.session_id: str | None = None # Real MessageHistory so conversation state is testable - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory self.conversation = MessageHistory() @@ -120,7 +120,7 @@ def cancellable_mock_agent(): agent._agent_pool = pool # state.py resolves _pool via agent._agent_pool # Set up SessionPool mock for new architecture - from agentpool.lifecycle import RunState + from wolfharness.lifecycle import RunState session_pool = Mock() session_pool.sessions = Mock() @@ -188,8 +188,8 @@ def sample_message_request(): def _setup_session(state: ServerState, session_id: str) -> None: """Set up session state manually (bypassing ensure_session which needs pool.storage).""" - from agentpool_server.opencode_server.models import Session - from agentpool_server.opencode_server.models.common import TimeCreatedUpdated + from wolfharness_server.opencode_server.models import Session + from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated now = now_ms() session = Session( diff --git a/tests/servers/opencode_server/test_command_execution.py b/tests/servers/opencode_server/test_command_execution.py index 2b3880965..a6c91e5d1 100644 --- a/tests/servers/opencode_server/test_command_execution.py +++ b/tests/servers/opencode_server/test_command_execution.py @@ -33,7 +33,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = [pytest.mark.asyncio, pytest.mark.integration] @@ -298,7 +298,7 @@ async def test_collision_warning_logged( mock_prompt.name = "collision-cmd" mock_agent.list_prompts = AsyncMock(return_value=[mock_prompt]) - with patch("agentpool_server.opencode_server.routes.session_routes.logger") as mock_logger: + with patch("wolfharness_server.opencode_server.routes.session_routes.logger") as mock_logger: response = await async_client.post( f"/session/{session_id}/command", json={"command": "collision-cmd"}, @@ -390,7 +390,7 @@ async def test_skill_command_routes_through_session_pool( # noqa: PLR0915 """ from slashed import Command as SlashedCommand - from agentpool_server.opencode_server.models import UserMessage + from wolfharness_server.opencode_server.models import UserMessage # Create session first response = await async_client.post("/session", json={"title": "Test Session"}) @@ -652,10 +652,10 @@ async def test_skill_command_full_chain_integration( # noqa: PLR0915 """ from slashed import Command as SlashedCommand - from agentpool_server.opencode_server.event_processor import ( + from wolfharness_server.opencode_server.event_processor import ( OpenCodeUserMessageMeta, ) - from agentpool_server.opencode_server.models import TextPart, UserMessage + from wolfharness_server.opencode_server.models import TextPart, UserMessage # Create session response = await async_client.post("/session", json={"title": "Integration Test"}) diff --git a/tests/servers/opencode_server/test_command_model_and_hints.py b/tests/servers/opencode_server/test_command_model_and_hints.py index 85c433a01..0cc47da97 100644 --- a/tests/servers/opencode_server/test_command_model_and_hints.py +++ b/tests/servers/opencode_server/test_command_model_and_hints.py @@ -7,8 +7,8 @@ import pytest -from agentpool_server.opencode_server.models.agent import Command -from agentpool_server.opencode_server.routes.agent_routes import _extract_hints +from wolfharness_server.opencode_server.models.agent import Command +from wolfharness_server.opencode_server.routes.agent_routes import _extract_hints pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_commit_revert.py b/tests/servers/opencode_server/test_commit_revert.py index 41cbb63e1..0f35a68d1 100644 --- a/tests/servers/opencode_server/test_commit_revert.py +++ b/tests/servers/opencode_server/test_commit_revert.py @@ -16,10 +16,10 @@ import pytest -from agentpool.messaging import ChatMessage -from agentpool.utils.streams import FileChange -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from wolfharness.messaging import ChatMessage +from wolfharness.utils.streams import FileChange +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageRequest, @@ -33,14 +33,14 @@ TimeCreatedUpdated, UserMessage, ) -from agentpool_server.opencode_server.routes.message_routes import ( +from wolfharness_server.opencode_server.routes.message_routes import ( _commit_revert, _truncate_agent_history, ) if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.unit @@ -464,7 +464,7 @@ async def _tracking_truncate(sid: str, up_to: str) -> int: original_broadcast = server_state.broadcast_event async def _tracking_broadcast(event: Any) -> None: - from agentpool_server.opencode_server.models import SessionUpdatedEvent + from wolfharness_server.opencode_server.models import SessionUpdatedEvent if isinstance(event, SessionUpdatedEvent): call_order.append("broadcast_session_updated") diff --git a/tests/servers/opencode_server/test_commit_revert_integration.py b/tests/servers/opencode_server/test_commit_revert_integration.py index d025b92b9..7a2bc7897 100644 --- a/tests/servers/opencode_server/test_commit_revert_integration.py +++ b/tests/servers/opencode_server/test_commit_revert_integration.py @@ -24,11 +24,11 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select -from agentpool.messaging import ChatMessage -from agentpool.storage import StorageManager -from agentpool.utils.time_utils import now_ms -from agentpool_config.storage import SQLStorageConfig, StorageConfig -from agentpool_server.opencode_server.models import ( +from wolfharness.messaging import ChatMessage +from wolfharness.storage import StorageManager +from wolfharness.utils.time_utils import now_ms +from wolfharness_config.storage import SQLStorageConfig, StorageConfig +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -40,19 +40,19 @@ TimeCreatedUpdated, UserMessage, ) -from agentpool_server.opencode_server.routes.message_routes import _commit_revert -from agentpool_server.opencode_server.routes.session_routes import ( +from wolfharness_server.opencode_server.routes.message_routes import _commit_revert +from wolfharness_server.opencode_server.routes.session_routes import ( RevertRequest, revert_session, ) -from agentpool_storage.sql_provider.models import Message -from agentpool_storage.sql_provider.sql_provider import SQLModelProvider +from wolfharness_storage.sql_provider.models import Message +from wolfharness_storage.sql_provider.sql_provider import SQLModelProvider if TYPE_CHECKING: from pathlib import Path - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_concurrent_messages.py b/tests/servers/opencode_server/test_concurrent_messages.py index 9b097c4db..bfb183d4b 100644 --- a/tests/servers/opencode_server/test_concurrent_messages.py +++ b/tests/servers/opencode_server/test_concurrent_messages.py @@ -12,14 +12,14 @@ import pytest -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessageRequest, TextPartInput, ) -from agentpool_server.opencode_server.models.message import UserMessage -from agentpool_server.opencode_server.routes.message_routes import _process_message -from agentpool_server.opencode_server.session_pool_integration import ensure_session -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.models.message import UserMessage +from wolfharness_server.opencode_server.routes.message_routes import _process_message +from wolfharness_server.opencode_server.session_pool_integration import ensure_session +from wolfharness_server.opencode_server.state import ServerState if TYPE_CHECKING: @@ -84,8 +84,8 @@ async def stream() -> AsyncIterator[Any]: await asyncio.sleep(self.delay) # Yield a simple text event - from agentpool.agents.events import StreamCompleteEvent, TextContentItem - from agentpool.messaging import ChatMessage + from wolfharness.agents.events import StreamCompleteEvent, TextContentItem + from wolfharness.messaging import ChatMessage yield TextContentItem(text=f"Response for {session_id}") yield StreamCompleteEvent(message=ChatMessage(role="assistant", content="done")) @@ -142,7 +142,7 @@ async def save_session(session_data: Any) -> None: pool.session_pool.sessions.get_session = Mock(return_value=None) # Set up a real EventBus so adapter can subscribe/unsubscribe - from agentpool.orchestrator.core import EventBus + from wolfharness.orchestrator.core import EventBus event_bus = EventBus(max_queue_size=100) pool.session_pool.event_bus = event_bus @@ -157,8 +157,8 @@ async def _mock_receive_request( message_id: str | None = None, **kwargs: Any, ): - from agentpool.lifecycle import RunOutcome, RunState - from agentpool.orchestrator.run import RunHandle + from wolfharness.lifecycle import RunOutcome, RunState + from wolfharness.orchestrator.run import RunHandle handle = Mock(spec=RunHandle) handle.run_id = "test-run" @@ -258,12 +258,12 @@ async def _wrapping_send_message(*args: Any, **kwargs: Any) -> Any: if message_id is not None and content is not None: import time as _time - from agentpool_server.opencode_server.models.common import TimeCreated - from agentpool_server.opencode_server.models.message import ( + from wolfharness_server.opencode_server.models.common import TimeCreated + from wolfharness_server.opencode_server.models.message import ( MessageWithParts, UserMessage, ) - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( append_message_to_session, ) @@ -344,7 +344,7 @@ async def send_message_with_id(msg_id: str): ) # Debug: capture all error events first - from agentpool_server.opencode_server.models.events import SessionErrorEvent + from wolfharness_server.opencode_server.models.events import SessionErrorEvent error_events = [e for e in all_events if isinstance(e, SessionErrorEvent)] if error_events: diff --git a/tests/servers/opencode_server/test_config_routes.py b/tests/servers/opencode_server/test_config_routes.py index 967a5d7fe..418d569ad 100644 --- a/tests/servers/opencode_server/test_config_routes.py +++ b/tests/servers/opencode_server/test_config_routes.py @@ -6,8 +6,8 @@ import pytest -from agentpool.agents.modes import ModeCategory, ModeInfo -from agentpool_server.opencode_server.routes.config_routes import list_modes +from wolfharness.agents.modes import ModeCategory, ModeInfo +from wolfharness_server.opencode_server.routes.config_routes import list_modes pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_converter_thinking.py b/tests/servers/opencode_server/test_converter_thinking.py index 309de99b2..043288e75 100644 --- a/tests/servers/opencode_server/test_converter_thinking.py +++ b/tests/servers/opencode_server/test_converter_thinking.py @@ -18,12 +18,12 @@ ) import pytest -from agentpool.messaging.messages import ChatMessage -from agentpool_server.opencode_server.converters import ( +from wolfharness.messaging.messages import ChatMessage +from wolfharness_server.opencode_server.converters import ( chat_message_to_opencode, opencode_to_chat_message, ) -from agentpool_server.opencode_server.models import ReasoningPart +from wolfharness_server.opencode_server.models import ReasoningPart pytestmark = pytest.mark.integration @@ -114,7 +114,7 @@ def test_chat_message_to_opencode_preserves_text_and_thinking(): msg = _make_assistant_chat_message_with_thinking() result = chat_message_to_opencode(msg, session_id="s1") - from agentpool_server.opencode_server.models import TextPart + from wolfharness_server.opencode_server.models import TextPart reasoning_parts = [p for p in result.parts if isinstance(p, ReasoningPart)] text_parts = [p for p in result.parts if isinstance(p, TextPart)] diff --git a/tests/servers/opencode_server/test_d14_separation.py b/tests/servers/opencode_server/test_d14_separation.py index 0b7b00af2..9326ffb13 100644 --- a/tests/servers/opencode_server/test_d14_separation.py +++ b/tests/servers/opencode_server/test_d14_separation.py @@ -12,7 +12,7 @@ import pytest -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) diff --git a/tests/servers/opencode_server/test_dedup.py b/tests/servers/opencode_server/test_dedup.py index 254e4a03e..18f78f077 100644 --- a/tests/servers/opencode_server/test_dedup.py +++ b/tests/servers/opencode_server/test_dedup.py @@ -14,12 +14,12 @@ import pytest -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( MessagePath, MessageTime, MessageWithParts, @@ -38,7 +38,7 @@ def _make_ctx() -> EventProcessorContext: agent_name="agent", model_id="default", parent_id="", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) state = MagicMock() @@ -51,7 +51,9 @@ def _make_ctx() -> EventProcessorContext: ) -_BRIDGE_PATH = "agentpool_server.opencode_server.opencode_message_bridge.append_message_to_session" +_BRIDGE_PATH = ( + "wolfharness_server.opencode_server.opencode_message_bridge.append_message_to_session" +) async def test_accepted_source_produces_events() -> None: diff --git a/tests/servers/opencode_server/test_deferred_opencode.py b/tests/servers/opencode_server/test_deferred_opencode.py index 3d8dab6e3..d5ddefab8 100644 --- a/tests/servers/opencode_server/test_deferred_opencode.py +++ b/tests/servers/opencode_server/test_deferred_opencode.py @@ -12,18 +12,18 @@ import pytest -from agentpool.agents.events.events import ToolCallDeferredEvent -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.agents.events.events import ToolCallDeferredEvent +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, PartUpdatedEvent, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( TimeStartEndCompacted, ToolPart, ToolStateCompleted, @@ -35,7 +35,7 @@ if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState # ============================================================================= @@ -64,7 +64,7 @@ async def test_deferred_event_creates_tool_part_running(server_state: ServerStat agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -130,7 +130,7 @@ async def test_deferred_event_sets_title_with_deferred_strategy( agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -191,7 +191,7 @@ async def test_deferred_event_skipped_when_already_completed( agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -250,7 +250,7 @@ async def test_deferred_event_skipped_when_already_errored( Similar to the completed dedup test, but for ToolStateError. """ - from agentpool_server.opencode_server.models.parts import ( + from wolfharness_server.opencode_server.models.parts import ( TimeStartEnd, ToolStateError, ) @@ -264,7 +264,7 @@ async def test_deferred_event_skipped_when_already_errored( agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( diff --git a/tests/servers/opencode_server/test_diagnostic.py b/tests/servers/opencode_server/test_diagnostic.py index d878f9b7f..a58b124bb 100644 --- a/tests/servers/opencode_server/test_diagnostic.py +++ b/tests/servers/opencode_server/test_diagnostic.py @@ -7,11 +7,11 @@ import pytest -from agentpool_server.opencode_server.routes.global_routes import ( +from wolfharness_server.opencode_server.routes.global_routes import ( VERSION, GlobalEventFactory, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -38,7 +38,7 @@ def __init__(self, working_dir: str | None = "/tmp/test_wd") -> None: def get_event_factory(self) -> GlobalEventFactory: if self._event_factory is None: - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers self._event_factory = GlobalEventFactory( directory=self.working_dir or "", @@ -132,8 +132,8 @@ async def test_diagnostic_working_dir_none_returns_directory_null() -> None: from fastapi import FastAPI from httpx import ASGITransport, AsyncClient - from agentpool_server.opencode_server.dependencies import get_state - from agentpool_server.opencode_server.routes.global_routes import router as global_router + from wolfharness_server.opencode_server.dependencies import get_state + from wolfharness_server.opencode_server.routes.global_routes import router as global_router state = _make_state_with_none_working_dir() app = FastAPI() diff --git a/tests/servers/opencode_server/test_duplicate_message_id.py b/tests/servers/opencode_server/test_duplicate_message_id.py index c0ec6ca94..2f1f71624 100644 --- a/tests/servers/opencode_server/test_duplicate_message_id.py +++ b/tests/servers/opencode_server/test_duplicate_message_id.py @@ -9,7 +9,7 @@ Fix: ``append_message_to_session`` catches ``ValueError`` for duplicate message IDs and treats the write as idempotent. -See: https://github.com/Leoyzen/agentpool/issues/229 +See: https://github.com/Leoyzen/wolfharness/issues/229 """ from __future__ import annotations @@ -19,18 +19,18 @@ import pytest -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.opencode_message_bridge import ( +from wolfharness_server.opencode_server.opencode_message_bridge import ( append_message_to_session, ) if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.unit diff --git a/tests/servers/opencode_server/test_duplication_reproduction.py b/tests/servers/opencode_server/test_duplication_reproduction.py index 6a29b1583..443646a0a 100644 --- a/tests/servers/opencode_server/test_duplication_reproduction.py +++ b/tests/servers/opencode_server/test_duplication_reproduction.py @@ -22,15 +22,15 @@ import pytest -from agentpool.orchestrator.core import EventBus, EventEnvelope -from agentpool_server.opencode_server.models import ( +from wolfharness.orchestrator.core import EventBus, EventEnvelope +from wolfharness_server.opencode_server.models import ( MessageUpdatedEvent, MessageWithParts, PartUpdatedEvent, TextPart, UserMessage, ) -from agentpool_server.opencode_server.models.common import TimeCreated +from wolfharness_server.opencode_server.models.common import TimeCreated pytestmark = pytest.mark.integration @@ -57,7 +57,7 @@ def _make_user_message(session_id: str = "sess-dup") -> MessageWithParts: def _wrap_sse_event(data: Any, session_id: str) -> EventEnvelope: """Wrap an SSE event as the event bridge does (CustomEvent wrapper).""" - from agentpool.agents.events.events import CustomEvent + from wolfharness.agents.events.events import CustomEvent return EventEnvelope( source_session_id=session_id, diff --git a/tests/servers/opencode_server/test_elicitation_bug_coverage.py b/tests/servers/opencode_server/test_elicitation_bug_coverage.py index 18468609f..d392def65 100644 --- a/tests/servers/opencode_server/test_elicitation_bug_coverage.py +++ b/tests/servers/opencode_server/test_elicitation_bug_coverage.py @@ -30,14 +30,14 @@ from pydantic_ai.tools import DeferredToolRequests, RunContext import pytest -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events.events import ElicitationDeferredEvent -from agentpool.agents.native_agent.elicitation_bridge import ( +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events.events import ElicitationDeferredEvent +from wolfharness.agents.native_agent.elicitation_bridge import ( ElicitationFutureRegistry, create_elicitation_bridge_capability, ) -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -50,7 +50,7 @@ def _make_mock_session_controller(session_id: str, *, checkpoint_enabled: bool = False) -> Mock: """Create a mock SessionController with a SessionState for the given session.""" - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState session = SessionState(session_id=session_id, agent_name="test-agent") session.checkpoint_enabled = checkpoint_enabled @@ -125,8 +125,8 @@ def _make_provider( session_id, checkpoint_enabled=checkpoint_enabled ) - from agentpool_server.opencode_server.models import Session - from agentpool_server.opencode_server.models.common import TimeCreatedUpdated + from wolfharness_server.opencode_server.models import Session + from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated now = 0 session = Session( @@ -188,7 +188,7 @@ async def test_mcp_elicitation_registers_pending_question( ) with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ): await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -223,7 +223,7 @@ async def test_mcp_elicitation_event_published_to_event_bus( requests = _make_deferred_requests() with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ) as mock_emit: await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -257,7 +257,7 @@ async def test_resolve_question_succeeds_for_mcp_handle( ) with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ): await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -377,7 +377,7 @@ async def test_reply_to_mcp_elicitation_handle_succeeds( run_ctx: RunContext[Any], ) -> None: """reply_to_question succeeds for a handle registered via ElicitationBridgeCapability.""" - from agentpool_server.opencode_server.models import QuestionReply + from wolfharness_server.opencode_server.models import QuestionReply provider, _state, _controller = _make_provider("test-session") @@ -394,7 +394,7 @@ async def test_reply_to_mcp_elicitation_handle_succeeds( ) with patch( - "agentpool.agents.native_agent.elicitation_bridge._emit_elicitation_event", + "wolfharness.agents.native_agent.elicitation_bridge._emit_elicitation_event", new_callable=AsyncMock, ): await cap.handle_deferred_tool_calls(run_ctx, requests=requests) @@ -427,7 +427,7 @@ async def test_path3_broadcast_elicitation_question_called( agent_ctx: AgentContext, ) -> None: """handle_elicitation Path 3 calls broadcast_elicitation_question().""" - from agentpool.sessions.models import ElicitationResumePayload + from wolfharness.sessions.models import ElicitationResumePayload provider, _state, _controller = _make_provider("test-session", checkpoint_enabled=True) provider._broadcast_called = False @@ -493,7 +493,7 @@ class TestElicitationToolPartName: ToolPart is NOT auto-cleaned when it transitions to completed/error. This is an opencode TUI-side issue (session-data.ts) that cannot be - fixed in the agentpool repo. This test documents the behavior. + fixed in the wolfharness repo. This test documents the behavior. """ @pytest.mark.asyncio @@ -501,16 +501,16 @@ async def test_elicitation_tool_part_has_tool_name_elicitation( self, ) -> None: """ElicitationDeferredEvent creates a ToolPart with tool='elicitation'.""" - from agentpool_server.opencode_server.event_processor import EventProcessor - from agentpool_server.opencode_server.event_processor_context import ( + from wolfharness_server.opencode_server.event_processor import EventProcessor + from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) - from agentpool_server.opencode_server.models.message import ( + from wolfharness_server.opencode_server.models.message import ( MessagePath, MessageTime, MessageWithParts, ) - from agentpool_server.opencode_server.models.parts import ToolStateRunning + from wolfharness_server.opencode_server.models.parts import ToolStateRunning mock_state = MagicMock() assistant_msg = MessageWithParts.assistant( diff --git a/tests/servers/opencode_server/test_ensure_session.py b/tests/servers/opencode_server/test_ensure_session.py index 3a1bea7bf..99fe0d97a 100644 --- a/tests/servers/opencode_server/test_ensure_session.py +++ b/tests/servers/opencode_server/test_ensure_session.py @@ -6,16 +6,16 @@ import pytest -from agentpool.agents.base_agent import BaseAgent -from agentpool_server.opencode_server.models import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness_server.opencode_server.models import ( Session, SessionIdleEvent, SessionStatusEvent, SessionUpdatedEvent, TimeCreatedUpdated, ) -from agentpool_server.opencode_server.session_pool_integration import ensure_session -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.session_pool_integration import ensure_session +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -63,12 +63,12 @@ async def test_ensure_session_creates_new_session(mock_state: ServerState) -> No parent_id = "parent_session_456" with patch( - "agentpool_server.opencode_server.converters.opencode_to_session_data" + "wolfharness_server.opencode_server.converters.opencode_to_session_data" ) as mock_converter: mock_converter.return_value = MagicMock() with patch( - "agentpool_server.opencode_server.input_provider.OpenCodeInputProvider" + "wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider" ) as mock_provider_class: mock_provider = MagicMock() mock_provider_class.return_value = mock_provider @@ -114,13 +114,13 @@ async def test_ensure_session_persists_to_storage(mock_state: ServerState) -> No session_id = "test_session_789" with patch( - "agentpool_server.opencode_server.converters.opencode_to_session_data" + "wolfharness_server.opencode_server.converters.opencode_to_session_data" ) as mock_converter: mock_session_data = MagicMock() mock_converter.return_value = mock_session_data with patch( - "agentpool_server.opencode_server.input_provider.OpenCodeInputProvider" + "wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider" ) as mock_provider_class: mock_provider_class.return_value = MagicMock() @@ -144,9 +144,9 @@ async def test_ensure_session_caches_in_memory(mock_state: ServerState) -> None: session_id = "test_session_abc" with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), patch( - "agentpool_server.opencode_server.input_provider.OpenCodeInputProvider" + "wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider" ) as mock_provider_class, ): mock_provider = MagicMock() @@ -178,8 +178,8 @@ async def test_ensure_session_broadcasts_idle_events(mock_state: ServerState) -> session_id = "test_session_idle_event" with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), - patch("agentpool_server.opencode_server.input_provider.OpenCodeInputProvider"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider"), patch.object(mock_state, "broadcast_event", new=AsyncMock()) as mock_broadcast, ): await ensure_session(mock_state, session_id) @@ -206,9 +206,9 @@ async def test_ensure_session_creates_input_provider(mock_state: ServerState) -> session_id = "test_session_def" with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), patch( - "agentpool_server.opencode_server.input_provider.OpenCodeInputProvider" + "wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider" ) as mock_provider_class, ): mock_provider = MagicMock() @@ -227,8 +227,8 @@ async def test_ensure_session_without_parent_id(mock_state: ServerState) -> None session_id = "test_session_no_parent" with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), - patch("agentpool_server.opencode_server.input_provider.OpenCodeInputProvider"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider"), ): result = await ensure_session(mock_state, session_id) @@ -242,8 +242,8 @@ async def test_ensure_session_is_idempotent(mock_state: ServerState) -> None: session_id = "test_session_idempotent" with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), - patch("agentpool_server.opencode_server.input_provider.OpenCodeInputProvider"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider"), ): result1 = await ensure_session(mock_state, session_id) result2 = await ensure_session(mock_state, session_id) @@ -318,8 +318,8 @@ async def test_ensure_session_child_inherits_parent_project_and_directory( mock_state.sessions[parent_id] = parent_session with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), - patch("agentpool_server.opencode_server.input_provider.OpenCodeInputProvider"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider"), ): child = await ensure_session(mock_state, child_id, parent_id=parent_id) @@ -340,8 +340,8 @@ async def test_ensure_session_child_falls_back_when_parent_missing( orphan_parent_id = "nonexistent_parent" with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), - patch("agentpool_server.opencode_server.input_provider.OpenCodeInputProvider"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider"), ): child = await ensure_session(mock_state, child_id, parent_id=orphan_parent_id) @@ -362,8 +362,8 @@ async def test_ensure_session_child_skips_agent_binding(mock_state: ServerState) parent_id = "parent_session_xyz" with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), - patch("agentpool_server.opencode_server.input_provider.OpenCodeInputProvider"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider"), ): result = await ensure_session(mock_state, session_id, parent_id=parent_id) diff --git a/tests/servers/opencode_server/test_ensure_session_idle.py b/tests/servers/opencode_server/test_ensure_session_idle.py index 2a11178ae..5b71d3765 100644 --- a/tests/servers/opencode_server/test_ensure_session_idle.py +++ b/tests/servers/opencode_server/test_ensure_session_idle.py @@ -15,11 +15,11 @@ import pytest -from agentpool_server.opencode_server.routes.session_routes import _ensure_session_idle +from wolfharness_server.opencode_server.routes.session_routes import _ensure_session_idle if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.unit @@ -126,7 +126,7 @@ async def _hang_forever(_session_id: str) -> str: state = _make_state(session_state=session_state, session_pool=session_pool) with patch( - "agentpool_server.opencode_server.routes.session_routes._IDLE_WAIT_TIMEOUT", + "wolfharness_server.opencode_server.routes.session_routes._IDLE_WAIT_TIMEOUT", 0.05, ): await _ensure_session_idle(state, "sess-1") diff --git a/tests/servers/opencode_server/test_event_adaptation.py b/tests/servers/opencode_server/test_event_adaptation.py index 82c74d322..568ff531f 100644 --- a/tests/servers/opencode_server/test_event_adaptation.py +++ b/tests/servers/opencode_server/test_event_adaptation.py @@ -21,7 +21,7 @@ ) import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent as AgentPoolPartDeltaEvent, PartStartEvent, RunErrorEvent, @@ -32,11 +32,11 @@ ToolCallProgressEvent, ToolCallStartEvent, ) -from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, @@ -45,7 +45,7 @@ SessionErrorEvent, SessionStatusEvent, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( ReasoningPart, StepFinishPart, TextPart, @@ -79,7 +79,7 @@ def adapter_context() -> EventProcessorContext: time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ) @@ -746,7 +746,7 @@ async def test_part_end_produces_no_output( assert len(events) == 0, "PartEndEvent should not produce any OpenCode events" @pytest.mark.asyncio - async def test_no_agentpool_events_leak_through( + async def test_no_wolfharness_events_leak_through( self, adapter_context: EventProcessorContext, ) -> None: @@ -773,7 +773,7 @@ async def test_no_agentpool_events_leak_through( @pytest.fixture def event_context(): """Create an event context for testing event conversion.""" - from agentpool_server.opencode_server.event_processor_context import ( + from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) @@ -785,7 +785,7 @@ def event_context(): time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ) @@ -850,7 +850,7 @@ async def test_text_part_start_creates_text_part( event_context, ) -> None: """PartStartEvent with TextPart should yield PartUpdatedEvent with TextPart.""" - from agentpool.agents.events import PartStartEvent + from wolfharness.agents.events import PartStartEvent adapter = OpenCodeEventAdapter(context=event_context) @@ -868,7 +868,7 @@ async def test_thinking_part_start_creates_reasoning_part( event_context, ) -> None: """PartStartEvent with ThinkingPart should yield PartUpdatedEvent with ReasoningPart.""" - from agentpool.agents.events import PartStartEvent + from wolfharness.agents.events import PartStartEvent adapter = OpenCodeEventAdapter(context=event_context) @@ -877,7 +877,7 @@ async def test_thinking_part_start_creates_reasoning_part( part_updated = [e for e in events if isinstance(e, PartUpdatedEvent)] assert len(part_updated) == 1 - from agentpool_server.opencode_server.models.parts import ReasoningPart + from wolfharness_server.opencode_server.models.parts import ReasoningPart assert isinstance(part_updated[0].properties.part, ReasoningPart) assert part_updated[0].properties.part.text == "Let me think..." @@ -913,7 +913,7 @@ async def test_text_delta_yields_part_delta_event( event_context, ) -> None: """Text delta should yield PartDeltaEvent.""" - from agentpool.agents.events import ( + from wolfharness.agents.events import ( PartDeltaEvent as AgentPoolPartDeltaEvent, PartStartEvent, ) @@ -938,7 +938,7 @@ async def test_thinking_delta_yields_part_delta_event( event_context, ) -> None: """Thinking delta should yield PartDeltaEvent.""" - from agentpool.agents.events import ( + from wolfharness.agents.events import ( PartDeltaEvent as AgentPoolPartDeltaEvent, PartStartEvent, ) @@ -1335,7 +1335,7 @@ async def test_convert_stream_yields_all_events(self, event_context) -> None: """convert_stream should yield OpenCode events for all AgentPool events.""" async def _make_stream(): - from agentpool.agents.events import PartStartEvent + from wolfharness.agents.events import PartStartEvent yield PartStartEvent.text(index=0, content="Hello") yield PartStartEvent.text(index=1, content="World") @@ -1361,7 +1361,7 @@ async def test_all_required_events_have_handlers( event_context, ) -> None: """Every required AgentPool event type should have a conversion handler.""" - from agentpool.agents.events import ( + from wolfharness.agents.events import ( PartDeltaEvent, PartStartEvent, ToolCallCompleteEvent, @@ -1392,12 +1392,12 @@ async def test_all_required_events_have_handlers( assert len(events) >= 1, f"Event {type(event).__name__} produced no output" @pytest.mark.asyncio - async def test_no_agentpool_events_leak_through( + async def test_no_wolfharness_events_leak_through( self, event_context, ) -> None: """Converted events should all be OpenCode Event types, never raw AgentPool events.""" - from agentpool.agents.events import RunStartedEvent + from wolfharness.agents.events import RunStartedEvent adapter = OpenCodeEventAdapter(context=event_context) diff --git a/tests/servers/opencode_server/test_event_bridge.py b/tests/servers/opencode_server/test_event_bridge.py index df4f13bd2..de6ecab8d 100644 --- a/tests/servers/opencode_server/test_event_bridge.py +++ b/tests/servers/opencode_server/test_event_bridge.py @@ -13,18 +13,18 @@ import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( CustomEvent, RunFailedEvent, RunStartedEvent, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus, EventEnvelope -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus, EventEnvelope +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -34,11 +34,11 @@ SessionStatus, SessionStatusEvent, ) -from agentpool_server.opencode_server.models.events import ServerConnectedEvent -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.opencode_server.models.events import ServerConnectedEvent +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -48,7 +48,7 @@ from collections.abc import AsyncIterator from pathlib import Path - from agentpool_server.opencode_server.models.events import Event + from wolfharness_server.opencode_server.models.events import Event # ============================================================================= @@ -59,7 +59,7 @@ @pytest.fixture def bridged_state(tmp_project_dir: Path, mock_agent: Mock) -> ServerState: """Create a ServerState with an active OpenCodeEventBridge.""" - from agentpool.orchestrator.core import EventBus + from wolfharness.orchestrator.core import EventBus # Wire a real EventBus into the mock pool so __post_init__ can discover it mock_agent.host_context.session_pool.event_bus = EventBus() @@ -535,11 +535,11 @@ async def test_stream_complete_sets_time_completed() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): @@ -578,11 +578,11 @@ async def test_stream_complete_skips_if_already_completed() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): @@ -632,11 +632,11 @@ async def test_d1_reset_finalizes_previous_turn() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): @@ -699,11 +699,11 @@ async def test_d1_reset_skips_finalize_if_already_completed() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): @@ -732,7 +732,7 @@ async def test_d2_warning_logged_on_incomplete_turn() -> None: """ from unittest.mock import AsyncMock, patch - import agentpool_server.opencode_server.opencode_event_bridge as bridge_module + import wolfharness_server.opencode_server.opencode_event_bridge as bridge_module session_id = "sess-d2" bridge, _ctx, _broadcast_calls = _setup_bridge_for_handle( @@ -748,11 +748,11 @@ async def test_d2_warning_logged_on_incomplete_turn() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ), patch.object(bridge_module.logger, "warning") as mock_warning, @@ -792,11 +792,11 @@ async def test_run_failed_sets_time_completed() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): @@ -839,11 +839,11 @@ async def test_stream_complete_cancelled_sets_time_completed() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ), ): @@ -877,11 +877,11 @@ async def test_finalize_skips_when_no_context() -> None: with ( patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): diff --git a/tests/servers/opencode_server/test_event_pipeline_e2e.py b/tests/servers/opencode_server/test_event_pipeline_e2e.py index 9b732460b..0c4a4a22b 100644 --- a/tests/servers/opencode_server/test_event_pipeline_e2e.py +++ b/tests/servers/opencode_server/test_event_pipeline_e2e.py @@ -23,37 +23,37 @@ ) import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunStartedEvent, StreamCompleteEvent, ToolCallCompleteEvent, ToolCallProgressEvent, ToolCallStartEvent, ) -from agentpool.messaging import ChatMessage -from agentpool_server.opencode_server.models import PartDeltaEvent, PartUpdatedEvent -from agentpool_server.opencode_server.models.events import ( +from wolfharness.messaging import ChatMessage +from wolfharness_server.opencode_server.models import PartDeltaEvent, PartUpdatedEvent +from wolfharness_server.opencode_server.models.events import ( SessionErrorEvent, SessionStatusEvent, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( StepFinishPart, TextPart, ToolPart, ToolStateCompleted, ToolStateRunning, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration if TYPE_CHECKING: - from agentpool.orchestrator.core import SessionPool + from wolfharness.orchestrator.core import SessionPool # ============================================================================= @@ -74,8 +74,8 @@ def _extract_opencode_events(sse_queue: Any) -> list[Any]: event. This helper filters for CustomEvent wrappers and extracts the underlying OpenCode event from ``.event_data``. """ - from agentpool.agents.events.events import CustomEvent - from agentpool.orchestrator.core import EventEnvelope + from wolfharness.agents.events.events import CustomEvent + from wolfharness.orchestrator.core import EventEnvelope result: list[Any] = [] while not _stream_empty(sse_queue): @@ -117,7 +117,7 @@ def server_state(tmp_path: Any) -> ServerState: @pytest.fixture async def session_pool(server_state: ServerState): # type: ignore[no-untyped-def] """Create a real SessionPool with a real EventBus.""" - from agentpool.orchestrator.core import SessionPool + from wolfharness.orchestrator.core import SessionPool pool_mock = Mock() pool_mock.main_agent = Mock() @@ -144,7 +144,7 @@ async def session_pool(server_state: ServerState): # type: ignore[no-untyped-de server_state._pool = pool_mock pool_mock.session_pool = sp # Re-initialize event_bridge now that event_bus is available - from agentpool_server.opencode_server.event_bridge import OpenCodeEventBridge + from wolfharness_server.opencode_server.event_bridge import OpenCodeEventBridge server_state.event_bridge = OpenCodeEventBridge(server_state, sp.event_bus) @@ -480,7 +480,7 @@ async def test_run_error_produces_session_error( Verifies error events pass through event_bridge correctly. """ - from agentpool.agents.events import RunErrorEvent + from wolfharness.agents.events import RunErrorEvent integration = OpenCodeSessionPoolIntegration( session_pool=session_pool, diff --git a/tests/servers/opencode_server/test_event_processor.py b/tests/servers/opencode_server/test_event_processor.py index 827dd765f..617344308 100644 --- a/tests/servers/opencode_server/test_event_processor.py +++ b/tests/servers/opencode_server/test_event_processor.py @@ -16,11 +16,11 @@ ) import pytest -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, @@ -35,7 +35,7 @@ if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState # ============================================================================= @@ -61,7 +61,7 @@ async def test_process_text_start_creates_text_part(server_state: ServerState) - agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -112,7 +112,7 @@ async def test_process_text_delta_accumulates_text(server_state: ServerState) -> agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -165,7 +165,7 @@ async def test_process_text_delta_without_start(server_state: ServerState) -> No agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -209,7 +209,7 @@ def _make_stream_complete_context(server_state: ServerState) -> EventProcessorCo agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) return EventProcessorContext( @@ -224,8 +224,8 @@ def _make_stream_complete_context(server_state: ServerState) -> EventProcessorCo @pytest.mark.asyncio async def test_stream_complete_emits_idle_status(server_state: ServerState) -> None: """Test that a completed StreamCompleteEvent emits SessionStatusEvent(idle).""" - from agentpool.agents.events import StreamCompleteEvent - from agentpool.messaging import ChatMessage + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.messaging import ChatMessage processor = EventProcessor() ctx = _make_stream_complete_context(server_state) @@ -243,8 +243,8 @@ async def test_stream_complete_emits_idle_status(server_state: ServerState) -> N @pytest.mark.asyncio async def test_stream_complete_emits_cancelled_status(server_state: ServerState) -> None: """Test that a cancelled StreamCompleteEvent emits SessionStatusEvent(cancelled).""" - from agentpool.agents.events import StreamCompleteEvent - from agentpool.messaging import ChatMessage + from wolfharness.agents.events import StreamCompleteEvent + from wolfharness.messaging import ChatMessage processor = EventProcessor() ctx = _make_stream_complete_context(server_state) @@ -266,7 +266,7 @@ async def test_stream_complete_emits_cancelled_status(server_state: ServerState) def test_create_mcp_tools_changed_event() -> None: """EventProcessor.create_mcp_tools_changed_event creates correct event.""" - from agentpool_server.opencode_server.models.events import McpToolsChangedEvent + from wolfharness_server.opencode_server.models.events import McpToolsChangedEvent processor = EventProcessor() event = processor.create_mcp_tools_changed_event(server="my_mcp_server") @@ -284,8 +284,8 @@ async def test_mcp_tools_changed_event_from_change_event() -> None: 1. McpServerCap.on_change() yields ChangeEvent(kind="tools_changed") 2. EventProcessor.create_mcp_tools_changed_event() converts to McpToolsChangedEvent """ - from agentpool.capabilities.change_event import ChangeEvent - from agentpool_server.opencode_server.models.events import McpToolsChangedEvent + from wolfharness.capabilities.change_event import ChangeEvent + from wolfharness_server.opencode_server.models.events import McpToolsChangedEvent processor = EventProcessor() diff --git a/tests/servers/opencode_server/test_event_processor_diff.py b/tests/servers/opencode_server/test_event_processor_diff.py index c324fd5de..41ab59875 100644 --- a/tests/servers/opencode_server/test_event_processor_diff.py +++ b/tests/servers/opencode_server/test_event_processor_diff.py @@ -2,7 +2,7 @@ The opencode TUI Edit component reads ``metadata.diff`` as a unified diff text string (produced by ``createTwoFilesPatch`` in opencode's edit.ts). -The agentpool event_processor must convert ``DiffContentItem`` from +The wolfharness event_processor must convert ``DiffContentItem`` from ``ToolCallProgressEvent`` into that text format and carry it through to ``ToolStateCompleted.metadata.diff`` so the TUI can render the diff view. """ @@ -11,13 +11,13 @@ import pytest -from agentpool.agents.events import DiffContentItem, ToolCallProgressEvent -from agentpool.agents.events.events import ToolCallCompleteEvent, ToolCallStartEvent -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.agents.events import DiffContentItem, ToolCallProgressEvent +from wolfharness.agents.events.events import ToolCallCompleteEvent, ToolCallStartEvent +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( MessagePath, MessageTime, MessageWithParts, @@ -33,7 +33,7 @@ def _make_ctx(server_state: object) -> EventProcessorContext: agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) return EventProcessorContext( diff --git a/tests/servers/opencode_server/test_event_processor_user_message.py b/tests/servers/opencode_server/test_event_processor_user_message.py index 2e243eb82..505852d23 100644 --- a/tests/servers/opencode_server/test_event_processor_user_message.py +++ b/tests/servers/opencode_server/test_event_processor_user_message.py @@ -14,15 +14,15 @@ import pytest -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool_server.opencode_server.event_processor import ( +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness_server.opencode_server.event_processor import ( EventProcessor, OpenCodeUserMessageMeta, ) -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageUpdatedEvent, @@ -36,7 +36,7 @@ if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def _make_ctx(server_state: ServerState) -> EventProcessorContext: @@ -48,7 +48,7 @@ def _make_ctx(server_state: ServerState) -> EventProcessorContext: agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) return EventProcessorContext( diff --git a/tests/servers/opencode_server/test_event_processor_user_message_integration.py b/tests/servers/opencode_server/test_event_processor_user_message_integration.py index b91240303..d52d49909 100644 --- a/tests/servers/opencode_server/test_event_processor_user_message_integration.py +++ b/tests/servers/opencode_server/test_event_processor_user_message_integration.py @@ -14,15 +14,15 @@ import pytest -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool_server.opencode_server.event_processor import ( +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness_server.opencode_server.event_processor import ( EventProcessor, OpenCodeUserMessageMeta, ) -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageUpdatedEvent, @@ -36,7 +36,7 @@ if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def _make_ctx(server_state: ServerState) -> EventProcessorContext: @@ -48,7 +48,7 @@ def _make_ctx(server_state: ServerState) -> EventProcessorContext: agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) return EventProcessorContext( diff --git a/tests/servers/opencode_server/test_file_diff_models.py b/tests/servers/opencode_server/test_file_diff_models.py index ab9c71154..94e6cd940 100644 --- a/tests/servers/opencode_server/test_file_diff_models.py +++ b/tests/servers/opencode_server/test_file_diff_models.py @@ -6,7 +6,7 @@ import pytest -from agentpool_server.opencode_server.models.common import FileDiff +from wolfharness_server.opencode_server.models.common import FileDiff pytestmark = pytest.mark.integration @@ -22,7 +22,7 @@ class _StubFileChange: def to_unified_diff(self) -> str: """Return a minimal unified diff string.""" - from agentpool.utils.diffs import compute_unified_diff + from wolfharness.utils.diffs import compute_unified_diff return compute_unified_diff( self.old_content or "", diff --git a/tests/servers/opencode_server/test_global_compat_routes.py b/tests/servers/opencode_server/test_global_compat_routes.py index 7001cac0c..ddc8dea57 100644 --- a/tests/servers/opencode_server/test_global_compat_routes.py +++ b/tests/servers/opencode_server/test_global_compat_routes.py @@ -16,14 +16,14 @@ from fastapi.testclient import TestClient import pytest -from agentpool.models.manifest import AgentsManifest -from agentpool.storage import StorageManager -from agentpool.utils.streams import FileOpsTracker -from agentpool.utils.todos import TodoTracker -from agentpool_server.opencode_server.dependencies import get_state -from agentpool_server.opencode_server.routes.config_routes import router as config_router -from agentpool_server.opencode_server.routes.global_routes import router as global_router -from agentpool_server.opencode_server.state import ServerState +from wolfharness.models.manifest import AgentsManifest +from wolfharness.storage import StorageManager +from wolfharness.utils.streams import FileOpsTracker +from wolfharness.utils.todos import TodoTracker +from wolfharness_server.opencode_server.dependencies import get_state +from wolfharness_server.opencode_server.routes.config_routes import router as config_router +from wolfharness_server.opencode_server.routes.global_routes import router as global_router +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -36,7 +36,7 @@ @pytest.fixture def _server_state(tmp_path: Path) -> ServerState: """Build a ServerState with a mock agent for config route tests.""" - from agentpool_config.storage import MemoryStorageConfig, StorageConfig + from wolfharness_config.storage import MemoryStorageConfig, StorageConfig storage_manager = StorageManager(config=StorageConfig(providers=[MemoryStorageConfig()])) file_ops = FileOpsTracker() diff --git a/tests/servers/opencode_server/test_global_event.py b/tests/servers/opencode_server/test_global_event.py index 6a5ccbfa8..fe61910b7 100644 --- a/tests/servers/opencode_server/test_global_event.py +++ b/tests/servers/opencode_server/test_global_event.py @@ -11,10 +11,10 @@ import pytest -from agentpool_server.opencode_server.models import GlobalEvent -from agentpool_server.opencode_server.models.app import ProjectTime -from agentpool_server.opencode_server.models.common import TimeCreated -from agentpool_server.opencode_server.models.events import ( +from wolfharness_server.opencode_server.models import GlobalEvent +from wolfharness_server.opencode_server.models.app import ProjectTime +from wolfharness_server.opencode_server.models.common import TimeCreated +from wolfharness_server.opencode_server.models.events import ( CommandExecutedEvent, Event, FileEditedEvent, @@ -58,26 +58,26 @@ TuiToastShowEvent, VcsBranchUpdatedEvent, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( UserMessage, ) -from agentpool_server.opencode_server.models.parts import Part, TextPart -from agentpool_server.opencode_server.models.pty import PtyInfo -from agentpool_server.opencode_server.models.question import ( +from wolfharness_server.opencode_server.models.parts import Part, TextPart +from wolfharness_server.opencode_server.models.pty import PtyInfo +from wolfharness_server.opencode_server.models.question import ( QuestionInfo, QuestionOption, ) -from agentpool_server.opencode_server.models.session import ( +from wolfharness_server.opencode_server.models.session import ( Session, TimeCreatedUpdated, ) -from agentpool_server.opencode_server.routes.global_routes import ( +from wolfharness_server.opencode_server.routes.global_routes import ( GlobalEventFactory, _event_generator, _extract_session_id, _serialize_event, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState if TYPE_CHECKING: @@ -234,7 +234,7 @@ async def unsubscribe(self, session_id: str, queue: asyncio.Queue[Any]) -> None: queue.shutdown() async def publish(self, session_id: str, event: Any) -> None: - from agentpool.orchestrator.core import EventEnvelope + from wolfharness.orchestrator.core import EventEnvelope envelope = EventEnvelope(source_session_id=session_id, event=event) for subscriber_sid, subscribers in self._subscribers.items(): @@ -282,7 +282,7 @@ def __init__(self, working_dir: str = "/tmp/test_wd") -> None: def get_event_factory(self) -> GlobalEventFactory: if self._event_factory is None: - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers self._event_factory = GlobalEventFactory( directory=self.working_dir, @@ -541,7 +541,7 @@ async def test_global_event_integration_project_computed( server_state: ServerState, ) -> None: """Test project field is computed via compute_project_id.""" - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id event = SessionStatusEvent.create(session_id="s3", status_type="busy") results = await _collect_real_events(server_state, wrap_payload=True, events_to_send=[event]) @@ -1113,7 +1113,7 @@ def test_session_id_properties_base_class_hierarchy() -> None: SessionIdProperties, and that SessionErrorProperties (which has session_id: str | None) does NOT inherit from it. """ - from agentpool_server.opencode_server.models.events import SessionErrorProperties + from wolfharness_server.opencode_server.models.events import SessionErrorProperties # SessionIdProperties must have session_id: str assert "session_id" in SessionIdProperties.model_fields @@ -1364,7 +1364,7 @@ async def test_global_health_endpoint(async_client: AsyncClient) -> None: @pytest.mark.anyio async def test_global_health_endpoint_fields(async_client: AsyncClient) -> None: """GET /global/health returns correct healthy and version fields.""" - from agentpool_server.opencode_server.routes.global_routes import VERSION + from wolfharness_server.opencode_server.routes.global_routes import VERSION response = await async_client.get("/global/health") data = response.json() diff --git a/tests/servers/opencode_server/test_init_session.py b/tests/servers/opencode_server/test_init_session.py index bc2f5027b..e18003122 100644 --- a/tests/servers/opencode_server/test_init_session.py +++ b/tests/servers/opencode_server/test_init_session.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = [pytest.mark.asyncio, pytest.mark.integration] diff --git a/tests/servers/opencode_server/test_list_sessions_restore.py b/tests/servers/opencode_server/test_list_sessions_restore.py index 9bf051d2e..59d1b660d 100644 --- a/tests/servers/opencode_server/test_list_sessions_restore.py +++ b/tests/servers/opencode_server/test_list_sessions_restore.py @@ -13,10 +13,10 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.models import Session -from agentpool_server.opencode_server.models.common import TimeCreatedUpdated -from agentpool_server.opencode_server.routes.session_routes import list_sessions +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.models import Session +from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated +from wolfharness_server.opencode_server.routes.session_routes import list_sessions pytestmark = pytest.mark.integration @@ -109,7 +109,7 @@ async def test_list_sessions_returns_persisted_sessions_after_restart(): state = _make_state(store=store, session_controller_sessions=[]) with patch( - "agentpool_server.opencode_server.routes.session_routes.session_data_to_opencode" + "wolfharness_server.opencode_server.routes.session_routes.session_data_to_opencode" ) as mock_convert: mock_convert.side_effect = lambda data: _make_session( data.session_id, directory=data.cwd or "/test", title=data.metadata.get("title", "") @@ -144,7 +144,7 @@ async def test_in_memory_session_overrides_store_version(): ) with patch( - "agentpool_server.opencode_server.routes.session_routes.session_data_to_opencode" + "wolfharness_server.opencode_server.routes.session_routes.session_data_to_opencode" ) as mock_convert: mock_convert.return_value = _make_session("ses_1", directory="/test", title="From Store") result = await list_sessions(state, directory="/test") @@ -195,7 +195,7 @@ async def test_python_cwd_filter_catches_other_directories(): state = _make_state(store=store, session_controller_sessions=[]) with patch( - "agentpool_server.opencode_server.routes.session_routes.session_data_to_opencode" + "wolfharness_server.opencode_server.routes.session_routes.session_data_to_opencode" ) as mock_convert: mock_convert.side_effect = lambda data: _make_session( data.session_id, directory=data.cwd or "", title=data.metadata.get("title", "") @@ -218,7 +218,7 @@ async def test_legacy_path_when_no_session_controller(): state.session_controller = None with patch( - "agentpool_server.opencode_server.routes.session_routes.session_data_to_opencode" + "wolfharness_server.opencode_server.routes.session_routes.session_data_to_opencode" ) as mock_convert: mock_convert.return_value = _make_session("ses_1", directory="/test") result = await list_sessions(state, directory="/test") @@ -239,7 +239,7 @@ async def test_cwd_filter_on_store_first_path(): state = _make_state(store=store, session_controller_sessions=[]) with patch( - "agentpool_server.opencode_server.routes.session_routes.session_data_to_opencode" + "wolfharness_server.opencode_server.routes.session_routes.session_data_to_opencode" ) as mock_convert: mock_convert.return_value = _make_session("ses_1", directory="/test") await list_sessions(state, directory="/test") @@ -316,7 +316,7 @@ async def test_merged_list_sorted_by_recency(): state = _make_state(store=store, session_controller_sessions=[]) with patch( - "agentpool_server.opencode_server.routes.session_routes.session_data_to_opencode" + "wolfharness_server.opencode_server.routes.session_routes.session_data_to_opencode" ) as mock_convert: mock_convert.side_effect = lambda data: _make_session( data.session_id, @@ -383,7 +383,7 @@ async def test_state_sessions_cache_populated(): state = _make_state(store=store, session_controller_sessions=[]) with patch( - "agentpool_server.opencode_server.routes.session_routes.session_data_to_opencode" + "wolfharness_server.opencode_server.routes.session_routes.session_data_to_opencode" ) as mock_convert: mock_convert.return_value = _make_session("ses_1", directory="/test") await list_sessions(state, directory="/test") diff --git a/tests/servers/opencode_server/test_mcp_routes.py b/tests/servers/opencode_server/test_mcp_routes.py index 8c4c5f515..28496832b 100644 --- a/tests/servers/opencode_server/test_mcp_routes.py +++ b/tests/servers/opencode_server/test_mcp_routes.py @@ -10,7 +10,7 @@ import pytest -from agentpool.common_types import MCPServerStatus +from wolfharness.common_types import MCPServerStatus if TYPE_CHECKING: @@ -18,7 +18,7 @@ from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = [pytest.mark.asyncio, pytest.mark.integration] diff --git a/tests/servers/opencode_server/test_mcp_status_behavior.py b/tests/servers/opencode_server/test_mcp_status_behavior.py index fd5d954ac..ede4325d5 100644 --- a/tests/servers/opencode_server/test_mcp_status_behavior.py +++ b/tests/servers/opencode_server/test_mcp_status_behavior.py @@ -24,15 +24,15 @@ import pytest import yamling -from agentpool import AgentPool, AgentsManifest -from agentpool.capabilities.mcp_server_cap import McpServerCap -from agentpool_server.opencode_server.dependencies import get_state -from agentpool_server.opencode_server.routes import agent_router -from agentpool_server.opencode_server.state import ServerState +from wolfharness import AgentPool, AgentsManifest +from wolfharness.capabilities.mcp_server_cap import McpServerCap +from wolfharness_server.opencode_server.dependencies import get_state +from wolfharness_server.opencode_server.routes import agent_router +from wolfharness_server.opencode_server.state import ServerState if TYPE_CHECKING: - from agentpool_config.mcp_server import MCPServerConfig + from wolfharness_config.mcp_server import MCPServerConfig pytestmark = [pytest.mark.asyncio, pytest.mark.integration] diff --git a/tests/servers/opencode_server/test_mcp_status_converter.py b/tests/servers/opencode_server/test_mcp_status_converter.py index 09bacc825..3df05ebac 100644 --- a/tests/servers/opencode_server/test_mcp_status_converter.py +++ b/tests/servers/opencode_server/test_mcp_status_converter.py @@ -9,8 +9,8 @@ import pytest -from agentpool.common_types import MCPServerStatus -from agentpool_server.opencode_server.converters import to_mcp_status +from wolfharness.common_types import MCPServerStatus +from wolfharness_server.opencode_server.converters import to_mcp_status pytestmark = pytest.mark.unit diff --git a/tests/servers/opencode_server/test_message_id_alignment.py b/tests/servers/opencode_server/test_message_id_alignment.py index 548d6da15..35903f3fa 100644 --- a/tests/servers/opencode_server/test_message_id_alignment.py +++ b/tests/servers/opencode_server/test_message_id_alignment.py @@ -15,10 +15,10 @@ import pytest -from agentpool.lifecycle.types import DeliveryMode -from agentpool_server.opencode_server.event_processor_context import EventProcessorContext -from agentpool_server.opencode_server.models import MessagePath, MessageTime, MessageWithParts -from agentpool_server.opencode_server.models.message import ( +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness_server.opencode_server.event_processor_context import EventProcessorContext +from wolfharness_server.opencode_server.models import MessagePath, MessageTime, MessageWithParts +from wolfharness_server.opencode_server.models.message import ( CommandRequest, MessageRequest, TextPartInput, @@ -122,7 +122,7 @@ class TestRouteMessagePassesMessageId: @pytest.mark.asyncio async def test_route_message_stores_pending_message_id(self): """route_message should store message_id in _pending_message_ids.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -137,7 +137,7 @@ async def test_route_message_stores_pending_message_id(self): server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.broadcast_event = AsyncMock() integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -160,7 +160,7 @@ async def test_route_message_stores_pending_message_id(self): @pytest.mark.asyncio async def test_route_message_passes_message_id_to_receive_request(self): """route_message should pass message_id to receive_request.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -172,7 +172,7 @@ async def test_route_message_passes_message_id_to_receive_request(self): server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) integration = OpenCodeSessionPoolIntegration(session_pool, server_state) integration._start_event_consumer = AsyncMock() @@ -191,7 +191,7 @@ async def test_route_message_passes_message_id_to_receive_request(self): @pytest.mark.asyncio async def test_route_message_without_message_id_passes_none(self): """route_message should pass message_id=None when not provided.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -203,7 +203,7 @@ async def test_route_message_without_message_id_passes_none(self): server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) integration = OpenCodeSessionPoolIntegration(session_pool, server_state) integration._start_event_consumer = AsyncMock() @@ -229,7 +229,7 @@ class TestBeforeConsumerLoopPendingMessageId: @pytest.mark.asyncio async def test_before_consumer_loop_uses_pending_message_id(self): """_before_consumer_loop should use pending message_id when available.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -238,7 +238,7 @@ async def test_before_consumer_loop_uses_pending_message_id(self): session_pool.sessions.get_session = Mock(return_value=None) server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) integration = OpenCodeSessionPoolIntegration(session_pool, server_state) # Set a pending message_id @@ -256,7 +256,7 @@ async def test_before_consumer_loop_uses_pending_message_id(self): @pytest.mark.asyncio async def test_before_consumer_loop_generates_id_when_no_pending(self): """_before_consumer_loop should generate a new ID when no pending message_id.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -265,7 +265,7 @@ async def test_before_consumer_loop_generates_id_when_no_pending(self): session_pool.sessions.get_session = Mock(return_value=None) server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -296,17 +296,17 @@ async def test_handle_event_preserves_ctx_message_id_from_event(self): """ from pydantic_ai.messages import TextPart as PydanticTextPart - from agentpool.agents.events import PartStartEvent - from agentpool.orchestrator.event_bus import EventEnvelope - from agentpool_server.opencode_server.models import MessageWithParts - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness.agents.events import PartStartEvent + from wolfharness.orchestrator.event_bus import EventEnvelope + from wolfharness_server.opencode_server.models import MessageWithParts + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) session_pool = Mock() server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.broadcast_event = AsyncMock() integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -319,7 +319,7 @@ async def test_handle_event_preserves_ctx_message_id_from_event(self): agent_name="agent", model_id="default", parent_id="", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -363,16 +363,16 @@ async def _empty_convert(event): @pytest.mark.asyncio async def test_handle_event_does_not_update_when_event_has_no_message_id(self): """_handle_event should not update ctx.assistant_msg_id when event has no message_id.""" - from agentpool.agents.events import RunStartedEvent - from agentpool.orchestrator.event_bus import EventEnvelope - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness.agents.events import RunStartedEvent + from wolfharness.orchestrator.event_bus import EventEnvelope + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) session_pool = Mock() server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.broadcast_event = AsyncMock() integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -384,7 +384,7 @@ async def test_handle_event_does_not_update_when_event_has_no_message_id(self): agent_name="agent", model_id="default", parent_id="", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -436,8 +436,8 @@ class TestMessageIdTimestampConsistency: @pytest.mark.unit def test_id_timestamp_matches_now_ms(self) -> None: """C1: Timestamp decoded from ascending ID should match now_ms() window.""" - from agentpool.utils import identifiers - from agentpool.utils.time_utils import now_ms + from wolfharness.utils import identifiers + from wolfharness.utils.time_utils import now_ms ts_before = now_ms() msg_id = identifiers.ascending("message") @@ -455,7 +455,7 @@ def test_id_timestamp_matches_now_ms(self) -> None: @pytest.mark.unit def test_same_ms_ids_have_consistent_timestamps(self) -> None: """C1: Two IDs generated in rapid succession should have close timestamps.""" - from agentpool.utils import identifiers + from wolfharness.utils import identifiers id1 = identifiers.ascending("message") id2 = identifiers.ascending("message") @@ -487,14 +487,14 @@ class TestCustomEventBypassesRegistration: @pytest.mark.asyncio async def test_custom_event_does_not_register_assistant(self): """CustomEvent should NOT trigger assistant message registration.""" - from agentpool.agents.events.events import CustomEvent - from agentpool.orchestrator.event_bus import EventEnvelope - from agentpool_server.opencode_server.models import ( + from wolfharness.agents.events.events import CustomEvent + from wolfharness.orchestrator.event_bus import EventEnvelope + from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -503,7 +503,7 @@ async def test_custom_event_does_not_register_assistant(self): session_pool.sessions.get_session = Mock(return_value=None) server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.broadcast_event = AsyncMock() integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -515,7 +515,7 @@ async def test_custom_event_does_not_register_assistant(self): agent_name="agent", model_id="default", parent_id="", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -559,14 +559,14 @@ async def test_real_agent_event_does_register_assistant(self): the next real agent event (RunStartedEvent) must trigger assistant registration. """ - from agentpool.agents.events import RunStartedEvent - from agentpool.orchestrator.event_bus import EventEnvelope - from agentpool_server.opencode_server.models import ( + from wolfharness.agents.events import RunStartedEvent + from wolfharness.orchestrator.event_bus import EventEnvelope + from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -575,7 +575,7 @@ async def test_real_agent_event_does_register_assistant(self): session_pool.sessions.get_session = Mock(return_value=None) server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.broadcast_event = AsyncMock() integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -587,7 +587,7 @@ async def test_real_agent_event_does_register_assistant(self): agent_name="agent", model_id="default", parent_id="", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -614,7 +614,7 @@ async def _empty_convert(event): import unittest.mock as _mock with _mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=_mock.AsyncMock, ) as mock_append: event = RunStartedEvent( @@ -656,15 +656,15 @@ async def test_custom_event_then_runstarted_registers_on_runstarted(self): 2. System notifications 3. RunStartedEvent → register assistant message """ - from agentpool.agents.events import RunStartedEvent - from agentpool.agents.events.events import CustomEvent - from agentpool.orchestrator.event_bus import EventEnvelope - from agentpool_server.opencode_server.models import ( + from wolfharness.agents.events import RunStartedEvent + from wolfharness.agents.events.events import CustomEvent + from wolfharness.orchestrator.event_bus import EventEnvelope + from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -673,7 +673,7 @@ async def test_custom_event_then_runstarted_registers_on_runstarted(self): session_pool.sessions.get_session = Mock(return_value=None) server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.broadcast_event = AsyncMock() integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -685,7 +685,7 @@ async def test_custom_event_then_runstarted_registers_on_runstarted(self): agent_name="agent", model_id="default", parent_id="", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -738,7 +738,7 @@ async def _empty_convert(event): import unittest.mock as _mock with _mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=_mock.AsyncMock, ): await integration._handle_event("test-session", envelope2) @@ -767,16 +767,16 @@ class TestEventBridgeStepStartPart: @pytest.mark.asyncio async def test_step_start_part_broadcast_on_registration(self): """StepStartPart should be broadcast when assistant message is registered.""" - from agentpool.agents.events import RunStartedEvent - from agentpool.orchestrator.event_bus import EventEnvelope - from agentpool_server.opencode_server.models import ( + from wolfharness.agents.events import RunStartedEvent + from wolfharness.orchestrator.event_bus import EventEnvelope + from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, PartUpdatedEvent, StepStartPart, ) - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -785,7 +785,7 @@ async def test_step_start_part_broadcast_on_registration(self): session_pool.sessions.get_session = Mock(return_value=None) server_state = Mock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) server_state.broadcast_event = AsyncMock() integration = OpenCodeSessionPoolIntegration(session_pool, server_state) @@ -797,7 +797,7 @@ async def test_step_start_part_broadcast_on_registration(self): agent_name="agent", model_id="default", parent_id="", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -824,7 +824,7 @@ async def _empty_convert(event): import unittest.mock as _mock with _mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=_mock.AsyncMock, ): event = RunStartedEvent( diff --git a/tests/servers/opencode_server/test_message_models.py b/tests/servers/opencode_server/test_message_models.py index e67bf6343..3e52fdbdd 100644 --- a/tests/servers/opencode_server/test_message_models.py +++ b/tests/servers/opencode_server/test_message_models.py @@ -8,7 +8,7 @@ import pytest -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageRequest, MessageTime, @@ -17,7 +17,7 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( AssistantMessage, TextPartInput, ) @@ -102,7 +102,7 @@ def test_user_factory_creates_user_message(self): def test_assistant_factory_creates_assistant_message(self): """MessageWithParts.assistant() should create an assistant message.""" - from agentpool_server.opencode_server.models import AssistantMessage + from wolfharness_server.opencode_server.models import AssistantMessage msg = MessageWithParts.assistant( message_id="msg-1", diff --git a/tests/servers/opencode_server/test_message_routes_dedup.py b/tests/servers/opencode_server/test_message_routes_dedup.py index f544a347d..b03c19df8 100644 --- a/tests/servers/opencode_server/test_message_routes_dedup.py +++ b/tests/servers/opencode_server/test_message_routes_dedup.py @@ -11,15 +11,15 @@ import pytest -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool_server.opencode_server.event_processor import ( +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness_server.opencode_server.event_processor import ( EventProcessor, OpenCodeUserMessageMeta, ) -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, @@ -29,7 +29,7 @@ pytestmark = pytest.mark.unit if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def _make_ctx(server_state: ServerState) -> EventProcessorContext: @@ -41,7 +41,7 @@ def _make_ctx(server_state: ServerState) -> EventProcessorContext: agent_name="test-agent", model_id="test-model", parent_id="parent", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) return EventProcessorContext( diff --git a/tests/servers/opencode_server/test_message_routes_regression.py b/tests/servers/opencode_server/test_message_routes_regression.py index 43df99f63..71e397b7e 100644 --- a/tests/servers/opencode_server/test_message_routes_regression.py +++ b/tests/servers/opencode_server/test_message_routes_regression.py @@ -33,7 +33,7 @@ async def test_persist_message_to_storage_not_called_for_user_messages() -> None persist_message_to_storage function is not invoked. """ with patch( - "agentpool_server.opencode_server.routes.message_routes.persist_message_to_storage", + "wolfharness_server.opencode_server.routes.message_routes.persist_message_to_storage", new_callable=AsyncMock, ) as mock_persist: # Verify the mock was not called during import or setup @@ -43,7 +43,7 @@ async def test_persist_message_to_storage_not_called_for_user_messages() -> None assert mock_persist is not None # Verify the function exists in the module - from agentpool_server.opencode_server.routes import message_routes + from wolfharness_server.opencode_server.routes import message_routes assert hasattr(message_routes, "persist_message_to_storage"), ( "persist_message_to_storage should exist in message_routes module" diff --git a/tests/servers/opencode_server/test_mode_consistency.py b/tests/servers/opencode_server/test_mode_consistency.py index 584081fc2..e85ff3ccf 100644 --- a/tests/servers/opencode_server/test_mode_consistency.py +++ b/tests/servers/opencode_server/test_mode_consistency.py @@ -8,7 +8,7 @@ The TUI uses AssistantMessage.agent (name) to resolve the agent for display. These tests verify: -1. /agent endpoint returns mode="primary" for all agentpool agents (correct) +1. /agent endpoint returns mode="primary" for all wolfharness agents (correct) 2. Assistant messages created by _before_consumer_loop have mode=agent_name 3. chat_message_to_opencode preserves mode from ChatMessage.name 4. Subagent assistant messages have mode and agent matching the child agent @@ -20,8 +20,8 @@ import pytest -from agentpool.messaging import ChatMessage -from agentpool_server.opencode_server.converters import chat_message_to_opencode +from wolfharness.messaging import ChatMessage +from wolfharness_server.opencode_server.converters import chat_message_to_opencode pytestmark = pytest.mark.unit @@ -34,13 +34,13 @@ @pytest.mark.unit async def test_agent_endpoint_mode_is_primary_for_all_agents() -> None: - """GET /agent should return mode='primary' for all agentpool agents. + """GET /agent should return mode='primary' for all wolfharness agents. - AgentMode is Literal['subagent', 'primary', 'all']. All agentpool agents + AgentMode is Literal['subagent', 'primary', 'all']. All wolfharness agents are primary (visible in switcher). This is correct — mode is a category, not an agent identifier. """ - from agentpool_server.opencode_server.routes.agent_routes import list_agents + from wolfharness_server.opencode_server.routes.agent_routes import list_agents agent1 = MagicMock() agent1.description = "Agent 1" @@ -76,7 +76,7 @@ async def test_before_consumer_loop_sets_mode_to_agent_name() -> None: The TUI uses the message's mode and agent fields to identify which agent produced the message. Both should be the agent's name. """ - from agentpool_server.opencode_server.opencode_event_bridge import ( + from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) @@ -89,7 +89,7 @@ async def test_before_consumer_loop_sets_mode_to_agent_name() -> None: server_state = MagicMock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = MagicMock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = MagicMock(return_value=("default", "wolfharness")) bridge = OpenCodeEventBridgeMixin.__new__(OpenCodeEventBridgeMixin) bridge.session_pool = session_pool @@ -179,7 +179,7 @@ async def test_subagent_assistant_mode_matches_child_agent_name() -> None: When a subagent session is created, the child session's assistant message should have mode=child_agent_name so the TUI can display the correct agent. """ - from agentpool_server.opencode_server.opencode_event_bridge import ( + from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) @@ -192,7 +192,7 @@ async def test_subagent_assistant_mode_matches_child_agent_name() -> None: server_state = MagicMock() server_state.working_dir = "/tmp" - server_state.resolve_default_model_info = MagicMock(return_value=("default", "agentpool")) + server_state.resolve_default_model_info = MagicMock(return_value=("default", "wolfharness")) bridge = OpenCodeEventBridgeMixin.__new__(OpenCodeEventBridgeMixin) bridge.session_pool = session_pool diff --git a/tests/servers/opencode_server/test_model_default_resolution.py b/tests/servers/opencode_server/test_model_default_resolution.py index 7e1412213..5c32b3403 100644 --- a/tests/servers/opencode_server/test_model_default_resolution.py +++ b/tests/servers/opencode_server/test_model_default_resolution.py @@ -1,6 +1,6 @@ """Tests for ServerState.resolve_default_model_info() and dynamic model default propagation. -Replaces hardcoded "sonnet"/"claude-code"/"default"/"agentpool" fallbacks with +Replaces hardcoded "sonnet"/"claude-code"/"default"/"wolfharness" fallbacks with config-derived defaults resolved from the configured agent's model_name. """ @@ -10,7 +10,7 @@ import pytest -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.unit @@ -51,14 +51,14 @@ def test_resolves_model_with_colon_in_name(self) -> None: assert provider_id == "openai" def test_falls_back_when_model_name_is_none(self) -> None: - """When agent.model_name is None, returns ('default', 'agentpool').""" + """When agent.model_name is None, returns ('default', 'wolfharness').""" state = _make_server_state(None) model_id, provider_id = state.resolve_default_model_info() assert model_id == "default" - assert provider_id == "agentpool" + assert provider_id == "wolfharness" def test_falls_back_when_model_name_has_no_colon(self) -> None: - """When agent.model_name has no colon (e.g. 'gpt-4o'), returns ('default', 'agentpool'). + """When agent.model_name has no colon (e.g. 'gpt-4o'), returns ('default', 'wolfharness'). This is a graceful fallback — a well-formed model_name should always include a provider prefix, but we handle the edge case safely. @@ -66,4 +66,4 @@ def test_falls_back_when_model_name_has_no_colon(self) -> None: state = _make_server_state("gpt-4o") model_id, provider_id = state.resolve_default_model_info() assert model_id == "default" - assert provider_id == "agentpool" + assert provider_id == "wolfharness" diff --git a/tests/servers/opencode_server/test_model_variant_resolution.py b/tests/servers/opencode_server/test_model_variant_resolution.py index a338a467c..85b665ff4 100644 --- a/tests/servers/opencode_server/test_model_variant_resolution.py +++ b/tests/servers/opencode_server/test_model_variant_resolution.py @@ -13,13 +13,13 @@ import pytest -from agentpool.agents.events import StreamCompleteEvent -from agentpool.models.model_configs import AnyModelConfig, StringModelConfig -from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.models.model_configs import AnyModelConfig, StringModelConfig +from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageUpdatedEvent, diff --git a/tests/servers/opencode_server/test_opencode_model_switching.py b/tests/servers/opencode_server/test_opencode_model_switching.py index d5476fc4b..5a72fa295 100644 --- a/tests/servers/opencode_server/test_opencode_model_switching.py +++ b/tests/servers/opencode_server/test_opencode_model_switching.py @@ -1,7 +1,7 @@ """Tests to verify OpenCode model switching issues. These tests verify the root causes of the issue where model changes -in OpenCode TUI are not reflected in agentpool runtime. +in OpenCode TUI are not reflected in wolfharness runtime. """ from __future__ import annotations @@ -11,8 +11,9 @@ import pytest -from agentpool import Agent, AgentPool, AgentsManifest -from agentpool_server.opencode_server.models import ( +from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness import Agent, AgentPool, AgentsManifest +from wolfharness_server.opencode_server.models import ( MessageRequest, MessageWithParts, ModelRef, @@ -20,8 +21,7 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.models.config import Config -from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness_server.opencode_server.models.config import Config if TYPE_CHECKING: @@ -315,7 +315,7 @@ async def test_opencode_model_flow_simulation(): """Simulate the complete OpenCode model switching flow. This test simulates: - 1. OpenCode TUI connects to agentpool + 1. OpenCode TUI connects to wolfharness 2. TUI gets available models (including model_variants as synthetic provider) 3. User selects 'qwen35' in TUI 4. TUI sends message with model override @@ -432,9 +432,9 @@ def _make_mock_state_with_session_agent( """Create a ServerState wired so get_or_create_session_agent returns per-session mocks.""" from unittest.mock import AsyncMock, Mock - from agentpool.lifecycle import RunOutcome, RunState - from agentpool.utils.time_utils import now_ms - from agentpool_server.opencode_server.state import ServerState + from wolfharness.lifecycle import RunOutcome, RunState + from wolfharness.utils.time_utils import now_ms + from wolfharness_server.opencode_server.state import ServerState shared_agent = PerSessionAgentMock(name="shared-agent") shared_agent.env = Mock() @@ -508,8 +508,8 @@ async def _get_or_create_session_agent( # Pre-populate sessions in state for session_id in session_agents: - from agentpool_server.opencode_server.models import Session - from agentpool_server.opencode_server.models.common import TimeCreatedUpdated + from wolfharness_server.opencode_server.models import Session + from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated now = now_ms() session = Session( @@ -535,7 +535,7 @@ async def test_model_switch_targets_per_session_agent(tmp_project_dir: Path) -> ``session_pool.sessions.get_or_create_session_agent()`` so each session gets its own isolated model configuration. """ - from agentpool.utils import identifiers as identifier + from wolfharness.utils import identifiers as identifier session_id = "test-session-a" session_agents: dict[str, PerSessionAgentMock] = { @@ -580,7 +580,7 @@ async def test_model_switch_targets_per_session_agent(tmp_project_dir: Path) -> @pytest.mark.unit async def test_model_switch_affects_only_target_session(tmp_project_dir: Path) -> None: """Switching model in session A must not affect session B's agent.""" - from agentpool.utils import identifiers as identifier + from wolfharness.utils import identifiers as identifier session_a = "session-a" session_b = "session-b" @@ -640,7 +640,7 @@ async def test_model_switch_affects_only_target_session(tmp_project_dir: Path) - @pytest.mark.unit async def test_other_sessions_retain_original_model(tmp_project_dir: Path) -> None: """After switching model in one session, other sessions keep their original model.""" - from agentpool.utils import identifiers as identifier + from wolfharness.utils import identifiers as identifier session_a = "session-a" session_b = "session-b" diff --git a/tests/servers/opencode_server/test_opencode_resume.py b/tests/servers/opencode_server/test_opencode_resume.py index c5db5e0d1..3bbf740d9 100644 --- a/tests/servers/opencode_server/test_opencode_resume.py +++ b/tests/servers/opencode_server/test_opencode_resume.py @@ -12,24 +12,24 @@ import pytest -from agentpool.orchestrator.core import SessionPool -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.orchestrator.core import SessionPool +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( TextPart, ToolPart, ToolStateRunning, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -85,7 +85,7 @@ def server_state(tmp_path: Any) -> ServerState: def _make_assistant_msg(session_id: str, working_dir: str) -> MessageWithParts: """Create a fresh assistant MessageWithParts for testing.""" - from agentpool.utils.time_utils import now_ms + from wolfharness.utils.time_utils import now_ms return MessageWithParts.assistant( message_id="msg-test-001", @@ -94,7 +94,7 @@ def _make_assistant_msg(session_id: str, working_dir: str) -> MessageWithParts: agent_name="test-agent", model_id="test-model", parent_id=session_id, - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd=working_dir, root=working_dir), ) @@ -184,7 +184,7 @@ def test_serialize_deserialize_roundtrip_with_tool_parts( ) # Add a tool part - from agentpool_server.opencode_server.models.parts import ( + from wolfharness_server.opencode_server.models.parts import ( TimeStart, ) @@ -354,7 +354,7 @@ async def test_restored_context_has_correct_assistant_msg( ) # Add a tool part to the context and assistant message - from agentpool_server.opencode_server.models.parts import ( + from wolfharness_server.opencode_server.models.parts import ( TimeStart, ) @@ -455,7 +455,7 @@ async def test_restored_context_does_not_replay_completed_parts( working_dir=server_state.working_dir, ) # Simulate an already-completed part - from agentpool_server.opencode_server.models.parts import ( + from wolfharness_server.opencode_server.models.parts import ( TimeStart, ) @@ -650,7 +650,7 @@ async def test_deserialize_preserves_subagent_tool_parts( working_dir=server_state.working_dir, ) - from agentpool_server.opencode_server.models.parts import ( + from wolfharness_server.opencode_server.models.parts import ( TimeStart, ) diff --git a/tests/servers/opencode_server/test_p1_part_updated_event.py b/tests/servers/opencode_server/test_p1_part_updated_event.py index 41571d119..9ce6f52fd 100644 --- a/tests/servers/opencode_server/test_p1_part_updated_event.py +++ b/tests/servers/opencode_server/test_p1_part_updated_event.py @@ -16,12 +16,12 @@ import pytest -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageUpdatedEvent, @@ -35,7 +35,7 @@ if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def _make_ctx(server_state: ServerState) -> EventProcessorContext: @@ -47,7 +47,7 @@ def _make_ctx(server_state: ServerState) -> EventProcessorContext: agent_name="test-agent", model_id="test-model", parent_id="parent-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) return EventProcessorContext( diff --git a/tests/servers/opencode_server/test_p2_protocol_channel_routing.py b/tests/servers/opencode_server/test_p2_protocol_channel_routing.py index 5954d7058..206330d86 100644 --- a/tests/servers/opencode_server/test_p2_protocol_channel_routing.py +++ b/tests/servers/opencode_server/test_p2_protocol_channel_routing.py @@ -19,11 +19,11 @@ import pytest -from agentpool.agents.events import UserMessageInsertedEvent -from agentpool.lifecycle.comm_channel import ProtocolChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.orchestrator.core import EventBus -from agentpool.orchestrator.session_controller_runs import ( +from wolfharness.agents.events import UserMessageInsertedEvent +from wolfharness.lifecycle.comm_channel import ProtocolChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.orchestrator.core import EventBus +from wolfharness.orchestrator.session_controller_runs import ( SessionControllerRunsMixin, ) @@ -32,7 +32,7 @@ if TYPE_CHECKING: - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.orchestrator.session_controller import SessionState class _FakeController(SessionControllerRunsMixin): @@ -301,7 +301,7 @@ async def test_replayed_non_user_message_still_published() -> None: Then: The event IS still published to the EventBus (only UserMessageInsertedEvent is skipped during replay). """ - from agentpool.agents.events.events import RunStartedEvent + from wolfharness.agents.events.events import RunStartedEvent event_bus = EventBus() protocol_channel = _make_protocol_channel(event_bus, "sess-p2") diff --git a/tests/servers/opencode_server/test_p3_context_serialization.py b/tests/servers/opencode_server/test_p3_context_serialization.py index 6c7cbe524..e79a97d3e 100644 --- a/tests/servers/opencode_server/test_p3_context_serialization.py +++ b/tests/servers/opencode_server/test_p3_context_serialization.py @@ -14,22 +14,22 @@ import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( RunFailedEvent, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventEnvelope -from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) @@ -156,11 +156,11 @@ async def test_context_serialized_after_stream_complete() -> None: with ( __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ), ): @@ -225,7 +225,7 @@ async def test_serialization_failure_falls_back_to_fresh_context() -> None: Then: An error is logged, the turn does NOT crash, and no resume data is stored. """ - import agentpool_server.opencode_server.opencode_event_bridge as bridge_module + import wolfharness_server.opencode_server.opencode_event_bridge as bridge_module session_id = "sess-p3-fail" bridge, ctx, _broadcast_calls = _setup_bridge_for_handle( @@ -244,11 +244,11 @@ async def test_serialization_failure_falls_back_to_fresh_context() -> None: with ( __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ), __import__("unittest.mock").mock.patch.object( @@ -298,11 +298,11 @@ async def test_context_serialized_after_run_failed() -> None: with ( __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ), ): diff --git a/tests/servers/opencode_server/test_p4_message_registered_reset.py b/tests/servers/opencode_server/test_p4_message_registered_reset.py index 91d6e254f..bff225ef2 100644 --- a/tests/servers/opencode_server/test_p4_message_registered_reset.py +++ b/tests/servers/opencode_server/test_p4_message_registered_reset.py @@ -19,22 +19,22 @@ import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( RunFailedEvent, RunStartedEvent, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventEnvelope -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) @@ -138,11 +138,11 @@ def _patch_mocks(): def _ctx(): with ( __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): @@ -223,7 +223,7 @@ async def test_d1_fires_on_turn2_after_stream_complete() -> None: - Turn 2: RunStarted → D1 fires (True) → new msg → _message_registered=False - Turn 2: register → _message_registered=True """ - import agentpool_server.opencode_server.opencode_event_bridge as bridge_module + import wolfharness_server.opencode_server.opencode_event_bridge as bridge_module session_id = "sess-p4-d1" bridge, ctx, _broadcast = _setup_bridge( diff --git a/tests/servers/opencode_server/test_part_id_dedup.py b/tests/servers/opencode_server/test_part_id_dedup.py index c88f12981..d00c4fa59 100644 --- a/tests/servers/opencode_server/test_part_id_dedup.py +++ b/tests/servers/opencode_server/test_part_id_dedup.py @@ -22,11 +22,11 @@ import pytest -from agentpool_server.opencode_server.event_processor import ( +from wolfharness_server.opencode_server.event_processor import ( EventProcessor, OpenCodeUserMessageMeta, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( MessagePath, MessageTime, MessageWithParts, @@ -35,7 +35,7 @@ def _make_ctx(session_id: str = "test-session") -> Any: """Create a minimal EventProcessorContext for testing.""" - from agentpool_server.opencode_server.event_processor_context import ( + from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) @@ -46,7 +46,7 @@ def _make_ctx(session_id: str = "test-session") -> Any: agent_name="test-agent", model_id="test-model", parent_id="parent-001", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) ctx = EventProcessorContext( @@ -74,7 +74,7 @@ async def test_accepted_source_does_not_yield_part_updated_events() -> None: # Mock append_message_to_session to avoid actual state mutation with patch( - "agentpool_server.opencode_server.opencode_message_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_message_bridge.append_message_to_session", new_callable=AsyncMock, ): meta = OpenCodeUserMessageMeta( @@ -118,7 +118,7 @@ async def test_accepted_source_yields_part_updated_events() -> None: ctx.state.messages = {} with patch( - "agentpool_server.opencode_server.opencode_message_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_message_bridge.append_message_to_session", new_callable=AsyncMock, ): meta = OpenCodeUserMessageMeta( @@ -157,8 +157,8 @@ async def test_accepted_source_part_ids_differ_from_db_reconstruction() -> None: sources causes duplication: ``chat_message_to_opencode`` generates new part IDs, different from the original parts in meta. """ - from agentpool.messaging.messages import ChatMessage - from agentpool_server.opencode_server.converters import chat_message_to_opencode + from wolfharness.messaging.messages import ChatMessage + from wolfharness_server.opencode_server.converters import chat_message_to_opencode # Create a ChatMessage as stored in DB chat_msg = ChatMessage[str]( @@ -197,7 +197,7 @@ async def test_accepted_source_no_part_updated_with_text_content() -> None: ctx.state.messages = {} with patch( - "agentpool_server.opencode_server.opencode_message_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_message_bridge.append_message_to_session", new_callable=AsyncMock, ): events = [] @@ -226,7 +226,7 @@ async def test_accepted_source_text_content_yields_part_updated() -> None: ctx.state.messages = {} with patch( - "agentpool_server.opencode_server.opencode_message_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_message_bridge.append_message_to_session", new_callable=AsyncMock, ): events = [] diff --git a/tests/servers/opencode_server/test_permission_session_state.py b/tests/servers/opencode_server/test_permission_session_state.py index 6f66ae96a..382f72429 100644 --- a/tests/servers/opencode_server/test_permission_session_state.py +++ b/tests/servers/opencode_server/test_permission_session_state.py @@ -11,17 +11,20 @@ import pytest -from agentpool.orchestrator.core import SessionState -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider, PendingPermission -from agentpool_server.opencode_server.models import ( +from wolfharness.orchestrator.core import SessionState +from wolfharness_server.opencode_server.input_provider import ( + OpenCodeInputProvider, + PendingPermission, +) +from wolfharness_server.opencode_server.models import ( PermissionReplyRequest, PermissionResolvedEvent, ) -from agentpool_server.opencode_server.routes.permission_routes import ( +from wolfharness_server.opencode_server.routes.permission_routes import ( list_permissions, reply_to_permission, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_project_id_mismatch.py b/tests/servers/opencode_server/test_project_id_mismatch.py index 3603cbb3b..d726866e9 100644 --- a/tests/servers/opencode_server/test_project_id_mismatch.py +++ b/tests/servers/opencode_server/test_project_id_mismatch.py @@ -22,18 +22,18 @@ from httpx import ASGITransport, AsyncClient import pytest -from agentpool_server.opencode_server.dependencies import get_state -from agentpool_server.opencode_server.models import SessionStatusEvent -from agentpool_server.opencode_server.routes import app_router, global_router -from agentpool_server.opencode_server.routes.global_routes import GlobalEventFactory -from agentpool_storage.opencode_provider.helpers import compute_project_id +from wolfharness_server.opencode_server.dependencies import get_state +from wolfharness_server.opencode_server.models import SessionStatusEvent +from wolfharness_server.opencode_server.routes import app_router, global_router +from wolfharness_server.opencode_server.routes.global_routes import GlobalEventFactory +from wolfharness_storage.opencode_provider.helpers import compute_project_id if TYPE_CHECKING: from collections.abc import AsyncIterator from pathlib import Path - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState @pytest.fixture diff --git a/tests/servers/opencode_server/test_prompt_async.py b/tests/servers/opencode_server/test_prompt_async.py index 98bc8a6b0..d14e0e303 100644 --- a/tests/servers/opencode_server/test_prompt_async.py +++ b/tests/servers/opencode_server/test_prompt_async.py @@ -6,7 +6,7 @@ import pytest -from agentpool_server.opencode_server.models import MessageRequest, TextPartInput +from wolfharness_server.opencode_server.models import MessageRequest, TextPartInput pytestmark = pytest.mark.integration @@ -121,7 +121,7 @@ async def test_prompt_async_creates_user_message( response = await async_client.post("/session", json={"title": "Async Message"}) session_id = response.json()["id"] - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( get_messages_for_session, ) diff --git a/tests/servers/opencode_server/test_question_handling.py b/tests/servers/opencode_server/test_question_handling.py index 5f1ca86d1..821195729 100644 --- a/tests/servers/opencode_server/test_question_handling.py +++ b/tests/servers/opencode_server/test_question_handling.py @@ -15,12 +15,16 @@ from mcp import types import pytest -from agentpool.orchestrator.core import SessionController, SessionState -from agentpool.tasks.exceptions import RunAbortedError -from agentpool.utils import identifiers as identifier -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider, PendingPermission -from agentpool_server.opencode_server.models import ( +from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness.orchestrator.core import SessionController, SessionState +from wolfharness.tasks.exceptions import RunAbortedError +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.input_provider import ( + OpenCodeInputProvider, + PendingPermission, +) +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageRequest, PermissionRequestEvent, @@ -30,22 +34,21 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( MessageAbortedError, MessageWithParts, ) -from agentpool_server.opencode_server.routes.question_routes import ( +from wolfharness_server.opencode_server.routes.question_routes import ( list_questions, reject_question, reply_to_question, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( append_message_to_session, get_messages_for_session, get_session_status, ) -from agentpool_server.opencode_server.state import PendingQuestion, ServerState -from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness_server.opencode_server.state import PendingQuestion, ServerState pytestmark = pytest.mark.integration @@ -78,7 +81,7 @@ def __init__(self) -> None: self._input_provider = None self.model_name = "test-model" self.session_id: str | None = None - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory self.conversation = MessageHistory() @@ -125,7 +128,7 @@ def __init__(self) -> None: self.model_name = "test-model" self.session_id: str | None = None self.block_forever_event: asyncio.Event = asyncio.Event() - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory self.conversation = MessageHistory() @@ -176,7 +179,7 @@ def __init__(self, state: ServerState) -> None: self.model_name = "test-model" self.session_id: str | None = None self._state = state - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory self.conversation = MessageHistory() @@ -299,7 +302,7 @@ async def _mock_receive_request( message_id: str | None = None, **kwargs: Any, ) -> str | None: - from agentpool.lifecycle import RunOutcome, RunState + from wolfharness.lifecycle import RunOutcome, RunState complete_event = asyncio.Event() _completion_events[session_id] = complete_event @@ -318,7 +321,7 @@ async def _background_run(): run_handle._run_state = RunState.DONE run_handle.outcome = RunOutcome.FAILED # Publish RunFailedEvent so the message_routes error path fires - from agentpool.agents.events import RunFailedEvent + from wolfharness.agents.events import RunFailedEvent await session_pool.event_bus.publish( session_id, @@ -411,7 +414,7 @@ def blocking_real_question_state(tmp_project_dir): placeholder_agent.storage = placeholder_agent.agent_pool.storage state = ServerState(working_dir=str(tmp_project_dir), agent=placeholder_agent) # Set up a mock session_controller for the BlockingOnRealQuestionAgentMock - from agentpool.orchestrator.core import SessionState as SPSessionState + from wolfharness.orchestrator.core import SessionState as SPSessionState sp_session = SPSessionState(session_id="test-session", agent_name="test-agent") controller = Mock() @@ -457,8 +460,8 @@ def sample_message_request(): def _setup_session(state: ServerState, session_id: str) -> None: """Set up session state manually.""" - from agentpool_server.opencode_server.models import Session - from agentpool_server.opencode_server.models.common import TimeCreatedUpdated + from wolfharness_server.opencode_server.models import Session + from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated now = now_ms() session = Session( @@ -665,7 +668,7 @@ async def _mock_get_status(sid: str) -> Any: _original_broadcast = state.broadcast_event async def _capturing_broadcast(event: Any) -> None: - from agentpool_server.opencode_server.models import SessionStatusEvent + from wolfharness_server.opencode_server.models import SessionStatusEvent if isinstance(event, SessionStatusEvent): _session_statuses[event.properties.session_id] = event.properties.status @@ -691,7 +694,7 @@ async def _mock_create_session(sid: str, *args: Any, **kw: Any) -> Any: session_id, sample_message_request, state, user_msg_id, user_msg_with_parts ) # Phase 3: mark session idle (not included in run_message_phases) - from agentpool_server.opencode_server.routes.message_routes import ( + from wolfharness_server.opencode_server.routes.message_routes import ( _mark_session_idle_safe, ) @@ -852,7 +855,7 @@ async def test_no_deadlock_different_session_after_blocking_question( # This MUST NOT deadlock — per-session agents resolve the issue try: - from agentpool_server.opencode_server.routes.session_routes import get_or_load_session + from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session await asyncio.wait_for(get_or_load_session(state, new_session_id), timeout=1.0) # Either gets the session or None — both are fine, no deadlock @@ -1044,7 +1047,7 @@ async def test_cancel_all_pending_questions_allows_new_session_access_after_sse_ _setup_session(state, new_session_id) # This MUST NOT deadlock — per-session agents resolve the issue - from agentpool_server.opencode_server.routes.session_routes import get_or_load_session + from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session try: await asyncio.wait_for(get_or_load_session(state, new_session_id), timeout=2.0) @@ -1084,7 +1087,7 @@ def __init__(self) -> None: self._input_provider = None self.model_name = "test-model" self.session_id: str | None = None - from agentpool.messaging.message_history import MessageHistory + from wolfharness.messaging.message_history import MessageHistory self.conversation = MessageHistory() @@ -1201,7 +1204,7 @@ async def acquire_raising_cancel() -> bool: def _make_mock_session_controller(session_id: str) -> Mock: """Create a mock SessionController with a SessionState for the given session.""" - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState session = SessionState(session_id=session_id, agent_name="test-agent") controller = Mock() @@ -1699,8 +1702,8 @@ async def test_multi_question_rfc0010_backward_compat(): async def test_multi_question_event_structure(): """Test that SSE QuestionAskedEvent has correct structure for multi-questions.""" - from agentpool_server.opencode_server.models.events import QuestionAskedEvent - from agentpool_server.opencode_server.models.question import QuestionInfo, QuestionOption + from wolfharness_server.opencode_server.models.events import QuestionAskedEvent + from wolfharness_server.opencode_server.models.question import QuestionInfo, QuestionOption # Create a QuestionsAskedEvent with multiple questions questions = [ @@ -1834,7 +1837,7 @@ def _make_pending_question( future: asyncio.Future[list[list[str]]] | None = None, ) -> PendingQuestion: """Create a PendingQuestion for testing.""" - from agentpool_server.opencode_server.models.question import QuestionInfo, QuestionOption + from wolfharness_server.opencode_server.models.question import QuestionInfo, QuestionOption if future is None: future = asyncio.get_event_loop().create_future() diff --git a/tests/servers/opencode_server/test_reasoning.py b/tests/servers/opencode_server/test_reasoning.py index 63a314971..3d6c61e2b 100644 --- a/tests/servers/opencode_server/test_reasoning.py +++ b/tests/servers/opencode_server/test_reasoning.py @@ -12,15 +12,15 @@ ) import pytest -from agentpool_server.opencode_server.event_processor import EventProcessor -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_processor import EventProcessor +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import PartUpdatedEvent -from agentpool_server.opencode_server.models.events import ( +from wolfharness_server.opencode_server.models import PartUpdatedEvent +from wolfharness_server.opencode_server.models.events import ( PartUpdatedEventProperties, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( ReasoningPart, TextPart as OpenCodeTextPart, ) @@ -333,7 +333,7 @@ async def test_reasoning_part_gets_end_time_on_stream_complete(): ) # Stream completes without any text starting - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage chat_msg = ChatMessage(content="", role="assistant") events.extend(list(processor._process_stream_complete(ctx, chat_msg))) diff --git a/tests/servers/opencode_server/test_replay_buffer_sync.py b/tests/servers/opencode_server/test_replay_buffer_sync.py index c44dc418b..1e3221d48 100644 --- a/tests/servers/opencode_server/test_replay_buffer_sync.py +++ b/tests/servers/opencode_server/test_replay_buffer_sync.py @@ -24,19 +24,19 @@ import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( UserMessageInsertedEvent, ) -from agentpool.orchestrator.core import EventBus -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.orchestrator.core import EventBus +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) diff --git a/tests/servers/opencode_server/test_resource_resolution.py b/tests/servers/opencode_server/test_resource_resolution.py index 2065491d8..9af226251 100644 --- a/tests/servers/opencode_server/test_resource_resolution.py +++ b/tests/servers/opencode_server/test_resource_resolution.py @@ -5,7 +5,7 @@ ``ResourceSource`` (L2 integration tests). The tests are written against the target API where: -- ``resolve_resource_content()`` lives in ``agentpool.capabilities.resource_resolver`` +- ``resolve_resource_content()`` lives in ``wolfharness.capabilities.resource_resolver`` - ``_resolve_resource()`` in the converter delegates to ``resolve_resource_content()`` by filtering ``agent._all_capabilities`` via ``isinstance`` checks. """ @@ -19,13 +19,13 @@ from pydantic_ai import BinaryContent import pytest -from agentpool.capabilities.resource_protocols import ( +from wolfharness.capabilities.resource_protocols import ( BlobResourceContent, ResourceEntry, SkillEntry, TextResourceContent, ) -from agentpool.capabilities.resource_resolver import resolve_resource_content +from wolfharness.capabilities.resource_resolver import resolve_resource_content if TYPE_CHECKING: @@ -104,7 +104,7 @@ class FakeAgent: """ def __init__(self, capabilities: list[Any], name: str = "fake-agent") -> None: - from agentpool.capabilities.extension_registry import ExtensionRegistry, Scope, ScopeLevel + from wolfharness.capabilities.extension_registry import ExtensionRegistry, Scope, ScopeLevel registry = ExtensionRegistry() pool_scope = Scope(level=ScopeLevel.POOL) @@ -332,7 +332,7 @@ async def test_resolve_resource_skill_reference_traversal_blocked(tmp_path: Any) """Path traversal in reference path is blocked by ResolvedSkillURI.parse().""" from upathtools import UPath - from agentpool.skills.exceptions import SecurityError + from wolfharness.skills.exceptions import SecurityError skill_dir = tmp_path / "my-skill" skill_dir.mkdir() @@ -443,10 +443,10 @@ async def test_resolve_resource_text_truncation() -> None: async def test_extract_user_prompt_with_binary_resource() -> None: """Resource returns BlobResourceContent → result contains BinaryContent in XML sandwich.""" - from agentpool_server.opencode_server.converters import extract_user_prompt_from_parts - from agentpool_server.opencode_server.models import FilePartInput - from agentpool_server.opencode_server.models.common import TextSpan - from agentpool_server.opencode_server.models.parts import ResourceSource + from wolfharness_server.opencode_server.converters import extract_user_prompt_from_parts + from wolfharness_server.opencode_server.models import FilePartInput + from wolfharness_server.opencode_server.models.common import TextSpan + from wolfharness_server.opencode_server.models.parts import ResourceSource blob_data = base64.b64encode(b"img").decode() cap = FakeResourceAccess( @@ -475,10 +475,10 @@ async def test_extract_user_prompt_with_binary_resource() -> None: async def test_extract_user_prompt_resource_no_agent() -> None: """agent=None → FilePartInput falls through to generic file handler.""" - from agentpool_server.opencode_server.converters import extract_user_prompt_from_parts - from agentpool_server.opencode_server.models import FilePartInput - from agentpool_server.opencode_server.models.common import TextSpan - from agentpool_server.opencode_server.models.parts import ResourceSource + from wolfharness_server.opencode_server.converters import extract_user_prompt_from_parts + from wolfharness_server.opencode_server.models import FilePartInput + from wolfharness_server.opencode_server.models.common import TextSpan + from wolfharness_server.opencode_server.models.parts import ResourceSource source = ResourceSource( text=TextSpan(value="@viking:doc.md", start=0, end=14), @@ -500,14 +500,14 @@ async def test_extract_user_prompt_resource_no_agent() -> None: async def test_extract_user_prompt_mixed_parts() -> None: """Text + resource + agent parts all processed.""" - from agentpool_server.opencode_server.converters import extract_user_prompt_from_parts - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.converters import extract_user_prompt_from_parts + from wolfharness_server.opencode_server.models import ( AgentPartInput, FilePartInput, TextPartInput, ) - from agentpool_server.opencode_server.models.common import TextSpan - from agentpool_server.opencode_server.models.parts import ResourceSource + from wolfharness_server.opencode_server.models.common import TextSpan + from wolfharness_server.opencode_server.models.parts import ResourceSource cap = FakeResourceAccess( read_result=[TextResourceContent(text="resource content", uri="viking://doc")] @@ -556,12 +556,12 @@ async def test_resolve_resource_timing_bug() -> None: Expected behavior after fix: ``_resolve_resource()`` should find the cap even when the registry hasn't registered it yet. """ - from agentpool import AgentPool, AgentsManifest, NativeAgentConfig - from agentpool_config.capabilities import GenericCapabilityConfig - from agentpool_server.opencode_server.converters import extract_user_prompt_from_parts - from agentpool_server.opencode_server.models import FilePartInput - from agentpool_server.opencode_server.models.common import TextSpan - from agentpool_server.opencode_server.models.parts import ResourceSource + from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig + from wolfharness_config.capabilities import GenericCapabilityConfig + from wolfharness_server.opencode_server.converters import extract_user_prompt_from_parts + from wolfharness_server.opencode_server.models import FilePartInput + from wolfharness_server.opencode_server.models.common import TextSpan + from wolfharness_server.opencode_server.models.parts import ResourceSource # Build a real agent config with a GenericCapabilityConfig pointing to # our test ResourceAccess capability. @@ -581,7 +581,7 @@ async def test_resolve_resource_timing_bug() -> None: # This populates _config_capabilities_built (eager build in __init__) # but does NOT register them in the ExtensionRegistry — that only # happens in get_agentlet(), which we deliberately do NOT call. - from agentpool.agents.native_agent import Agent + from wolfharness.agents.native_agent import Agent agent = Agent.from_config( agent_config, @@ -591,8 +591,8 @@ async def test_resolve_resource_timing_bug() -> None: assert agent._config_capabilities_built, "No config capabilities built" # Verify the cap is in _all_capabilities but NOT in the registry - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.resource_protocols import ResourceAccess + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.resource_protocols import ResourceAccess ra_caps = [c for c in agent._all_capabilities if isinstance(c, ResourceAccess)] assert len(ra_caps) >= 1, "No ResourceAccess cap in _all_capabilities" @@ -616,7 +616,7 @@ async def test_resolve_resource_timing_bug() -> None: # Verify ResourceCapability is NOT in the registry (it's a tool wrapper, # not a ResourceAccess provider). - from agentpool.capabilities.resource_capability import ResourceCapability + from wolfharness.capabilities.resource_capability import ResourceCapability assert not any(isinstance(c, ResourceCapability) for c in registry_caps), ( "ResourceCapability should not be in get_resource_access() results" @@ -666,9 +666,9 @@ def _make_pool_config( def _make_resource_part(uri: str, client_name: str = "test") -> Any: """Build a FilePartInput with a ResourceSource for @-mention testing.""" - from agentpool_server.opencode_server.models import FilePartInput - from agentpool_server.opencode_server.models.common import TextSpan - from agentpool_server.opencode_server.models.parts import ResourceSource + from wolfharness_server.opencode_server.models import FilePartInput + from wolfharness_server.opencode_server.models.common import TextSpan + from wolfharness_server.opencode_server.models.parts import ResourceSource return FilePartInput( mime="text/plain", @@ -693,8 +693,8 @@ async def test_e2e_at_mention_resolves_resource() -> None: """ import yamling - from agentpool import AgentPool, AgentsManifest - from agentpool_server.opencode_server.converters import extract_user_prompt_from_parts + from wolfharness import AgentPool, AgentsManifest + from wolfharness_server.opencode_server.converters import extract_user_prompt_from_parts config = _make_pool_config( cap_args={"read_text": "hello world", "read_uri": "test://doc.md"}, @@ -702,7 +702,7 @@ async def test_e2e_at_mention_resolves_resource() -> None: manifest = AgentsManifest.model_validate(yamling.load_yaml(config, verify_type=dict)) async with AgentPool(manifest) as pool: - from agentpool.agents.native_agent import Agent + from wolfharness.agents.native_agent import Agent agent = Agent.from_config( pool.agent_configs["test_agent"], @@ -721,8 +721,8 @@ async def test_e2e_at_mention_wrong_uri_returns_empty() -> None: """L2: @-mention with a URI that no cap can resolve → content dropped.""" import yamling - from agentpool import AgentPool, AgentsManifest - from agentpool_server.opencode_server.converters import extract_user_prompt_from_parts + from wolfharness import AgentPool, AgentsManifest + from wolfharness_server.opencode_server.converters import extract_user_prompt_from_parts config = _make_pool_config( cap_args={"read_text": "hello world", "read_uri": "test://doc.md"}, @@ -730,7 +730,7 @@ async def test_e2e_at_mention_wrong_uri_returns_empty() -> None: manifest = AgentsManifest.model_validate(yamling.load_yaml(config, verify_type=dict)) async with AgentPool(manifest) as pool: - from agentpool.agents.native_agent import Agent + from wolfharness.agents.native_agent import Agent agent = Agent.from_config( pool.agent_configs["test_agent"], @@ -756,9 +756,9 @@ async def test_e2e_resource_capability_excluded_from_registry() -> None: """ import yamling - from agentpool import AgentPool, AgentsManifest - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.resource_capability import ResourceCapability + from wolfharness import AgentPool, AgentsManifest + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.resource_capability import ResourceCapability config = _make_pool_config() manifest = AgentsManifest.model_validate(yamling.load_yaml(config, verify_type=dict)) @@ -786,9 +786,9 @@ async def test_e2e_multiple_agents_resource_isolation() -> None: """ import yamling - from agentpool import AgentPool, AgentsManifest - from agentpool.capabilities.extension_registry import Scope, ScopeLevel - from agentpool.capabilities.resource_protocols import ResourceAccess + from wolfharness import AgentPool, AgentsManifest + from wolfharness.capabilities.extension_registry import Scope, ScopeLevel + from wolfharness.capabilities.resource_protocols import ResourceAccess config = """\ agents: @@ -830,8 +830,8 @@ async def test_e2e_skill_resource_resolution() -> None: """L2: SkillResource cap → skill:// URI resolved via _resolve_resource.""" import yamling - from agentpool import AgentPool, AgentsManifest - from agentpool_server.opencode_server.converters import extract_user_prompt_from_parts + from wolfharness import AgentPool, AgentsManifest + from wolfharness_server.opencode_server.converters import extract_user_prompt_from_parts config = _make_pool_config( cap_type="tests.fixtures.test_resource_cap.TestSkillResourceCap", @@ -840,7 +840,7 @@ async def test_e2e_skill_resource_resolution() -> None: manifest = AgentsManifest.model_validate(yamling.load_yaml(config, verify_type=dict)) async with AgentPool(manifest) as pool: - from agentpool.agents.native_agent import Agent + from wolfharness.agents.native_agent import Agent agent = Agent.from_config( pool.agent_configs["test_agent"], @@ -891,7 +891,7 @@ async def test_scope_visibility_matrix( Scope hierarchy: POOL > AGENT > SESSION > TURN Query at a scope sees everything at that scope level and above (outer). """ - from agentpool.capabilities.extension_registry import ( + from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, @@ -933,7 +933,7 @@ async def test_agent_isolation_matrix( expected_found: bool, ) -> None: """Matrix: cap at AGENT scope for agent A → query for agent B → not found.""" - from agentpool.capabilities.extension_registry import ( + from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, @@ -972,8 +972,8 @@ async def test_config_cap_no_duplicate_tools_after_get_agentlet() -> None: The fix: register config-defined caps directly at AGENT scope inside _compile_agent_capabilities() but do NOT include them in the returned list. """ - from agentpool import AgentPool, AgentsManifest, NativeAgentConfig - from agentpool_config.capabilities import GenericCapabilityConfig + from wolfharness import AgentPool, AgentsManifest, NativeAgentConfig + from wolfharness_config.capabilities import GenericCapabilityConfig cap_config = GenericCapabilityConfig( type="tests.fixtures.test_resource_cap.TestToolAndResourceCap", @@ -1002,7 +1002,7 @@ async def test_config_cap_no_duplicate_tools_after_get_agentlet() -> None: # (built in __init__). If both lists have the same cap type, # get_agentlet() will assemble duplicate tools → pydantic-ai # raises "tool name conflicts" error. - from agentpool.capabilities.resource_protocols import ResourceAccess + from wolfharness.capabilities.resource_protocols import ResourceAccess extra_ra = [c for c in agent._extra_capabilities if isinstance(c, ResourceAccess)] assert len(extra_ra) == 0, ( diff --git a/tests/servers/opencode_server/test_restart_recovery.py b/tests/servers/opencode_server/test_restart_recovery.py index f16991e00..fb73398c8 100644 --- a/tests/servers/opencode_server/test_restart_recovery.py +++ b/tests/servers/opencode_server/test_restart_recovery.py @@ -8,11 +8,11 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.models import SessionIdleEvent -from agentpool_server.opencode_server.routes.session_routes import get_or_load_session -from agentpool_server.opencode_server.state import ServerState -from agentpool_storage.opencode_provider import helpers +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.models import SessionIdleEvent +from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session +from wolfharness_server.opencode_server.state import ServerState +from wolfharness_storage.opencode_provider import helpers pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_revert_cycle.py b/tests/servers/opencode_server/test_revert_cycle.py index 7c459703c..0562dc026 100644 --- a/tests/servers/opencode_server/test_revert_cycle.py +++ b/tests/servers/opencode_server/test_revert_cycle.py @@ -18,7 +18,7 @@ import pytest -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageRequest, @@ -29,7 +29,7 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( append_message_to_session, ) @@ -37,7 +37,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -735,7 +735,7 @@ async def test_stage_proceeds_after_cancel_timeout( await _add_messages_to_state(server_state, session_id, count=4) # Simulate a busy session: set current_run_id on the session state - from agentpool.orchestrator.session_pool import SessionState as PoolSessionState + from wolfharness.orchestrator.session_pool import SessionState as PoolSessionState busy_session = PoolSessionState( session_id=session_id, @@ -810,7 +810,7 @@ async def test_stale_run_handle_force_clears( await _add_messages_to_state(server_state, session_id, count=4) # Simulate stale run handle - from agentpool.orchestrator.session_pool import SessionState as PoolSessionState + from wolfharness.orchestrator.session_pool import SessionState as PoolSessionState stale_session = PoolSessionState( session_id=session_id, diff --git a/tests/servers/opencode_server/test_revert_stage_clear.py b/tests/servers/opencode_server/test_revert_stage_clear.py index 4b0f2eb28..2c934d8bc 100644 --- a/tests/servers/opencode_server/test_revert_stage_clear.py +++ b/tests/servers/opencode_server/test_revert_stage_clear.py @@ -16,9 +16,9 @@ import pytest -from agentpool.utils.streams import FileOpsTracker -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from wolfharness.utils.streams import FileOpsTracker +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -27,7 +27,7 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( append_message_to_session, ) @@ -38,7 +38,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState # ============================================================================= @@ -213,12 +213,12 @@ async def test_stage_clears_queues( await _add_messages_to_state(server_state, session_id, count=3) # Set up a mock session state with populated queues - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.orchestrator.session_controller import SessionState mock_session_state = SessionState(session_id=session_id, agent_name="test-agent") mock_session_state.prompt_queue.put_nowait("queued prompt 1") mock_session_state.prompt_queue.put_nowait("queued prompt 2") - from agentpool.lifecycle.types import Feedback + from wolfharness.lifecycle.types import Feedback mock_session_state.feedback_queue.put_nowait(Feedback(content="feedback 1", is_steer=False)) @@ -415,7 +415,7 @@ async def test_clear_while_busy_auto_cancels( assert stage_response.status_code == 200 # Simulate a busy session - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.orchestrator.session_controller import SessionState mock_session_state = SessionState(session_id=session_id, agent_name="test-agent") mock_session_state.current_run_id = "run-busy-123" @@ -455,8 +455,8 @@ async def test_clear_does_not_restore_cleared_queues( revert_message_id = messages[0].info.id # Set up a mock session state with populated queues - from agentpool.lifecycle.types import Feedback - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.lifecycle.types import Feedback + from wolfharness.orchestrator.session_controller import SessionState mock_session_state = SessionState(session_id=session_id, agent_name="test-agent") mock_session_state.prompt_queue.put_nowait("queued prompt") diff --git a/tests/servers/opencode_server/test_route_discovery.py b/tests/servers/opencode_server/test_route_discovery.py index f3a3a7af2..04c673750 100644 --- a/tests/servers/opencode_server/test_route_discovery.py +++ b/tests/servers/opencode_server/test_route_discovery.py @@ -15,15 +15,15 @@ from fastapi.testclient import TestClient import pytest -from agentpool.models.manifest import AgentsManifest -from agentpool.storage import StorageManager -from agentpool.utils.streams import FileOpsTracker -from agentpool.utils.todos import TodoTracker -from agentpool_server.opencode_server.dependencies import get_state -from agentpool_server.opencode_server.routes.agent_routes import router as agent_router -from agentpool_server.opencode_server.routes.config_routes import router as config_router -from agentpool_server.opencode_server.routes.global_routes import router as global_router -from agentpool_server.opencode_server.state import ServerState +from wolfharness.models.manifest import AgentsManifest +from wolfharness.storage import StorageManager +from wolfharness.utils.streams import FileOpsTracker +from wolfharness.utils.todos import TodoTracker +from wolfharness_server.opencode_server.dependencies import get_state +from wolfharness_server.opencode_server.routes.agent_routes import router as agent_router +from wolfharness_server.opencode_server.routes.config_routes import router as config_router +from wolfharness_server.opencode_server.routes.global_routes import router as global_router +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -41,7 +41,7 @@ @pytest.fixture def _server_state(tmp_path: Path) -> ServerState: """Build a ServerState with a mock agent for discovery route tests.""" - from agentpool_config.storage import MemoryStorageConfig, StorageConfig + from wolfharness_config.storage import MemoryStorageConfig, StorageConfig storage_manager = StorageManager(config=StorageConfig(providers=[MemoryStorageConfig()])) file_ops = FileOpsTracker() @@ -154,10 +154,10 @@ class TestRouteModuleImports: @pytest.mark.parametrize( "module_name", [ - "agentpool_server.opencode_server.routes.config_routes", - "agentpool_server.opencode_server.routes.agent_routes", - "agentpool_server.opencode_server.routes.global_routes", - "agentpool_server.opencode_server.routes.pty_routes", + "wolfharness_server.opencode_server.routes.config_routes", + "wolfharness_server.opencode_server.routes.agent_routes", + "wolfharness_server.opencode_server.routes.global_routes", + "wolfharness_server.opencode_server.routes.pty_routes", ], ) def test_module_imports_cleanly(self, module_name: str) -> None: @@ -168,10 +168,10 @@ def test_module_imports_cleanly(self, module_name: str) -> None: @pytest.mark.parametrize( "module_name", [ - "agentpool_server.opencode_server.routes.config_routes", - "agentpool_server.opencode_server.routes.agent_routes", - "agentpool_server.opencode_server.routes.global_routes", - "agentpool_server.opencode_server.routes.pty_routes", + "wolfharness_server.opencode_server.routes.config_routes", + "wolfharness_server.opencode_server.routes.agent_routes", + "wolfharness_server.opencode_server.routes.global_routes", + "wolfharness_server.opencode_server.routes.pty_routes", ], ) def test_no_merge_conflict_markers(self, module_name: str) -> None: diff --git a/tests/servers/opencode_server/test_run_error_session_status.py b/tests/servers/opencode_server/test_run_error_session_status.py index c74123d89..3017f5e3f 100644 --- a/tests/servers/opencode_server/test_run_error_session_status.py +++ b/tests/servers/opencode_server/test_run_error_session_status.py @@ -21,19 +21,19 @@ import pytest -from agentpool.agents.events.events import RunErrorEvent -from agentpool.orchestrator.core import EventEnvelope -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.agents.events.events import RunErrorEvent +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageAbortedError, MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) @@ -138,7 +138,7 @@ def _patch_set_session_status() -> AsyncMock: from unittest.mock import patch return patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ) @@ -148,7 +148,7 @@ def _patch_append_message() -> AsyncMock: from unittest.mock import patch return patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) diff --git a/tests/servers/opencode_server/test_server_lifecycle.py b/tests/servers/opencode_server/test_server_lifecycle.py index 359f681a3..d04d914fa 100644 --- a/tests/servers/opencode_server/test_server_lifecycle.py +++ b/tests/servers/opencode_server/test_server_lifecycle.py @@ -14,7 +14,7 @@ import pytest -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -131,8 +131,8 @@ async def test_bootstrap_preserves_pool_access(shared_agent: Mock, mock_pool: Mo After __post_init__, state.pool and state.storage must return the cached references without going through the shared agent. """ - from agentpool.storage import StorageManager - from agentpool_config.storage import MemoryStorageConfig, StorageConfig + from wolfharness.storage import StorageManager + from wolfharness_config.storage import MemoryStorageConfig, StorageConfig storage_manager = StorageManager(config=StorageConfig(providers=[MemoryStorageConfig()])) shared_agent.storage = storage_manager diff --git a/tests/servers/opencode_server/test_session_cache_404.py b/tests/servers/opencode_server/test_session_cache_404.py index fbfdb684b..11e662c2f 100644 --- a/tests/servers/opencode_server/test_session_cache_404.py +++ b/tests/servers/opencode_server/test_session_cache_404.py @@ -23,18 +23,18 @@ import pytest -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessageWithParts, Session, TextPart, TimeCreatedUpdated, UserMessage, ) -from agentpool_server.opencode_server.routes.session_routes import get_or_load_session +from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session if TYPE_CHECKING: - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def _make_session(session_id: str, parent_id: str | None = None) -> Session: @@ -52,7 +52,7 @@ def _make_session(session_id: str, parent_id: str | None = None) -> Session: def _make_dummy_message(session_id: str) -> MessageWithParts: """Create a minimal message for populating session history.""" - from agentpool_server.opencode_server.models import TimeCreated + from wolfharness_server.opencode_server.models import TimeCreated return MessageWithParts( info=UserMessage( diff --git a/tests/servers/opencode_server/test_session_cwd_consistency.py b/tests/servers/opencode_server/test_session_cwd_consistency.py index 9b300947f..8494ff8ac 100644 --- a/tests/servers/opencode_server/test_session_cwd_consistency.py +++ b/tests/servers/opencode_server/test_session_cwd_consistency.py @@ -18,7 +18,7 @@ import pytest -from agentpool.sessions.models import SessionData +from wolfharness.sessions.models import SessionData pytestmark = pytest.mark.integration @@ -27,7 +27,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState class TestSessionCwdConsistency: diff --git a/tests/servers/opencode_server/test_session_history_loading.py b/tests/servers/opencode_server/test_session_history_loading.py index 673a53564..b2767ff3f 100644 --- a/tests/servers/opencode_server/test_session_history_loading.py +++ b/tests/servers/opencode_server/test_session_history_loading.py @@ -12,8 +12,8 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.routes.session_routes import get_or_load_session +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session pytestmark = pytest.mark.integration @@ -24,7 +24,7 @@ from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState class TestSessionHistoryLoading: @@ -133,11 +133,11 @@ async def test_cached_session_with_wrong_agent_session_gets_reloaded( response_b.json()["id"] # Add session A to cache - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Session, TimeCreatedUpdated, ) - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers now = int(datetime.now(UTC).timestamp() * 1000) session_a = Session( diff --git a/tests/servers/opencode_server/test_session_integration.py b/tests/servers/opencode_server/test_session_integration.py index 5c458852f..242030a9e 100644 --- a/tests/servers/opencode_server/test_session_integration.py +++ b/tests/servers/opencode_server/test_session_integration.py @@ -26,11 +26,11 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator -from agentpool.lifecycle.types import DeliveryMode -from agentpool.orchestrator.core import SessionPool -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider -from agentpool_server.opencode_server.state import ServerState +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.orchestrator.core import SessionPool +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -49,8 +49,8 @@ def _stream_empty(queue: asyncio.Queue[Any]) -> bool: @pytest.fixture def mock_agent_pool() -> Mock: """Create a mock AgentPool for SessionPool construction.""" - from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent - from agentpool.messaging.messages import ChatMessage + from wolfharness.agents.events import RunStartedEvent, StreamCompleteEvent + from wolfharness.messaging.messages import ChatMessage pool = Mock() pool.main_agent = Mock() @@ -170,7 +170,7 @@ class TestOpenCodeSessionPoolIntegrationExists: @pytest.mark.asyncio async def test_integration_class_importable(self) -> None: """The OpenCodeSessionPoolIntegration class should be importable.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -183,7 +183,7 @@ async def test_integration_initialization( server_state: ServerState, ) -> None: """Integration should accept SessionPool and ServerState.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -205,7 +205,7 @@ async def test_create_session_delegates_to_session_pool( server_state: ServerState, ) -> None: """Creating a session should delegate to SessionPool.create_session().""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -234,7 +234,7 @@ async def test_create_session_persists_to_store( mock_session_store: Mock, ) -> None: """Session creation should persist to the session store.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -261,7 +261,7 @@ async def test_create_session_broadcasts_created_event( server_state: ServerState, ) -> None: """Session creation should broadcast a session.created SSE event.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -300,7 +300,7 @@ async def test_route_message_creates_run_handle( server_state: ServerState, ) -> None: """Routing a message should create a RunHandle via receive_request().""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -330,7 +330,7 @@ async def test_route_message_with_when_idle_priority_queues( server_state: ServerState, ) -> None: """Routing with 'when_idle' priority should queue when busy.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -370,7 +370,7 @@ async def test_route_message_with_asap_priority_injects( server_state: ServerState, ) -> None: """Routing with 'asap' priority should inject into active turn.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -400,7 +400,7 @@ async def test_route_message_publishes_run_started_event( server_state: ServerState, ) -> None: """Routing a message should publish RunStartedEvent to EventBus.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -448,7 +448,7 @@ async def test_create_session_starts_event_consumer( server_state: ServerState, ) -> None: """Creating a session should start the event consumer.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -472,7 +472,7 @@ async def test_status_broadcasts_busy_on_run_start( server_state: ServerState, ) -> None: """Run start should broadcast session.status with type 'busy'.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -520,7 +520,7 @@ async def test_status_broadcasts_idle_on_run_complete( server_state: ServerState, ) -> None: """Run completion should broadcast session.status with type 'idle'.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -574,7 +574,7 @@ async def test_abort_session_cancels_active_run( server_state: ServerState, ) -> None: """Aborting a session should cancel the active run.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -647,7 +647,7 @@ async def test_abort_session_broadcasts_error_event( server_state: ServerState, ) -> None: """Abort should broadcast a session.error SSE event.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -687,7 +687,7 @@ async def test_abort_idle_session_is_noop( server_state: ServerState, ) -> None: """Aborting an idle session should be a no-op.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -715,7 +715,7 @@ async def test_fork_session_creates_child_with_parent( server_state: ServerState, ) -> None: """Forking a session should create a child with parent_session_id.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -749,7 +749,7 @@ async def test_fork_session_inherits_metadata( mock_session_store: Mock, ) -> None: """Forked session should inherit parent's metadata.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -794,7 +794,7 @@ async def test_fork_session_tracked_as_child( server_state: ServerState, ) -> None: """Forked session should be tracked as a child of parent.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -832,7 +832,7 @@ async def test_attach_input_provider_to_session( mock_input_provider: OpenCodeInputProvider, ) -> None: """Input provider should be attachable to a session.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -863,7 +863,7 @@ async def test_route_message_with_input_provider( mock_input_provider: OpenCodeInputProvider, ) -> None: """Routing a message should pass the input provider to the turn runner.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -904,7 +904,7 @@ async def test_concurrent_sessions_have_isolated_input_providers( providers on ``SessionState`` only and lets SessionController pass the correct one at run time. """ - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -924,7 +924,7 @@ async def test_concurrent_sessions_have_isolated_input_providers( ) # Create distinct input providers for each session - from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider + from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider provider_a = OpenCodeInputProvider( state=server_state, session_id="test-session-concurrent-a" @@ -968,7 +968,7 @@ async def test_subscribe_to_session_events( """Should be able to subscribe to session events and receive OpenCode events.""" import asyncio - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -1002,7 +1002,7 @@ async def test_event_conversion_in_subscription( server_state: ServerState, ) -> None: """Subscribed events should be converted to OpenCode SSE events.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -1017,7 +1017,7 @@ async def test_event_conversion_in_subscription( ) # Publish a RunErrorEvent which is converted to SessionErrorEvent by the adapter - from agentpool.agents.events import RunErrorEvent + from wolfharness.agents.events import RunErrorEvent await session_pool.event_bus.publish( "test-session-017", @@ -1052,7 +1052,7 @@ async def test_shutdown_closes_all_sessions( server_state: ServerState, ) -> None: """Shutting down integration should close all tracked sessions.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) @@ -1082,7 +1082,7 @@ async def test_get_session_status_returns_current_status( server_state: ServerState, ) -> None: """Should return the current status of a session.""" - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) diff --git a/tests/servers/opencode_server/test_session_lifecycle.py b/tests/servers/opencode_server/test_session_lifecycle.py index fa97a3aad..4422d4642 100644 --- a/tests/servers/opencode_server/test_session_lifecycle.py +++ b/tests/servers/opencode_server/test_session_lifecycle.py @@ -19,9 +19,9 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.models import Session -from agentpool_server.opencode_server.models.events import ( +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.models import Session +from wolfharness_server.opencode_server.models.events import ( SessionCreatedEvent, SessionIdleEvent, SessionStatusEvent, @@ -31,8 +31,8 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState from tests.servers.opencode_server.conftest import EventCapture + from wolfharness_server.opencode_server.state import ServerState class TestSessionCreatedEvent: @@ -304,8 +304,8 @@ async def test_list_sessions_uses_session_controller_when_available( """list_sessions delegates to SessionController when session_controller is set.""" from unittest.mock import Mock - from agentpool_server.opencode_server.models.common import TimeCreatedUpdated - from agentpool_server.opencode_server.models.session_info import SessionInfo + from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated + from wolfharness_server.opencode_server.models.session_info import SessionInfo # Pre-populate the session cache so the route does not need storage server_state.sessions["ses_ctrl_001"] = Session( @@ -424,7 +424,7 @@ async def test_abort_native_agent_calls_interrupt_not_cancel_run( abort handler must NOT call session_pool.cancel_run() again. A double cancel() kills the start() generator (issue #182). """ - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState response = await async_client.post("/session", json={"title": "Native Agent Session"}) session_id = response.json()["id"] @@ -466,7 +466,7 @@ async def test_abort_non_native_shared_agent_skips_interrupt( server_state: ServerState, ): """Aborting a non-native shared agent should cancel run but NOT interrupt shared agent.""" - from agentpool.orchestrator.core import SessionState + from wolfharness.orchestrator.core import SessionState response = await async_client.post("/session", json={"title": "Shared Agent Session"}) session_id = response.json()["id"] diff --git a/tests/servers/opencode_server/test_session_pool_integration.py b/tests/servers/opencode_server/test_session_pool_integration.py index 44feac661..645cfa4f5 100644 --- a/tests/servers/opencode_server/test_session_pool_integration.py +++ b/tests/servers/opencode_server/test_session_pool_integration.py @@ -11,14 +11,14 @@ import pytest -from agentpool.agents.events import SpawnSessionStart, StreamCompleteEvent -from agentpool.messaging import ChatMessage -from agentpool_server.opencode_server.models import ( +from wolfharness.agents.events import SpawnSessionStart, StreamCompleteEvent +from wolfharness.messaging import ChatMessage +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( TimeStart, ToolPart, ToolStateCompleted, @@ -37,7 +37,7 @@ def _make_assistant_msg(session_id: str = "parent-test") -> MessageWithParts: agent_name="lead-agent", model_id="test-model", parent_id="parent-user-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) @@ -52,7 +52,7 @@ async def test_update_parent_toolpart_via_session_pool_integration( This test verifies the fix for the bug where _update_parent_toolpart checked part.metadata instead of part.state.metadata. """ - from agentpool_server.opencode_server.session_pool_integration import ( + from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ) diff --git a/tests/servers/opencode_server/test_session_provisioning.py b/tests/servers/opencode_server/test_session_provisioning.py index debb412e5..9f63b9b9e 100644 --- a/tests/servers/opencode_server/test_session_provisioning.py +++ b/tests/servers/opencode_server/test_session_provisioning.py @@ -14,12 +14,12 @@ import pytest -from agentpool.agents.base_agent import BaseAgent -from agentpool.agents.native_agent.checkpoint import CheckpointData -from agentpool.lifecycle.types import DeliveryMode -from agentpool.sessions.models import PendingDeferredCall, SessionData -from agentpool_server.opencode_server.converters import session_data_to_opencode -from agentpool_server.opencode_server.models import ( +from wolfharness.agents.base_agent import BaseAgent +from wolfharness.agents.native_agent.checkpoint import CheckpointData +from wolfharness.lifecycle.types import DeliveryMode +from wolfharness.sessions.models import PendingDeferredCall, SessionData +from wolfharness_server.opencode_server.converters import session_data_to_opencode +from wolfharness_server.opencode_server.models import ( MessageWithParts, Session, SessionCreatedEvent, @@ -28,15 +28,15 @@ SessionUpdatedEvent, TimeCreatedUpdated, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( ToolPart, ToolStateRunning, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, ensure_session, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -464,7 +464,7 @@ async def test_ensure_session_checkpointed_restores_input_provider( with ( patch.object(mock_state, "broadcast_event", new=AsyncMock()), patch( - "agentpool_server.opencode_server.input_provider.OpenCodeInputProvider" + "wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider" ) as mock_prov_cls, ): mock_prov = MagicMock() @@ -497,7 +497,7 @@ async def test_ensure_session_checkpointed_without_store( with ( patch.object(mock_state, "broadcast_event", new=AsyncMock()), patch( - "agentpool_server.opencode_server.input_provider.OpenCodeInputProvider" + "wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider" ) as mock_prov_cls, ): mock_prov_cls.return_value = MagicMock() @@ -687,8 +687,8 @@ async def test_concurrent_calls_produce_one_session( mock_state_store_first.pool.storage.load_session = AsyncMock(return_value=None) with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data"), - patch("agentpool_server.opencode_server.input_provider.OpenCodeInputProvider"), + patch("wolfharness_server.opencode_server.converters.opencode_to_session_data"), + patch("wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider"), patch.object(mock_state_store_first, "broadcast_event", new=AsyncMock()), ): results = await asyncio.gather( @@ -845,9 +845,11 @@ async def test_store_miss_fallback_creates_and_persists( mock_state_store_first.pool.session_pool.sessions.store = mock_store with ( - patch("agentpool_server.opencode_server.converters.opencode_to_session_data") as mock_conv, patch( - "agentpool_server.opencode_server.input_provider.OpenCodeInputProvider" + "wolfharness_server.opencode_server.converters.opencode_to_session_data" + ) as mock_conv, + patch( + "wolfharness_server.opencode_server.input_provider.OpenCodeInputProvider" ) as mock_prov_cls, patch.object(mock_state_store_first, "broadcast_event", new=AsyncMock()), ): diff --git a/tests/servers/opencode_server/test_session_scoped_consumer.py b/tests/servers/opencode_server/test_session_scoped_consumer.py index add3f2331..856a73d69 100644 --- a/tests/servers/opencode_server/test_session_scoped_consumer.py +++ b/tests/servers/opencode_server/test_session_scoped_consumer.py @@ -8,20 +8,20 @@ import pytest -from agentpool.agents.events import RunStartedEvent, StreamCompleteEvent -from agentpool.agents.events.events import UserMessageInsertedEvent -from agentpool.messaging.messages import ChatMessage -from agentpool.orchestrator.core import SessionPool -from agentpool_server.opencode_server.models import ( +from wolfharness.agents.events import RunStartedEvent, StreamCompleteEvent +from wolfharness.agents.events.events import UserMessageInsertedEvent +from wolfharness.messaging.messages import ChatMessage +from wolfharness.orchestrator.core import SessionPool +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( OpenCodeSessionPoolIntegration, get_messages_for_session, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -30,7 +30,7 @@ @pytest.fixture def mock_agent_pool() -> Mock: """Create a mock AgentPool for SessionPool construction.""" - from agentpool.messaging.messages import ChatMessage + from wolfharness.messaging.messages import ChatMessage pool = Mock() pool.main_agent = Mock() @@ -353,7 +353,7 @@ async def test_consumer_handles_spawn_session_start( server_state: ServerState, ) -> None: """Consumer should handle SpawnSessionStart by creating child consumers.""" - from agentpool.agents.events import SpawnSessionStart + from wolfharness.agents.events import SpawnSessionStart integration = OpenCodeSessionPoolIntegration( session_pool=session_pool, diff --git a/tests/servers/opencode_server/test_session_storage_load.py b/tests/servers/opencode_server/test_session_storage_load.py index 8b68d0402..bb997cadb 100644 --- a/tests/servers/opencode_server/test_session_storage_load.py +++ b/tests/servers/opencode_server/test_session_storage_load.py @@ -13,9 +13,9 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.models import Session, SessionUpdatedEvent -from agentpool_server.opencode_server.routes.session_routes import get_or_load_session +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.models import Session, SessionUpdatedEvent +from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session pytestmark = pytest.mark.integration @@ -24,7 +24,7 @@ if TYPE_CHECKING: from pathlib import Path - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState @pytest.fixture @@ -50,7 +50,7 @@ def mock_state_and_broadcast( Returns a tuple of (state, broadcast_mock) so the test can inspect broadcast calls without fighting pyright's type narrowing on overridden methods. """ - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState agent = Mock() diff --git a/tests/servers/opencode_server/test_session_switch_input_provider.py b/tests/servers/opencode_server/test_session_switch_input_provider.py index 2680f338f..0530cd504 100644 --- a/tests/servers/opencode_server/test_session_switch_input_provider.py +++ b/tests/servers/opencode_server/test_session_switch_input_provider.py @@ -14,9 +14,9 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.input_provider import OpenCodeInputProvider -from agentpool_server.opencode_server.routes.session_routes import get_or_load_session +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.input_provider import OpenCodeInputProvider +from wolfharness_server.opencode_server.routes.session_routes import get_or_load_session pytestmark = pytest.mark.integration @@ -27,7 +27,7 @@ from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState class TestSessionSwitchInputProvider: diff --git a/tests/servers/opencode_server/test_session_title_fixes.py b/tests/servers/opencode_server/test_session_title_fixes.py index 38c54046e..15f947154 100644 --- a/tests/servers/opencode_server/test_session_title_fixes.py +++ b/tests/servers/opencode_server/test_session_title_fixes.py @@ -16,17 +16,21 @@ import anyio import pytest -from agentpool.sessions.models import SessionData -from agentpool.storage.manager import SessionMetadata, SessionMetadataGeneratedEvent, StorageManager -from agentpool_config.storage import MemoryStorageConfig, SQLStorageConfig, StorageConfig -from agentpool_server.opencode_server.converters import ( +from wolfharness.sessions.models import SessionData +from wolfharness.storage.manager import ( + SessionMetadata, + SessionMetadataGeneratedEvent, + StorageManager, +) +from wolfharness_config.storage import MemoryStorageConfig, SQLStorageConfig, StorageConfig +from wolfharness_server.opencode_server.converters import ( opencode_to_session_data, session_data_to_opencode, ) -from agentpool_server.opencode_server.models import Session -from agentpool_server.opencode_server.models.common import TimeCreatedUpdated -from agentpool_storage.memory_provider.provider import MemoryStorageProvider -from agentpool_storage.sql_provider.sql_provider import SQLModelProvider +from wolfharness_server.opencode_server.models import Session +from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness_storage.sql_provider.sql_provider import SQLModelProvider pytestmark = pytest.mark.integration @@ -414,8 +418,8 @@ class TestOpenCodeProviderTitleFix: async def test_save_session_creates_new_session_file(self, tmp_path: Path) -> None: """Verify save_session creates new session file when it doesn't exist.""" - from agentpool_config.storage import OpenCodeStorageConfig - from agentpool_storage.opencode_provider.provider import OpenCodeStorageProvider + from wolfharness_config.storage import OpenCodeStorageConfig + from wolfharness_storage.opencode_provider.provider import OpenCodeStorageProvider config = OpenCodeStorageConfig(path=str(tmp_path / "opencode_storage")) provider = OpenCodeStorageProvider(config) @@ -437,7 +441,7 @@ async def test_save_session_creates_new_session_file(self, tmp_path: Path) -> No assert session_file.exists(), f"Session file should be created at {session_file}" # Verify title was saved - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers oc_session = helpers.read_session(session_file) assert oc_session is not None @@ -448,8 +452,8 @@ async def test_save_session_creates_new_session_file(self, tmp_path: Path) -> No async def test_save_session_updates_existing_session(self, tmp_path: Path) -> None: """Verify save_session updates existing session file.""" - from agentpool_config.storage import OpenCodeStorageConfig - from agentpool_storage.opencode_provider.provider import OpenCodeStorageProvider + from wolfharness_config.storage import OpenCodeStorageConfig + from wolfharness_storage.opencode_provider.provider import OpenCodeStorageProvider config = OpenCodeStorageConfig(path=str(tmp_path / "opencode_storage")) provider = OpenCodeStorageProvider(config) @@ -474,7 +478,7 @@ async def test_save_session_updates_existing_session(self, tmp_path: Path) -> No # Verify title was updated session_file = provider.sessions_path / "test_project" / "update_session_001.json" - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers oc_session = helpers.read_session(session_file) assert oc_session is not None @@ -483,8 +487,8 @@ async def test_save_session_updates_existing_session(self, tmp_path: Path) -> No async def test_load_session_reads_title(self, tmp_path: Path) -> None: """Verify load_session reads title from session file.""" - from agentpool_config.storage import OpenCodeStorageConfig - from agentpool_storage.opencode_provider.provider import OpenCodeStorageProvider + from wolfharness_config.storage import OpenCodeStorageConfig + from wolfharness_storage.opencode_provider.provider import OpenCodeStorageProvider config = OpenCodeStorageConfig(path=str(tmp_path / "opencode_storage")) provider = OpenCodeStorageProvider(config) @@ -554,7 +558,7 @@ async def test_generate_title_core_resolves_variant_name(self) -> None: manager._model_variants = {"my-variant": mock_variant} # Patch the Agent import inside the method body - with patch("agentpool.Agent") as mock_agent_cls: + with patch("wolfharness.Agent") as mock_agent_cls: mock_agent = MagicMock() mock_result = MagicMock() mock_result.data = SessionMetadata( diff --git a/tests/servers/opencode_server/test_session_title_integration.py b/tests/servers/opencode_server/test_session_title_integration.py index 650e819d5..311fa6b86 100644 --- a/tests/servers/opencode_server/test_session_title_integration.py +++ b/tests/servers/opencode_server/test_session_title_integration.py @@ -16,13 +16,13 @@ import anyio import pytest -from agentpool.sessions.models import SessionData -from agentpool_server.opencode_server.converters import ( +from wolfharness.sessions.models import SessionData +from wolfharness_server.opencode_server.converters import ( opencode_to_session_data, session_data_to_opencode, ) -from agentpool_server.opencode_server.models import Session -from agentpool_server.opencode_server.models.common import TimeCreatedUpdated +from wolfharness_server.opencode_server.models import Session +from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated pytestmark = pytest.mark.integration @@ -31,7 +31,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState class TestSessionTitlePersistence: @@ -226,7 +226,7 @@ async def test_log_session_triggers_title_generation( Note: This test mocks the LLM call to avoid external dependencies. """ - from agentpool.storage.manager import SessionMetadata, StorageManager + from wolfharness.storage.manager import SessionMetadata, StorageManager session_id = "test_title_gen_123" diff --git a/tests/servers/opencode_server/test_share_revert.py b/tests/servers/opencode_server/test_share_revert.py index 2b13a16d0..6b60ac001 100644 --- a/tests/servers/opencode_server/test_share_revert.py +++ b/tests/servers/opencode_server/test_share_revert.py @@ -11,8 +11,8 @@ import pytest -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -21,7 +21,7 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.session_pool_integration import ( append_message_to_session, ) @@ -32,7 +32,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState # ============================================================================= @@ -138,7 +138,7 @@ async def test_share_session_copies_all_messages( mock_sharer.share_conversation = AsyncMock(return_value=mock_result) with patch( - "agentpool_server.opencode_server.routes.session_routes.OpenCodeSharer", + "wolfharness_server.opencode_server.routes.session_routes.OpenCodeSharer", return_value=mock_sharer, ): share_response = await async_client.post(f"/session/{session_id}/share") @@ -172,7 +172,7 @@ async def test_share_session_with_message_limit( mock_sharer.share_conversation = AsyncMock(return_value=mock_result) with patch( - "agentpool_server.opencode_server.routes.session_routes.OpenCodeSharer", + "wolfharness_server.opencode_server.routes.session_routes.OpenCodeSharer", return_value=mock_sharer, ): share_response = await async_client.post(f"/session/{session_id}/share?num_messages=3") @@ -226,7 +226,7 @@ async def test_share_session_uses_message_history_api( mock_sharer.share_conversation = AsyncMock(return_value=mock_result) with patch( - "agentpool_server.opencode_server.routes.session_routes.OpenCodeSharer", + "wolfharness_server.opencode_server.routes.session_routes.OpenCodeSharer", return_value=mock_sharer, ): share_response = await async_client.post(f"/session/{session_id}/share") @@ -516,7 +516,7 @@ async def test_share_then_revert_in_same_session( # Debug: check what get_messages_for_session returns with patch( - "agentpool_server.opencode_server.routes.session_routes.OpenCodeSharer", + "wolfharness_server.opencode_server.routes.session_routes.OpenCodeSharer", return_value=mock_sharer, ): share_response = await async_client.post(f"/session/{session_id}/share") diff --git a/tests/servers/opencode_server/test_shell.py b/tests/servers/opencode_server/test_shell.py index 5e60f9c5a..378a9e2d5 100644 --- a/tests/servers/opencode_server/test_shell.py +++ b/tests/servers/opencode_server/test_shell.py @@ -13,8 +13,8 @@ import pytest -from agentpool_server.opencode_server.models import ShellRequest -from agentpool_server.opencode_server.routes.session_routes import run_shell_command +from wolfharness_server.opencode_server.models import ShellRequest +from wolfharness_server.opencode_server.routes.session_routes import run_shell_command pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_skill_autocomplete.py b/tests/servers/opencode_server/test_skill_autocomplete.py index e3ab55915..dbdf80a6a 100644 --- a/tests/servers/opencode_server/test_skill_autocomplete.py +++ b/tests/servers/opencode_server/test_skill_autocomplete.py @@ -14,14 +14,14 @@ import pytest from upathtools import UPath -from agentpool.skills.command import SkillCommand -from agentpool.skills.skill import Skill +from wolfharness.skills.command import SkillCommand +from wolfharness.skills.skill import Skill if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.asyncio @@ -109,7 +109,7 @@ async def test_command_endpoint_skill_provider_fallback( server_state.skill_bridge = None # Create skill_resolver mock - from agentpool.capabilities.resource_protocols import SkillEntry + from wolfharness.capabilities.resource_protocols import SkillEntry mock_entry = SkillEntry( name="provider-skill", diff --git a/tests/servers/opencode_server/test_skill_command_execution.py b/tests/servers/opencode_server/test_skill_command_execution.py index eefe9da02..639284de6 100644 --- a/tests/servers/opencode_server/test_skill_command_execution.py +++ b/tests/servers/opencode_server/test_skill_command_execution.py @@ -7,7 +7,7 @@ import pytest -from agentpool_server.opencode_server.routes.session_routes import _process_skill_template +from wolfharness_server.opencode_server.routes.session_routes import _process_skill_template pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_skill_command_path.py b/tests/servers/opencode_server/test_skill_command_path.py index 66350b03a..d1071beed 100644 --- a/tests/servers/opencode_server/test_skill_command_path.py +++ b/tests/servers/opencode_server/test_skill_command_path.py @@ -25,14 +25,14 @@ import pytest from slashed import Command as SlashedCommand, CommandContext -from agentpool_server.opencode_server.event_processor import ( +from wolfharness_server.opencode_server.event_processor import ( OpenCodeUserMessageMeta, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( TextPart, UserMessage, ) -from agentpool_server.opencode_server.routes.session_routes import ( +from wolfharness_server.opencode_server.routes.session_routes import ( _CommandOutputCapture, _DiscardOutputWriter, ) @@ -41,7 +41,7 @@ if TYPE_CHECKING: from httpx import AsyncClient as HttpxAsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = [pytest.mark.asyncio, pytest.mark.integration] diff --git a/tests/servers/opencode_server/test_soft_hide_revert.py b/tests/servers/opencode_server/test_soft_hide_revert.py index df6019827..27a230efa 100644 --- a/tests/servers/opencode_server/test_soft_hide_revert.py +++ b/tests/servers/opencode_server/test_soft_hide_revert.py @@ -14,9 +14,9 @@ import pytest -from agentpool.messaging.messages import ChatMessage -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from wolfharness.messaging.messages import ChatMessage +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -25,13 +25,13 @@ TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.models.common import TimeCreatedUpdated -from agentpool_server.opencode_server.models.session import Session, SessionRevert -from agentpool_server.opencode_server.opencode_message_bridge import ( +from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated +from wolfharness_server.opencode_server.models.session import Session, SessionRevert +from wolfharness_server.opencode_server.opencode_message_bridge import ( _apply_revert_filter, get_messages_for_session, ) -from agentpool_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.unit @@ -380,7 +380,7 @@ async def test_revert_filter_with_non_empty_session_pool( state.agent = Mock() state.agent.name = "test-agent" state.agent.model_name = None - state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) state.working_dir = "/tmp" pool_mock = Mock() @@ -432,7 +432,7 @@ async def test_session_pool_with_no_revert_returns_all( state.agent = Mock() state.agent.name = "test-agent" state.agent.model_name = None - state.resolve_default_model_info = Mock(return_value=("default", "agentpool")) + state.resolve_default_model_info = Mock(return_value=("default", "wolfharness")) state.working_dir = "/tmp" pool_mock = Mock() diff --git a/tests/servers/opencode_server/test_sse_compliance.py b/tests/servers/opencode_server/test_sse_compliance.py index 2201b0c73..dab209e0c 100644 --- a/tests/servers/opencode_server/test_sse_compliance.py +++ b/tests/servers/opencode_server/test_sse_compliance.py @@ -20,13 +20,13 @@ import pytest -from agentpool_server.opencode_server.models import GlobalEvent -from agentpool_server.opencode_server.models.common import ( +from wolfharness_server.opencode_server.models import GlobalEvent +from wolfharness_server.opencode_server.models.common import ( FileDiff, ModelRef, TimeCreated, ) -from agentpool_server.opencode_server.models.events import ( +from wolfharness_server.opencode_server.models.events import ( CommandExecutedEvent, MessageUpdatedEvent, PartDeltaEvent, @@ -42,24 +42,24 @@ TodoUpdatedEvent, TuiSessionSelectEvent, ) -from agentpool_server.opencode_server.models.message import UserMessage -from agentpool_server.opencode_server.models.parts import TextPart -from agentpool_server.opencode_server.models.session import ( +from wolfharness_server.opencode_server.models.message import UserMessage +from wolfharness_server.opencode_server.models.parts import TextPart +from wolfharness_server.opencode_server.models.session import ( Session, TimeCreatedUpdated as SessionTimeCreatedUpdated, ) -from agentpool_server.opencode_server.routes.global_routes import ( +from wolfharness_server.opencode_server.routes.global_routes import ( GlobalEventFactory, _event_generator, _extract_session_id, _serialize_event, ) -from agentpool_server.opencode_server.routes.routing import tui_event_filter +from wolfharness_server.opencode_server.routes.routing import tui_event_filter if TYPE_CHECKING: - from agentpool_server.opencode_server.models.events import Event - from agentpool_server.opencode_server.models.parts import Part + from wolfharness_server.opencode_server.models.events import Event + from wolfharness_server.opencode_server.models.parts import Part # ============================================================================= @@ -111,7 +111,7 @@ async def unsubscribe(self, session_id: str, queue: asyncio.Queue[Any]) -> None: queue.shutdown() async def publish(self, session_id: str, event: Any) -> None: - from agentpool.orchestrator.core import EventEnvelope + from wolfharness.orchestrator.core import EventEnvelope envelope = EventEnvelope(source_session_id=session_id, event=event) for subscriber_sid, subscribers in self._subscribers.items(): @@ -159,7 +159,7 @@ def __init__(self, working_dir: str = "/tmp/test_wd") -> None: def get_event_factory(self) -> GlobalEventFactory: if self._event_factory is None: - from agentpool_storage.opencode_provider import helpers + from wolfharness_storage.opencode_provider import helpers directory = self.working_dir self._event_factory = GlobalEventFactory( diff --git a/tests/servers/opencode_server/test_sse_headers.py b/tests/servers/opencode_server/test_sse_headers.py index 4e42b5e14..36794b1d7 100644 --- a/tests/servers/opencode_server/test_sse_headers.py +++ b/tests/servers/opencode_server/test_sse_headers.py @@ -11,7 +11,7 @@ import pytest -from agentpool_server.opencode_server.routes.global_routes import get_events, get_global_events +from wolfharness_server.opencode_server.routes.global_routes import get_events, get_global_events pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_step_usage.py b/tests/servers/opencode_server/test_step_usage.py index 4f5e38733..1b159b2fd 100644 --- a/tests/servers/opencode_server/test_step_usage.py +++ b/tests/servers/opencode_server/test_step_usage.py @@ -14,28 +14,28 @@ from pydantic_ai.usage import RunUsage import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent as AgentPoolPartDeltaEvent, PartStartEvent, StepUsageEvent, StreamCompleteEvent, ) -from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, PartDeltaEvent, PartUpdatedEvent, ) -from agentpool_server.opencode_server.models.parts import ( +from wolfharness_server.opencode_server.models.parts import ( StepFinishPart, TextPart, ) -from agentpool_server.opencode_server.stream_adapter import OpenCodeStreamAdapter +from wolfharness_server.opencode_server.stream_adapter import OpenCodeStreamAdapter pytestmark = pytest.mark.integration @@ -57,7 +57,7 @@ def adapter_context() -> EventProcessorContext: time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ) @@ -216,7 +216,7 @@ async def test_final_step_finish_still_emitted( time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ), @@ -400,7 +400,7 @@ async def test_step_finish_emitted_flag_edge_cases( time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ), @@ -428,7 +428,7 @@ async def test_step_finish_emitted_flag_edge_cases( time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ), @@ -471,7 +471,7 @@ async def test_step_finish_emitted_flag_edge_cases( time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ), @@ -532,7 +532,7 @@ async def test_subagent_session_id_does_not_suppress_parent_finalize( a *child* context (different session_id), then verifying the parent adapter's ``finalize()`` still emits its own ``StepFinishPart``. """ - from agentpool_server.opencode_server.event_processor import EventProcessor + from wolfharness_server.opencode_server.event_processor import EventProcessor # --- Create parent adapter --- adapter = _make_stream_adapter(adapter_context) @@ -547,7 +547,7 @@ async def test_subagent_session_id_does_not_suppress_parent_finalize( time=MessageTime(created=0), agent_name="test-agent", model_id="test-model", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), parent_id="msg-000", ) diff --git a/tests/servers/opencode_server/test_stream_adapter_event_feed.py b/tests/servers/opencode_server/test_stream_adapter_event_feed.py index 01b0efb39..0e7ba2880 100644 --- a/tests/servers/opencode_server/test_stream_adapter_event_feed.py +++ b/tests/servers/opencode_server/test_stream_adapter_event_feed.py @@ -14,13 +14,14 @@ from pydantic_ai import RequestUsage import pytest -from agentpool.agents.events import StreamCompleteEvent -from agentpool.lifecycle import RunOutcome, RunState -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventBus -from agentpool.utils import identifiers as identifier -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.lifecycle import RunOutcome, RunState +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventBus +from wolfharness.utils import identifiers as identifier +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageRequest, @@ -31,12 +32,11 @@ TimeCreatedUpdated, UserMessage, ) -from agentpool_server.opencode_server.models.message import MessageWithParts -from agentpool_server.opencode_server.models.parts import StepFinishPart -from agentpool_server.opencode_server.session_pool_integration import get_messages_for_session -from agentpool_server.opencode_server.state import ServerState -from agentpool_server.opencode_server.stream_adapter import OpenCodeStreamAdapter -from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness_server.opencode_server.models.message import MessageWithParts +from wolfharness_server.opencode_server.models.parts import StepFinishPart +from wolfharness_server.opencode_server.session_pool_integration import get_messages_for_session +from wolfharness_server.opencode_server.state import ServerState +from wolfharness_server.opencode_server.stream_adapter import OpenCodeStreamAdapter pytestmark = pytest.mark.integration @@ -45,7 +45,7 @@ def _setup_session(state: ServerState, session_id: str) -> None: """Set up session state manually.""" now = now_ms() - from agentpool_server.opencode_server.models import Session + from wolfharness_server.opencode_server.models import Session session = Session( id=session_id, diff --git a/tests/servers/opencode_server/test_subagent_integration.py b/tests/servers/opencode_server/test_subagent_integration.py index 28749261c..feb1ab7ca 100644 --- a/tests/servers/opencode_server/test_subagent_integration.py +++ b/tests/servers/opencode_server/test_subagent_integration.py @@ -16,32 +16,32 @@ from fastapi import FastAPI import pytest -from agentpool.agents.events import StreamCompleteEvent, SubAgentEvent -from agentpool.messaging import ChatMessage -from agentpool_server.opencode_server.dependencies import get_state -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.agents.events import StreamCompleteEvent, SubAgentEvent +from wolfharness.messaging import ChatMessage +from wolfharness_server.opencode_server.dependencies import get_state +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.routes import file_router, message_router, session_router -from agentpool_server.opencode_server.session_pool_integration import ( +from wolfharness_server.opencode_server.routes import file_router, message_router, session_router +from wolfharness_server.opencode_server.session_pool_integration import ( ensure_session, get_messages_for_session, ) -from agentpool_server.opencode_server.stream_adapter import OpenCodeStreamAdapter -from agentpool_toolsets.builtin.subagent_tools import SubagentTools +from wolfharness_server.opencode_server.stream_adapter import OpenCodeStreamAdapter +from wolfharness_toolsets.builtin.subagent_tools import SubagentTools pytestmark = pytest.mark.integration if TYPE_CHECKING: - from agentpool_server.opencode_server.event_processor import EventProcessor - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.event_processor import EventProcessor + from wolfharness_server.opencode_server.state import ServerState # ============================================================================= @@ -62,7 +62,7 @@ def _make_parent_ctx( agent_name="lead-agent", model_id="test-model", parent_id="parent-user-1", - provider_id="agentpool", + provider_id="wolfharness", path=MessagePath(cwd="/tmp", root="/tmp"), ) return EventProcessorContext( @@ -111,7 +111,7 @@ async def test_background_task_inject_prompt_wakes_lead_agent( """ import inspect - from agentpool.agents.base_agent import BaseAgent + from wolfharness.agents.base_agent import BaseAgent source = inspect.getsource(BaseAgent.inject_prompt) @@ -292,7 +292,7 @@ async def test_subagent_event_without_child_session_id(server_state: ServerState # Setup session_id = "parent-session" - from agentpool_server.opencode_server.models import MessagePath, MessageTime + from wolfharness_server.opencode_server.models import MessagePath, MessageTime assistant_msg = MessageWithParts.assistant( message_id="msg-1", @@ -326,7 +326,7 @@ async def event_stream(): # Run process_stream with ensure_session patched with patch( - "agentpool_server.opencode_server.session_pool_integration.ensure_session" + "wolfharness_server.opencode_server.session_pool_integration.ensure_session" ) as mock_ensure: async for _ in adapter.process_stream(event_stream()): pass @@ -404,7 +404,7 @@ async def test_backward_compatibility_non_subagent( # Mock normal stream without subagent events async def stream_generator(*args, **kwargs): - from agentpool.agents.events import PartDeltaEvent + from wolfharness.agents.events import PartDeltaEvent yield PartDeltaEvent.text(index=0, content="Normal response") yield StreamCompleteEvent( diff --git a/tests/servers/opencode_server/test_summarize_session.py b/tests/servers/opencode_server/test_summarize_session.py index c2bed4ece..24e75a6da 100644 --- a/tests/servers/opencode_server/test_summarize_session.py +++ b/tests/servers/opencode_server/test_summarize_session.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: from httpx import AsyncClient - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState pytestmark = [pytest.mark.asyncio, pytest.mark.integration] @@ -26,9 +26,9 @@ async def test_summarize_uses_session_pool( """Summarize always uses SessionPool.run_stream (no feature flag branching).""" from pydantic_ai import RequestUsage, TextPart, TextPartDelta - from agentpool.agents.events import PartDeltaEvent, PartStartEvent, StreamCompleteEvent - from agentpool.messaging.messages import ChatMessage - from agentpool_server.opencode_server.models import ( + from wolfharness.agents.events import PartDeltaEvent, PartStartEvent, StreamCompleteEvent + from wolfharness.messaging.messages import ChatMessage + from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -47,7 +47,7 @@ async def test_summarize_uses_session_pool( session_id=session_id, parent_id="", model_id="default", - provider_id="agentpool", + provider_id="wolfharness", mode="ask", agent="test-agent", path=MessagePath(cwd=server_state.working_dir, root=server_state.working_dir), @@ -81,11 +81,11 @@ async def mock_run_stream(*args: object, **kwargs: object): # Mock compact_conversation and get_messages_for_session with ( patch( - "agentpool.messaging.compaction.compact_conversation", + "wolfharness.messaging.compaction.compact_conversation", new=AsyncMock(), ), patch( - "agentpool_server.opencode_server.routes.session_routes.get_messages_for_session", + "wolfharness_server.opencode_server.routes.session_routes.get_messages_for_session", return_value=existing_messages, ), ): @@ -107,9 +107,9 @@ async def test_summarize_routes_through_session_pool( """SessionPool is the default execution path for summarization.""" from pydantic_ai import RequestUsage, TextPart, TextPartDelta - from agentpool.agents.events import PartDeltaEvent, PartStartEvent, StreamCompleteEvent - from agentpool.messaging.messages import ChatMessage - from agentpool_server.opencode_server.models import ( + from wolfharness.agents.events import PartDeltaEvent, PartStartEvent, StreamCompleteEvent + from wolfharness.messaging.messages import ChatMessage + from wolfharness_server.opencode_server.models import ( AssistantMessage, MessagePath, MessageTime, @@ -128,7 +128,7 @@ async def test_summarize_routes_through_session_pool( session_id=session_id, parent_id="", model_id="default", - provider_id="agentpool", + provider_id="wolfharness", mode="ask", agent="test-agent", path=MessagePath(cwd=server_state.working_dir, root=server_state.working_dir), @@ -159,11 +159,11 @@ async def test_summarize_routes_through_session_pool( # Mock compact_conversation and get_messages_for_session with ( patch( - "agentpool.messaging.compaction.compact_conversation", + "wolfharness.messaging.compaction.compact_conversation", new=AsyncMock(), ), patch( - "agentpool_server.opencode_server.routes.session_routes.get_messages_for_session", + "wolfharness_server.opencode_server.routes.session_routes.get_messages_for_session", return_value=existing_messages, ), ): diff --git a/tests/servers/opencode_server/test_title_management.py b/tests/servers/opencode_server/test_title_management.py index 04d850c13..200707221 100644 --- a/tests/servers/opencode_server/test_title_management.py +++ b/tests/servers/opencode_server/test_title_management.py @@ -23,22 +23,26 @@ import pytest -from agentpool.storage.manager import SessionMetadata, SessionMetadataGeneratedEvent, StorageManager -from agentpool.utils.time_utils import now_ms -from agentpool_server.opencode_server.models import ( +from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness.storage.manager import ( + SessionMetadata, + SessionMetadataGeneratedEvent, + StorageManager, +) +from wolfharness.utils.time_utils import now_ms +from wolfharness_server.opencode_server.models import ( MessageWithParts, Session, TextPartInput, TimeCreatedUpdated, UserMessage, ) -from agentpool_server.opencode_server.models.message import MessageRequest, TimeCreated -from agentpool_server.opencode_server.routes.message_routes import ( +from wolfharness_server.opencode_server.models.message import MessageRequest, TimeCreated +from wolfharness_server.opencode_server.routes.message_routes import ( _maybe_generate_title, ) -from agentpool_server.opencode_server.session_pool_integration import get_messages_for_session -from agentpool_server.opencode_server.state import ServerState -from tests.servers.opencode_server.conftest import run_message_phases +from wolfharness_server.opencode_server.session_pool_integration import get_messages_for_session +from wolfharness_server.opencode_server.state import ServerState pytestmark = pytest.mark.integration @@ -51,7 +55,7 @@ def _make_state(tmp_path: Any) -> ServerState: """Build a minimal ServerState with mocked heavy components.""" - from agentpool_config.storage import MemoryStorageConfig, StorageConfig + from wolfharness_config.storage import MemoryStorageConfig, StorageConfig agent = Mock() agent.name = "test-agent" @@ -192,15 +196,15 @@ async def slow_maybe_generate_title( with ( patch( - "agentpool_server.opencode_server.routes.message_routes._maybe_generate_title", + "wolfharness_server.opencode_server.routes.message_routes._maybe_generate_title", slow_maybe_generate_title, ), patch( - "agentpool_server.opencode_server.routes.message_routes.extract_user_prompt_from_parts", + "wolfharness_server.opencode_server.routes.message_routes.extract_user_prompt_from_parts", new=AsyncMock(return_value=["hello"]), ), patch( - "agentpool_server.opencode_server.routes.message_routes.OpenCodeStreamAdapter" + "wolfharness_server.opencode_server.routes.message_routes.OpenCodeStreamAdapter" ) as mock_adapter_class, ): mock_adapter_instance = mock_adapter_class.return_value @@ -257,11 +261,11 @@ async def slow_core( with ( patch.dict(os.environ, {}, clear=False), patch( - "agentpool_server.opencode_server.routes.message_routes.extract_user_prompt_from_parts", + "wolfharness_server.opencode_server.routes.message_routes.extract_user_prompt_from_parts", new=AsyncMock(return_value=["hello"]), ), patch( - "agentpool_server.opencode_server.routes.message_routes.OpenCodeStreamAdapter" + "wolfharness_server.opencode_server.routes.message_routes.OpenCodeStreamAdapter" ) as mock_adapter_class, ): os.environ.pop("PYTEST_CURRENT_TEST", None) diff --git a/tests/servers/opencode_server/test_todo_utils.py b/tests/servers/opencode_server/test_todo_utils.py index 2b5ea140e..a8c968c0c 100644 --- a/tests/servers/opencode_server/test_todo_utils.py +++ b/tests/servers/opencode_server/test_todo_utils.py @@ -4,9 +4,9 @@ import pytest -from agentpool.utils.todos import TodoTracker -from agentpool_server.opencode_server.models.session import Todo -from agentpool_server.opencode_server.todo_utils import build_opencode_todos +from wolfharness.utils.todos import TodoTracker +from wolfharness_server.opencode_server.models.session import Todo +from wolfharness_server.opencode_server.todo_utils import build_opencode_todos pytestmark = pytest.mark.integration diff --git a/tests/servers/opencode_server/test_tui_routing.py b/tests/servers/opencode_server/test_tui_routing.py index 794173729..5d02acf85 100644 --- a/tests/servers/opencode_server/test_tui_routing.py +++ b/tests/servers/opencode_server/test_tui_routing.py @@ -14,8 +14,8 @@ import pytest -from agentpool_server.opencode_server.models import GlobalEvent -from agentpool_server.opencode_server.routes.routing import ( +from wolfharness_server.opencode_server.models import GlobalEvent +from wolfharness_server.opencode_server.routes.routing import ( RoutingCheckResponse, tui_event_filter, ) diff --git a/tests/servers/opencode_server/test_turn_lifecycle_state_machine.py b/tests/servers/opencode_server/test_turn_lifecycle_state_machine.py index a717bca81..b83c53e7c 100644 --- a/tests/servers/opencode_server/test_turn_lifecycle_state_machine.py +++ b/tests/servers/opencode_server/test_turn_lifecycle_state_machine.py @@ -120,22 +120,22 @@ import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( RunFailedEvent, RunStartedEvent, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.core import EventEnvelope -from agentpool_server.opencode_server.event_processor_context import ( +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) -from agentpool_server.opencode_server.models import ( +from wolfharness_server.opencode_server.models import ( MessagePath, MessageTime, MessageWithParts, ) -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) @@ -258,11 +258,11 @@ def _patch_mocks(): def _ctx(): with ( __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.set_session_status", + "wolfharness_server.opencode_server.opencode_event_bridge.set_session_status", new_callable=AsyncMock, ), __import__("unittest.mock").mock.patch( - "agentpool_server.opencode_server.opencode_event_bridge.append_message_to_session", + "wolfharness_server.opencode_server.opencode_event_bridge.append_message_to_session", new_callable=AsyncMock, ) as mock_append, ): @@ -520,7 +520,7 @@ async def test_d1_warns_on_incomplete_turn() -> None: Then: D1 fires, _finalize_assistant_time logs warning because time.completed is None. """ - import agentpool_server.opencode_server.opencode_event_bridge as bridge_module + import wolfharness_server.opencode_server.opencode_event_bridge as bridge_module session_id = "sess-d1-warn" bridge, _ctx, _broadcast = _setup_bridge( diff --git a/tests/servers/opencode_server/test_turn_split_integration.py b/tests/servers/opencode_server/test_turn_split_integration.py index b8ff2109c..7c56babea 100644 --- a/tests/servers/opencode_server/test_turn_split_integration.py +++ b/tests/servers/opencode_server/test_turn_split_integration.py @@ -20,7 +20,7 @@ from pydantic_ai import TextPart, TextPartDelta import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( PartDeltaEvent, PartStartEvent, RunStartedEvent, @@ -30,26 +30,26 @@ ToolCallStartEvent, UserMessageInsertedEvent, ) -from agentpool.messaging import ChatMessage -from agentpool.orchestrator.event_bus import EventEnvelope -from agentpool.utils import identifiers as identifier -from agentpool_server.opencode_server.models import ( +from wolfharness.messaging import ChatMessage +from wolfharness.orchestrator.event_bus import EventEnvelope +from wolfharness.utils import identifiers as identifier +from wolfharness_server.opencode_server.models import ( AssistantMessage, MessageWithParts, TimeCreated, UserMessage, ) -from agentpool_server.opencode_server.opencode_event_bridge import ( +from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) if TYPE_CHECKING: - from agentpool_server.opencode_server.event_adapter import OpenCodeEventAdapter - from agentpool_server.opencode_server.event_processor_context import ( + from wolfharness_server.opencode_server.event_adapter import OpenCodeEventAdapter + from wolfharness_server.opencode_server.event_processor_context import ( EventProcessorContext, ) - from agentpool_server.opencode_server.state import ServerState + from wolfharness_server.opencode_server.state import ServerState def _part_start(index: int, session_id: str, content: str = "") -> PartStartEvent: @@ -462,7 +462,7 @@ async def test_split_preserves_tool_parts_in_a1( # The EventProcessor creates ToolPart entries in ctx.tool_parts and # appends them to assistant_msg.parts. After split, ctx.tool_parts is # cleared, but A1's parts list already has them. - from agentpool_server.opencode_server.models import ToolPart + from wolfharness_server.opencode_server.models import ToolPart a1_tool_parts = [p for p in a1.parts if isinstance(p, ToolPart)] assert len(a1_tool_parts) >= 1, "A1 should contain tool parts from the tool call before split" diff --git a/tests/servers/test_a2a_server.py b/tests/servers/test_a2a_server.py index 5bf700c7f..bde416016 100644 --- a/tests/servers/test_a2a_server.py +++ b/tests/servers/test_a2a_server.py @@ -6,10 +6,10 @@ import pytest -from agentpool import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server.a2a_server import A2AServer +from wolfharness import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.a2a_server import A2AServer pytestmark = pytest.mark.integration @@ -119,7 +119,7 @@ async def test_a2a_server_from_config(tmp_path): environments: test: type: inline - module_path: agentpool + module_path: wolfharness class_name: Agent agents: diff --git a/tests/servers/test_aggregating_server.py b/tests/servers/test_aggregating_server.py index 57d19237c..3c3a804c5 100644 --- a/tests/servers/test_aggregating_server.py +++ b/tests/servers/test_aggregating_server.py @@ -4,10 +4,10 @@ import pytest -from agentpool import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server import A2AServer, AggregatingServer, AGUIServer +from wolfharness import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server import A2AServer, AggregatingServer, AGUIServer pytestmark = pytest.mark.integration diff --git a/tests/servers/test_agui_server.py b/tests/servers/test_agui_server.py index cb95128ce..b6709adf3 100644 --- a/tests/servers/test_agui_server.py +++ b/tests/servers/test_agui_server.py @@ -4,10 +4,10 @@ import pytest -from agentpool import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server.agui_server import AGUIServer +from wolfharness import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.agui_server import AGUIServer pytestmark = pytest.mark.integration @@ -102,7 +102,7 @@ async def test_agui_server_from_config(tmp_path): environments: test: type: inline - module_path: agentpool + module_path: wolfharness class_name: Agent agents: diff --git a/tests/servers/test_openai_api_server.py b/tests/servers/test_openai_api_server.py index def7e09b5..0cf7b01f2 100644 --- a/tests/servers/test_openai_api_server.py +++ b/tests/servers/test_openai_api_server.py @@ -8,10 +8,10 @@ from pydantic_ai.usage import RunUsage import pytest -from agentpool import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_server.openai_api_server.server import ( +from wolfharness import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_server.openai_api_server.server import ( OpenAIAPIServer, _serialize_completion_usage, ) diff --git a/tests/servers/test_openai_api_thinking.py b/tests/servers/test_openai_api_thinking.py index e75873240..e9c08615f 100644 --- a/tests/servers/test_openai_api_thinking.py +++ b/tests/servers/test_openai_api_thinking.py @@ -19,11 +19,11 @@ ) import pytest -from agentpool.messaging.messages import ChatMessage -from agentpool_server.openai_api_server.completions.helpers import stream_response -from agentpool_server.openai_api_server.completions.models import ChatCompletionRequest -from agentpool_server.openai_api_server.responses.helpers import handle_request -from agentpool_server.openai_api_server.responses.models import ( +from wolfharness.messaging.messages import ChatMessage +from wolfharness_server.openai_api_server.completions.helpers import stream_response +from wolfharness_server.openai_api_server.completions.models import ChatCompletionRequest +from wolfharness_server.openai_api_server.responses.helpers import handle_request +from wolfharness_server.openai_api_server.responses.models import ( ResponseMessage, ResponseOutputReasoning, ResponseRequest, @@ -72,7 +72,7 @@ def _make_assistant_with_thinking( def test_non_streaming_completion_includes_reasoning_content(): """Test that reasoning_content is populated from ThinkingPart.""" - from agentpool_server.openai_api_server.completions.models import OpenAIMessage + from wolfharness_server.openai_api_server.completions.models import OpenAIMessage msg = _make_assistant_with_thinking( thinking_content="Deep reasoning here.", @@ -104,7 +104,7 @@ def test_non_streaming_completion_includes_reasoning_content(): def test_non_streaming_completion_no_thinking_no_reasoning_content(): """Test that reasoning_content is None without ThinkingPart.""" - from agentpool_server.openai_api_server.completions.models import OpenAIMessage + from wolfharness_server.openai_api_server.completions.models import OpenAIMessage timestamp = datetime(2025, 1, 1, 12, 0, 0, tzinfo=UTC) model_response = ModelResponse( diff --git a/tests/servers/test_step_usage_agui_openai.py b/tests/servers/test_step_usage_agui_openai.py index fd3dcd59b..7464d2508 100644 --- a/tests/servers/test_step_usage_agui_openai.py +++ b/tests/servers/test_step_usage_agui_openai.py @@ -12,13 +12,13 @@ from pydantic_ai import RequestUsage, RunUsage import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( PartDeltaEvent, PartStartEvent, StepUsageEvent, StreamCompleteEvent, ) -from agentpool.messaging import ChatMessage +from wolfharness.messaging import ChatMessage if TYPE_CHECKING: @@ -59,7 +59,7 @@ async def test_agui_handles_step_usage_event() -> None: """Feed StepUsageEvent to AG-UI adapter, assert no exception and CustomEvent emitted.""" from ag_ui.core import CustomEvent, RunAgentInput - from agentpool_server.agui_server.base_agent_adapter import BaseAgentAGUIAdapter + from wolfharness_server.agui_server.base_agent_adapter import BaseAgentAGUIAdapter step_usage = RunUsage(input_tokens=100, output_tokens=50) cumulative = RunUsage(input_tokens=200, output_tokens=100) @@ -120,7 +120,7 @@ async def _collect_stream_chunks( """Collect all SSE chunks from stream_response as parsed dicts.""" import anyenv - from agentpool_server.openai_api_server.completions.helpers import stream_response + from wolfharness_server.openai_api_server.completions.helpers import stream_response chunks: list[dict[str, Any]] = [] async for sse_data in stream_response(events, request): @@ -137,7 +137,7 @@ async def _collect_stream_chunks( @pytest.mark.asyncio async def test_openai_api_handles_step_usage_event() -> None: """Feed StepUsageEvent to OpenAI API helper, assert usage in output.""" - from agentpool_server.openai_api_server.completions.models import ChatCompletionRequest + from wolfharness_server.openai_api_server.completions.models import ChatCompletionRequest step_usage = RunUsage(input_tokens=120, output_tokens=80) cumulative = RunUsage(input_tokens=240, output_tokens=160) @@ -182,7 +182,7 @@ async def test_openai_api_mixed_content_and_usage_chunks() -> None: - The usage chunk has ``choices: []`` (usage-only, no content). - The usage chunk appears before`` sentinel. """ - from agentpool_server.openai_api_server.completions.models import ChatCompletionRequest + from wolfharness_server.openai_api_server.completions.models import ChatCompletionRequest step_usage = RunUsage(input_tokens=10, output_tokens=5) cumulative = RunUsage(input_tokens=10, output_tokens=5) diff --git a/tests/servers/test_subagent_event_mixin.py b/tests/servers/test_subagent_event_mixin.py index f9ca93b88..f62c5fa3c 100644 --- a/tests/servers/test_subagent_event_mixin.py +++ b/tests/servers/test_subagent_event_mixin.py @@ -13,13 +13,13 @@ from pydantic_ai import TextPartDelta import pytest -from agentpool.agents.events.events import ( +from wolfharness.agents.events.events import ( PartDeltaEvent, RunErrorEvent, SpawnSessionStart, ) -from agentpool.orchestrator.core import EventBus, EventEnvelope -from agentpool_server.mixins import ( +from wolfharness.orchestrator.core import EventBus, EventEnvelope +from wolfharness_server.mixins import ( ConsumerShutdown, ProtocolEventConsumerMixin, ) diff --git a/tests/sessions/conftest.py b/tests/sessions/conftest.py index 261b6257f..bd76fea86 100644 --- a/tests/sessions/conftest.py +++ b/tests/sessions/conftest.py @@ -6,9 +6,9 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.sessions.models import SessionData +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider if TYPE_CHECKING: @@ -18,7 +18,7 @@ @pytest.fixture def _clear_engine_cache(): """Clear engine cache to avoid cross-test contamination.""" - from agentpool_config.storage import _engine_cache + from wolfharness_config.storage import _engine_cache _engine_cache.clear() yield diff --git a/tests/sessions/test_checkpoint_round_trip.py b/tests/sessions/test_checkpoint_round_trip.py index c99df0b9c..013abd204 100644 --- a/tests/sessions/test_checkpoint_round_trip.py +++ b/tests/sessions/test_checkpoint_round_trip.py @@ -12,11 +12,11 @@ import pytest -from agentpool.sessions.models import SessionData +from wolfharness.sessions.models import SessionData if TYPE_CHECKING: - from agentpool_storage.sql_provider import SQLModelProvider + from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_creation_unification.py b/tests/sessions/test_creation_unification.py index 473214838..a17f28afb 100644 --- a/tests/sessions/test_creation_unification.py +++ b/tests/sessions/test_creation_unification.py @@ -15,13 +15,13 @@ import pytest -from agentpool.utils.identifiers import generate_session_id -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness.utils.identifiers import generate_session_id +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.sessions.models import SessionData + from wolfharness import AgentPool + from wolfharness.sessions.models import SessionData pytestmark = pytest.mark.unit @@ -46,7 +46,7 @@ async def test_session_pool_create_session_registers_in_pool(minimal_pool: Agent """SessionPool.create_session() registers the session in the controller.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) state, _was_created = await controller.get_or_create_session( @@ -65,7 +65,7 @@ async def test_acp_creation_path_registers_in_pool(minimal_pool: AgentPool) -> N """ACP creation path delegates to SessionPool.create_session().""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) # Simulate what ACPSessionManager.create_session() does for top-level @@ -90,7 +90,7 @@ async def test_a2a_creation_path_registers_in_pool(minimal_pool: AgentPool) -> N """A2A creation path uses SessionPool.create_session() + get_or_create_session_agent.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) # Simulate what A2AServer agent_handler does @@ -105,7 +105,7 @@ async def test_agui_creation_path_registers_in_pool(minimal_pool: AgentPool) -> """AG-UI creation path uses two-step pattern: create_session + get_or_create_session_agent.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) # Simulate what AGUIServer agent_handler does @@ -120,7 +120,7 @@ async def test_openai_api_creation_path_registers_in_pool(minimal_pool: AgentPoo """OpenAI API creation path uses generate_session_id().""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) session_id = generate_session_id() @@ -139,7 +139,7 @@ async def test_child_session_inherits_parent_project_id_and_cwd(minimal_pool: Ag """Child session created via SessionPool inherits parent's project_id and cwd.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) @@ -176,7 +176,7 @@ async def test_create_child_session_api_inherits_parent(minimal_pool: AgentPool) """SessionPool.create_child_session() inherits parent's project_id and cwd.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_pool import SessionPool + from wolfharness.orchestrator.session_pool import SessionPool sp = SessionPool(minimal_pool, store=store, enable_event_bus=False) @@ -265,7 +265,7 @@ async def test_acp_create_delegates_to_session_pool(minimal_pool: AgentPool) -> """ACP create delegates to SessionPool (no direct store.save_session).""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) @@ -302,7 +302,7 @@ async def test_acp_close_delegates_through_chain(minimal_pool: AgentPool) -> Non """ACP close delegates through SessionController → RunHandle cleanup.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) session_id = generate_session_id() @@ -326,7 +326,7 @@ async def test_agui_child_consumer_lifecycle(minimal_pool: AgentPool) -> None: """AG-UI child consumer lifecycle works after create_child_session extraction.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_pool import SessionPool + from wolfharness.orchestrator.session_pool import SessionPool sp = SessionPool(minimal_pool, store=store, enable_event_bus=True) @@ -352,7 +352,7 @@ async def test_openai_api_child_consumer_lifecycle(minimal_pool: AgentPool) -> N """OpenAI API session lifecycle works with generate_session_id().""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) @@ -381,7 +381,7 @@ async def test_concurrent_session_creation_multiple_protocols(minimal_pool: Agen """Concurrent session creation from ACP + OpenCode + AG-UI clients simultaneously.""" store = MemoryStorageProvider() async with store: - from agentpool.orchestrator.session_controller import SessionController + from wolfharness.orchestrator.session_controller import SessionController controller = SessionController(minimal_pool, store=store) diff --git a/tests/sessions/test_e2e_lifecycle.py b/tests/sessions/test_e2e_lifecycle.py index 323b7ec03..cda3e556a 100644 --- a/tests/sessions/test_e2e_lifecycle.py +++ b/tests/sessions/test_e2e_lifecycle.py @@ -16,17 +16,17 @@ import pytest -from agentpool.orchestrator.core import SessionController -from agentpool.sessions.models import PendingDeferredCall, SessionData -from agentpool.storage.manager import StorageManager -from agentpool_config.storage import SQLStorageConfig, StorageConfig -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.orchestrator.core import SessionController +from wolfharness.sessions.models import PendingDeferredCall, SessionData +from wolfharness.storage.manager import StorageManager +from wolfharness_config.storage import SQLStorageConfig, StorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider if TYPE_CHECKING: from pathlib import Path - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.integration, pytest.mark.asyncio] @@ -92,7 +92,7 @@ async def test_e2e_session_lifecycle_with_sql_model_provider( assert loaded.pending_deferred_calls[0].tool_call_id == "tc-e2e-001" # --- Step 2: Save checkpoint via StorageManager --- - from agentpool.agents.native_agent.checkpoint import CheckpointManager + from wolfharness.agents.native_agent.checkpoint import CheckpointManager checkpoint_mgr = CheckpointManager(storage_manager) await checkpoint_mgr.checkpoint( diff --git a/tests/sessions/test_elicitation_cancel.py b/tests/sessions/test_elicitation_cancel.py index b895e949c..0ed8794fd 100644 --- a/tests/sessions/test_elicitation_cancel.py +++ b/tests/sessions/test_elicitation_cancel.py @@ -16,8 +16,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.agents.events import StreamCompleteEvent +from wolfharness import Agent +from wolfharness.agents.events import StreamCompleteEvent pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_history.py b/tests/sessions/test_history.py index eee4510c6..4aaa4b26f 100644 --- a/tests/sessions/test_history.py +++ b/tests/sessions/test_history.py @@ -6,12 +6,12 @@ from pydantic_ai.usage import RunUsage import pytest -from agentpool.messaging import ChatMessage, TokenCost -from agentpool.utils.parse_time import parse_time_period -from agentpool.utils.time_utils import get_now -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.models import QueryFilters, StatsFilters -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.messaging import ChatMessage, TokenCost +from wolfharness.utils.parse_time import parse_time_period +from wolfharness.utils.time_utils import get_now +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.models import QueryFilters, StatsFilters +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_history_processors.py b/tests/sessions/test_history_processors.py index 38dd6a843..3b44fa56d 100644 --- a/tests/sessions/test_history_processors.py +++ b/tests/sessions/test_history_processors.py @@ -5,10 +5,10 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent -from agentpool.models.agents import NativeAgentConfig -from agentpool.utils.inspection import get_fn_name -from agentpool_config.session import MemoryConfig +from wolfharness import Agent +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.utils.inspection import get_fn_name +from wolfharness_config.session import MemoryConfig pytestmark = pytest.mark.unit @@ -18,7 +18,7 @@ def _validate_processor_signature(processor): """Validate processor signature using Agent's validation method.""" # Create a minimal agent instance to access the method - from agentpool.agents.native_agent.agent import Agent as NativeAgent + from wolfharness.agents.native_agent.agent import Agent as NativeAgent # Access as unbound method and call with self=None (internal use only) return NativeAgent._validate_processor_signature(None, processor) @@ -236,7 +236,7 @@ async def test_compaction_and_processors_interaction(mock_model): """ from pydantic_ai import ModelResponse, TextPart - from agentpool.messaging import ChatMessage + from wolfharness.messaging import ChatMessage history = [ ChatMessage.user_prompt("M1"), diff --git a/tests/sessions/test_list_sessions_batch.py b/tests/sessions/test_list_sessions_batch.py index 26fa1d7df..f3eb9c8f3 100644 --- a/tests/sessions/test_list_sessions_batch.py +++ b/tests/sessions/test_list_sessions_batch.py @@ -11,10 +11,10 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_config.storage import MemoryStorageConfig, SQLStorageConfig -from agentpool_storage.memory_provider import MemoryStorageProvider -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.sessions.models import SessionData +from wolfharness_config.storage import MemoryStorageConfig, SQLStorageConfig +from wolfharness_storage.memory_provider import MemoryStorageProvider +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_list_sessions_cross_agent.py b/tests/sessions/test_list_sessions_cross_agent.py index fe866f197..1d3e41a3b 100644 --- a/tests/sessions/test_list_sessions_cross_agent.py +++ b/tests/sessions/test_list_sessions_cross_agent.py @@ -14,9 +14,9 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.sessions.models import SessionData +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_message_ordering.py b/tests/sessions/test_message_ordering.py index cc5b7e760..60e786ced 100644 --- a/tests/sessions/test_message_ordering.py +++ b/tests/sessions/test_message_ordering.py @@ -13,12 +13,12 @@ import pytest -from agentpool.messaging import ChatMessage -from agentpool_storage.memory_provider import MemoryStorageProvider +from wolfharness.messaging import ChatMessage +from wolfharness_storage.memory_provider import MemoryStorageProvider if TYPE_CHECKING: - from agentpool_storage.sql_provider import SQLModelProvider + from wolfharness_storage.sql_provider import SQLModelProvider # --------------------------------------------------------------------------- @@ -291,7 +291,7 @@ def test_parse_iso_timestamp_naive_string_returns_aware() -> None: When: parsed via parse_iso_timestamp Then: the result has tzinfo=UTC (not None) """ - from agentpool.utils.time_utils import parse_iso_timestamp + from wolfharness.utils.time_utils import parse_iso_timestamp result = parse_iso_timestamp("2025-01-01T12:00:00") assert result.tzinfo is not None @@ -308,7 +308,7 @@ def test_parse_iso_timestamp_aware_string_preserves_timezone() -> None: """ from datetime import timedelta - from agentpool.utils.time_utils import parse_iso_timestamp + from wolfharness.utils.time_utils import parse_iso_timestamp result = parse_iso_timestamp("2025-01-01T12:00:00+05:00") assert result.tzinfo is not None @@ -323,7 +323,7 @@ def test_parse_iso_timestamp_z_suffix_returns_utc() -> None: When: parsed via parse_iso_timestamp Then: the result has tzinfo=UTC """ - from agentpool.utils.time_utils import parse_iso_timestamp + from wolfharness.utils.time_utils import parse_iso_timestamp result = parse_iso_timestamp("2025-01-01T12:00:00Z") assert result.tzinfo is not None diff --git a/tests/sessions/test_opencode_helpers.py b/tests/sessions/test_opencode_helpers.py index 2d68d313d..cb9915e92 100644 --- a/tests/sessions/test_opencode_helpers.py +++ b/tests/sessions/test_opencode_helpers.py @@ -9,7 +9,7 @@ from pydantic_ai import BinaryContent, ImageUrl, TextContent import pytest -from agentpool_storage.opencode_provider.helpers import convert_user_content_to_parts +from wolfharness_storage.opencode_provider.helpers import convert_user_content_to_parts pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_opencode_integration.py b/tests/sessions/test_opencode_integration.py index 55a336074..20dc646ab 100644 --- a/tests/sessions/test_opencode_integration.py +++ b/tests/sessions/test_opencode_integration.py @@ -13,12 +13,12 @@ import pytest -from agentpool.sessions.models import ProjectData, SessionData -from agentpool.utils.identifiers import ascending -from agentpool_config.storage import OpenCodeStorageConfig -from agentpool_storage.opencode_provider import OpenCodeStorageProvider -from agentpool_storage.opencode_provider.helpers import compute_project_id -from agentpool_storage.project_store import generate_project_id +from wolfharness.sessions.models import ProjectData, SessionData +from wolfharness.utils.identifiers import ascending +from wolfharness_config.storage import OpenCodeStorageConfig +from wolfharness_storage.opencode_provider import OpenCodeStorageProvider +from wolfharness_storage.opencode_provider.helpers import compute_project_id +from wolfharness_storage.project_store import generate_project_id pytestmark = pytest.mark.integration @@ -141,18 +141,18 @@ async def test_both_project_ids_accessible() -> None: _init_git_repo(str(project_dir)) opencode_id = compute_project_id(str(project_dir)) - agentpool_id = generate_project_id(str(project_dir)) + wolfharness_id = generate_project_id(str(project_dir)) # Both should return valid hex strings (or "global" for compute_project_id) assert len(opencode_id) >= 1 - assert len(agentpool_id) == 40 # SHA1 hex digest + assert len(wolfharness_id) == 40 # SHA1 hex digest # With a git repo, compute_project_id returns the root commit SHA assert opencode_id != "global" assert len(opencode_id) == 40 # commit SHA1 hex digest # They are different algorithms — expect different values - assert opencode_id != agentpool_id, ( + assert opencode_id != wolfharness_id, ( "compute_project_id (git root SHA) and generate_project_id (path SHA) " "use different algorithms and should produce different values" ) diff --git a/tests/sessions/test_opencode_project_methods.py b/tests/sessions/test_opencode_project_methods.py index 6243f01eb..b62bee867 100644 --- a/tests/sessions/test_opencode_project_methods.py +++ b/tests/sessions/test_opencode_project_methods.py @@ -13,9 +13,9 @@ import pytest -from agentpool.sessions.models import ProjectData -from agentpool_config.storage import OpenCodeStorageConfig -from agentpool_storage.opencode_provider import OpenCodeStorageProvider +from wolfharness.sessions.models import ProjectData +from wolfharness_config.storage import OpenCodeStorageConfig +from wolfharness_storage.opencode_provider import OpenCodeStorageProvider pytestmark = pytest.mark.unit @@ -60,7 +60,7 @@ async def test_save_and_get_project(provider: OpenCodeStorageProvider) -> None: worktree="/home/user/project-a", name="project-a", vcs="git", - config_path="/home/user/project-a/.agentpool.yml", + config_path="/home/user/project-a/.wolfharness.yml", settings={"model": "openai:gpt-4o"}, ) @@ -73,7 +73,7 @@ async def test_save_and_get_project(provider: OpenCodeStorageProvider) -> None: assert result.worktree == "/home/user/project-a" assert result.name == "project-a" assert result.vcs == "git" - assert result.config_path == "/home/user/project-a/.agentpool.yml" + assert result.config_path == "/home/user/project-a/.wolfharness.yml" assert result.settings == {"model": "openai:gpt-4o"} diff --git a/tests/sessions/test_opencode_session_methods.py b/tests/sessions/test_opencode_session_methods.py index 306302c30..a5e59bfd2 100644 --- a/tests/sessions/test_opencode_session_methods.py +++ b/tests/sessions/test_opencode_session_methods.py @@ -18,10 +18,10 @@ import anyenv import pytest -from agentpool_config.storage import OpenCodeStorageConfig -from agentpool_server.opencode_server.models import Session, TimeCreatedUpdated -from agentpool_storage.opencode_provider import OpenCodeStorageProvider -from agentpool_storage.opencode_provider.helpers import compute_project_id +from wolfharness_config.storage import OpenCodeStorageConfig +from wolfharness_server.opencode_server.models import Session, TimeCreatedUpdated +from wolfharness_storage.opencode_provider import OpenCodeStorageProvider +from wolfharness_storage.opencode_provider.helpers import compute_project_id pytestmark = pytest.mark.integration diff --git a/tests/sessions/test_opencode_thinking_roundtrip.py b/tests/sessions/test_opencode_thinking_roundtrip.py index 4b5dcecf2..b14b8d425 100644 --- a/tests/sessions/test_opencode_thinking_roundtrip.py +++ b/tests/sessions/test_opencode_thinking_roundtrip.py @@ -12,13 +12,13 @@ from pydantic_ai.messages import ModelResponse, ThinkingPart import pytest -from agentpool_server.opencode_server.models.message import ( +from wolfharness_server.opencode_server.models.message import ( AssistantMessage, MessagePath, MessageTime, ) -from agentpool_server.opencode_server.models.parts import ReasoningPart -from agentpool_storage.opencode_provider.helpers import ( +from wolfharness_server.opencode_server.models.parts import ReasoningPart +from wolfharness_storage.opencode_provider.helpers import ( _build_assistant_pydantic_messages, ) @@ -149,7 +149,7 @@ def test_thinking_and_text_parts_coexist( self, assistant_msg: AssistantMessage, timestamp: datetime ) -> None: """ThinkingPart and TextPart in the same message are both preserved.""" - from agentpool_server.opencode_server.models.parts import TextPart + from wolfharness_server.opencode_server.models.parts import TextPart reasoning = ReasoningPart( id="prt_005", diff --git a/tests/sessions/test_pending_deferred_call.py b/tests/sessions/test_pending_deferred_call.py index fee4d9fac..f36ed3362 100644 --- a/tests/sessions/test_pending_deferred_call.py +++ b/tests/sessions/test_pending_deferred_call.py @@ -7,7 +7,7 @@ from pydantic import TypeAdapter import pytest -from agentpool.sessions.models import PendingDeferredCall, SessionData +from wolfharness.sessions.models import PendingDeferredCall, SessionData pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_project_provider_selection.py b/tests/sessions/test_project_provider_selection.py index 5f6c374a9..1199b3979 100644 --- a/tests/sessions/test_project_provider_selection.py +++ b/tests/sessions/test_project_provider_selection.py @@ -4,10 +4,10 @@ import pytest -from agentpool.storage.manager import StorageManager -from agentpool_config.storage import MemoryStorageConfig, StorageConfig -from agentpool_storage.memory_provider import MemoryStorageProvider -from agentpool_storage.opencode_provider import OpenCodeStorageProvider +from wolfharness.storage.manager import StorageManager +from wolfharness_config.storage import MemoryStorageConfig, StorageConfig +from wolfharness_storage.memory_provider import MemoryStorageProvider +from wolfharness_storage.opencode_provider import OpenCodeStorageProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_round_trip_migration.py b/tests/sessions/test_round_trip_migration.py index 60780e549..1293d334d 100644 --- a/tests/sessions/test_round_trip_migration.py +++ b/tests/sessions/test_round_trip_migration.py @@ -13,12 +13,12 @@ import pytest -from agentpool.sessions.models import PendingDeferredCall, SessionData -from agentpool_storage.protocols import CheckpointStore, SessionPersistence +from wolfharness.sessions.models import PendingDeferredCall, SessionData +from wolfharness_storage.protocols import CheckpointStore, SessionPersistence if TYPE_CHECKING: - from agentpool_storage.sql_provider import SQLModelProvider + from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_session_controller.py b/tests/sessions/test_session_controller.py index 3d617f93a..8142553d2 100644 --- a/tests/sessions/test_session_controller.py +++ b/tests/sessions/test_session_controller.py @@ -6,11 +6,11 @@ import pytest -from agentpool.orchestrator import SessionController -from agentpool.sessions import SessionData -from agentpool_config.storage import MemoryStorageConfig, SQLStorageConfig -from agentpool_storage.memory_provider.provider import MemoryStorageProvider -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.orchestrator import SessionController +from wolfharness.sessions import SessionData +from wolfharness_config.storage import MemoryStorageConfig, SQLStorageConfig +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.integration @@ -19,7 +19,7 @@ if TYPE_CHECKING: from pathlib import Path - from agentpool import AgentPool + from wolfharness import AgentPool class TestSessionData: @@ -419,7 +419,7 @@ async def test_child_inherits_project_id_via_metadata( # previously used datetime.fromtimestamp(time.monotonic()), # producing a 1970 datetime. Verify the full pipeline produces # a sane time.created in the OpenCode session model. - from agentpool_server.opencode_server.converters import session_data_to_opencode + from wolfharness_server.opencode_server.converters import session_data_to_opencode opencode_session = session_data_to_opencode(child) assert opencode_session.time.created > 1_000_000_000_000 diff --git a/tests/sessions/test_session_from_db_edge_cases.py b/tests/sessions/test_session_from_db_edge_cases.py index cb625384e..71dbfb448 100644 --- a/tests/sessions/test_session_from_db_edge_cases.py +++ b/tests/sessions/test_session_from_db_edge_cases.py @@ -13,12 +13,12 @@ import pytest -from agentpool.sessions.models import SessionData -from agentpool_storage.sql_provider.models import Conversation +from wolfharness.sessions.models import SessionData +from wolfharness_storage.sql_provider.models import Conversation if TYPE_CHECKING: - from agentpool_storage.sql_provider import SQLModelProvider + from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_session_hierarchy.py b/tests/sessions/test_session_hierarchy.py index d712e148d..8578522d6 100644 --- a/tests/sessions/test_session_hierarchy.py +++ b/tests/sessions/test_session_hierarchy.py @@ -6,12 +6,12 @@ import pytest -from agentpool.orchestrator import SessionPool -from agentpool.sessions import SessionData -from agentpool.utils.identifiers import generate_session_id -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.memory_provider.provider import MemoryStorageProvider -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.orchestrator import SessionPool +from wolfharness.sessions import SessionData +from wolfharness.utils.identifiers import generate_session_id +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.integration @@ -20,7 +20,7 @@ if TYPE_CHECKING: from pathlib import Path - from agentpool import AgentPool + from wolfharness import AgentPool @pytest.fixture diff --git a/tests/sessions/test_session_id_opaque.py b/tests/sessions/test_session_id_opaque.py index de5636c46..4df665848 100644 --- a/tests/sessions/test_session_id_opaque.py +++ b/tests/sessions/test_session_id_opaque.py @@ -19,12 +19,12 @@ import pytest -from agentpool.sessions import SessionData -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness.sessions import SessionData +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -128,8 +128,8 @@ def mock_pool(self, minimal_pool: AgentPool) -> AgentPool: async def test_child_session_id_is_opaque_string(self, minimal_pool: AgentPool) -> None: """Child session IDs must be non-empty opaque strings.""" - from agentpool.orchestrator import SessionPool - from agentpool.utils.identifiers import generate_session_id + from wolfharness.orchestrator import SessionPool + from wolfharness.utils.identifiers import generate_session_id session_pool = SessionPool(pool=minimal_pool, store=None) state = await session_pool.create_session( @@ -156,8 +156,8 @@ async def test_create_child_with_opaque_parent_id( self, minimal_pool: AgentPool, parent_id: str ) -> None: """create_session must accept any string as parent_session_id.""" - from agentpool.orchestrator import SessionPool - from agentpool.utils.identifiers import generate_session_id + from wolfharness.orchestrator import SessionPool + from wolfharness.utils.identifiers import generate_session_id store = MemoryStorageProvider() parent = SessionData(session_id=parent_id, agent_name="parent_agent") @@ -187,8 +187,8 @@ async def test_get_child_sessions_with_opaque_parent_id( self, minimal_pool: AgentPool, parent_id: str ) -> None: """get_children must find children by opaque parent ID.""" - from agentpool.orchestrator import SessionPool - from agentpool.utils.identifiers import generate_session_id + from wolfharness.orchestrator import SessionPool + from wolfharness.utils.identifiers import generate_session_id store = MemoryStorageProvider() parent = SessionData(session_id=parent_id, agent_name="parent_agent") @@ -225,7 +225,7 @@ def test_no_sequential_session_id_regex(self) -> None: This test documents that the identifiers module doesn't encourage counter-based parsing. """ - from agentpool.utils.identifiers import generate_session_id + from wolfharness.utils.identifiers import generate_session_id id1 = generate_session_id() id2 = generate_session_id() @@ -237,7 +237,7 @@ def test_no_sequential_session_id_regex(self) -> None: def test_ascending_format_produces_sortable_ids(self) -> None: """ascending() IDs are lexicographically sortable — a property, not a format dependency.""" - from agentpool.utils.identifiers import ascending + from wolfharness.utils.identifiers import ascending ids = [ascending("session") for _ in range(10)] # Later IDs must sort after earlier ones @@ -246,7 +246,7 @@ def test_ascending_format_produces_sortable_ids(self) -> None: def test_ascending_with_given_accepts_any_valid_prefix(self) -> None: """ascending(prefix, given=...) validates prefix only — no format parsing.""" - from agentpool.utils.identifiers import ascending + from wolfharness.utils.identifiers import ascending # Providing an ID that starts with "ses" must be accepted result = ascending("session", given="ses_custom_suffix") @@ -254,7 +254,7 @@ def test_ascending_with_given_accepts_any_valid_prefix(self) -> None: def test_ascending_with_wrong_prefix_raises(self) -> None: """ascending(prefix, given=...) raises if prefix doesn't match.""" - from agentpool.utils.identifiers import ascending + from wolfharness.utils.identifiers import ascending with pytest.raises(ValueError, match="does not start with"): ascending("session", given="msg_something") @@ -270,10 +270,10 @@ class TestServerSessionLookupOpaque: def test_opencode_state_sessions_dict_opaque(self) -> None: """ServerState.sessions dict accepts any string key.""" - from agentpool.utils.time_utils import now_ms - from agentpool_server.opencode_server.models import Session - from agentpool_server.opencode_server.models.common import TimeCreatedUpdated - from agentpool_server.opencode_server.state import ServerState + from wolfharness.utils.time_utils import now_ms + from wolfharness_server.opencode_server.models import Session + from wolfharness_server.opencode_server.models.common import TimeCreatedUpdated + from wolfharness_server.opencode_server.state import ServerState agent = MagicMock() state = ServerState(working_dir="/tmp", agent=agent) @@ -302,7 +302,7 @@ def test_acp_session_manager_acp_sessions_dict_opaque(self) -> None: # We test the dict-based lookup pattern (get_session) conceptually. # The _acp_sessions dict maps session_id -> ACPSession using plain dict.get(). # This test documents that the lookup is opaque. - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness_server.acp_server.session_manager import ACPSessionManager # ACPSessionManager._acp_sessions is a plain dict[str, ACPSession] # get_session does: return self._acp_sessions.get(session_id) diff --git a/tests/sessions/test_session_persistence.py b/tests/sessions/test_session_persistence.py index 3838e0f89..84766c448 100644 --- a/tests/sessions/test_session_persistence.py +++ b/tests/sessions/test_session_persistence.py @@ -15,14 +15,14 @@ import pytest -from agentpool.orchestrator.core import SessionController -from agentpool.sessions.models import SessionData -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.orchestrator.core import SessionController +from wolfharness.sessions.models import SessionData +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit @@ -44,7 +44,7 @@ def make_session_data( @pytest.fixture def _clear_engine_cache(): """Clear engine cache to avoid cross-test contamination.""" - from agentpool_config.storage import _engine_cache + from wolfharness_config.storage import _engine_cache _engine_cache.clear() yield diff --git a/tests/sessions/test_sql_provider_no_dispose.py b/tests/sessions/test_sql_provider_no_dispose.py index 6d0578cf1..a54ed1962 100644 --- a/tests/sessions/test_sql_provider_no_dispose.py +++ b/tests/sessions/test_sql_provider_no_dispose.py @@ -11,9 +11,9 @@ import pytest -from agentpool.sessions import SessionData -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.sessions import SessionData +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.unit diff --git a/tests/sessions/test_state_mapper.py b/tests/sessions/test_state_mapper.py index 01a5203d5..0d2497ae8 100644 --- a/tests/sessions/test_state_mapper.py +++ b/tests/sessions/test_state_mapper.py @@ -13,8 +13,8 @@ import pytest -from agentpool.lifecycle.types import RunState -from agentpool.sessions.state_mapper import ( +from wolfharness.lifecycle.types import RunState +from wolfharness.sessions.state_mapper import ( SESSION_STATUS_ACTIVE, SESSION_STATUS_CHECKPOINTED, SESSION_STATUS_CLOSED, diff --git a/tests/sessions/test_state_mapper_integration.py b/tests/sessions/test_state_mapper_integration.py index abf8aaed4..868e47e48 100644 --- a/tests/sessions/test_state_mapper_integration.py +++ b/tests/sessions/test_state_mapper_integration.py @@ -18,9 +18,9 @@ import pytest -from agentpool.lifecycle.journal import DurableJournal -from agentpool.lifecycle.snapshot_store import DurableSnapshotStore -from agentpool.sessions.state_mapper import ( +from wolfharness.lifecycle.journal import DurableJournal +from wolfharness.lifecycle.snapshot_store import DurableSnapshotStore +from wolfharness.sessions.state_mapper import ( SESSION_STATUS_ACTIVE, SESSION_STATUS_CHECKPOINTED, SESSION_STATUS_RESUMING, @@ -29,7 +29,7 @@ if TYPE_CHECKING: - from agentpool.lifecycle.types import RunState + from wolfharness.lifecycle.types import RunState pytestmark = pytest.mark.integration diff --git a/tests/sessions/test_storage_provider_fixes.py b/tests/sessions/test_storage_provider_fixes.py index d880edb92..8066f9fa3 100644 --- a/tests/sessions/test_storage_provider_fixes.py +++ b/tests/sessions/test_storage_provider_fixes.py @@ -14,12 +14,12 @@ import pytest -from agentpool.messaging import ChatMessage -from agentpool.sessions import SessionData -from agentpool.utils.identifiers import ascending -from agentpool_config.storage import OpenCodeStorageConfig, SQLStorageConfig -from agentpool_storage.opencode_provider import OpenCodeStorageProvider -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.messaging import ChatMessage +from wolfharness.sessions import SessionData +from wolfharness.utils.identifiers import ascending +from wolfharness_config.storage import OpenCodeStorageConfig, SQLStorageConfig +from wolfharness_storage.opencode_provider import OpenCodeStorageProvider +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.integration @@ -166,7 +166,7 @@ async def test_load_session_returns_session_data( ) -> None: """Test that load_session returns SessionData for existing session.""" # Use ascending() to generate unique IDs - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Session, SessionSummary, TimeCreatedUpdated, @@ -212,7 +212,7 @@ async def test_load_session_returns_session_data( async def test_delete_session(self, provider: OpenCodeStorageProvider) -> None: """Test that delete_session removes session and all data.""" # Use ascending() to generate unique IDs - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Session, SessionSummary, TimeCreatedUpdated, @@ -259,7 +259,7 @@ async def test_list_session_ids(self, provider: OpenCodeStorageProvider) -> None # Use ascending() to generate unique IDs import anyenv - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Session, SessionSummary, TimeCreatedUpdated, @@ -372,11 +372,11 @@ async def test_list_session_ids_cwd_filters_by_directory( """ import anyenv - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Session, TimeCreatedUpdated, ) - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id # Create two separate git repos (simulating two different projects) alpha_dir = provider.base_path / "alpha_project" @@ -439,7 +439,7 @@ async def test_list_session_ids_cwd_none_returns_all( """list_session_ids(cwd=None) should return all sessions (no filtering).""" import anyenv - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Session, TimeCreatedUpdated, ) @@ -476,7 +476,7 @@ async def test_list_session_ids_cwd_excludes_corrupted_session( Regression test: previously, if read_session returned None (corrupted JSON / I/O error), the cwd filter was bypassed and the session was incorrectly included in results. """ - from agentpool_storage.opencode_provider.helpers import compute_project_id + from wolfharness_storage.opencode_provider.helpers import compute_project_id # Create a real git repo so compute_project_id returns a valid project_id workdir = provider.base_path / "corrupt_project" @@ -491,7 +491,7 @@ async def test_list_session_ids_cwd_excludes_corrupted_session( valid_id = ascending("session") import anyenv - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( Session, TimeCreatedUpdated, ) @@ -529,7 +529,7 @@ async def test_list_session_ids_signature_matches_base_class( """ import inspect - from agentpool_storage.base import StorageProvider + from wolfharness_storage.base import StorageProvider base_params = inspect.signature(StorageProvider.list_session_ids).parameters override_params = inspect.signature(OpenCodeStorageProvider.list_session_ids).parameters @@ -558,7 +558,7 @@ def test_deserialize_messages_returns_model_message_objects(self) -> None: """ from pydantic_ai import ModelRequest, ModelResponse, TextPart, UserPromptPart - from agentpool.storage.serialization import ( + from wolfharness.storage.serialization import ( deserialize_messages, serialize_messages, ) @@ -594,13 +594,13 @@ def test_deserialize_messages_returns_model_message_objects(self) -> None: def test_deserialize_messages_empty_json(self) -> None: """Test that deserialize_messages handles empty/None input.""" - from agentpool.storage.serialization import deserialize_messages + from wolfharness.storage.serialization import deserialize_messages assert deserialize_messages(None) == [] assert deserialize_messages("") == [] def test_serialize_messages_empty_list(self) -> None: """Test that serialize_messages returns None for empty list.""" - from agentpool.storage.serialization import serialize_messages + from wolfharness.storage.serialization import serialize_messages assert serialize_messages([]) is None diff --git a/tests/sessions/test_title_generation.py b/tests/sessions/test_title_generation.py index 8948bd415..9bf89e556 100644 --- a/tests/sessions/test_title_generation.py +++ b/tests/sessions/test_title_generation.py @@ -9,27 +9,27 @@ import anyio import pytest -from agentpool import AgentPool -from agentpool.messaging import ChatMessage -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool.storage.manager import StorageManager -from agentpool_config.storage import ( +from wolfharness import AgentPool +from wolfharness.messaging import ChatMessage +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness.storage.manager import StorageManager +from wolfharness_config.storage import ( FileStorageConfig, MemoryStorageConfig, SQLStorageConfig, StorageConfig, ) -from agentpool_storage.file_provider import FileProvider -from agentpool_storage.memory_provider import MemoryStorageProvider -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness_storage.file_provider import FileProvider +from wolfharness_storage.memory_provider import MemoryStorageProvider +from wolfharness_storage.sql_provider import SQLModelProvider pytestmark = pytest.mark.integration if TYPE_CHECKING: - from agentpool_storage.models import ConversationData + from wolfharness_storage.models import ConversationData GENERATED_TITLE = "Test Conversation Title" diff --git a/tests/skills/test_command.py b/tests/skills/test_command.py index 927d7e5fc..fd2ea5b24 100644 --- a/tests/skills/test_command.py +++ b/tests/skills/test_command.py @@ -5,8 +5,8 @@ import pytest from upathtools import UPath -from agentpool.skills.command import SkillCommand -from agentpool.skills.skill import Skill +from wolfharness.skills.command import SkillCommand +from wolfharness.skills.skill import Skill pytestmark = pytest.mark.unit diff --git a/tests/skills/test_exceptions.py b/tests/skills/test_exceptions.py index 7ee9391a2..a673e01fc 100644 --- a/tests/skills/test_exceptions.py +++ b/tests/skills/test_exceptions.py @@ -12,14 +12,14 @@ import pytest -from agentpool.skills.exceptions import ( +from wolfharness.skills.exceptions import ( ProviderError, ReferenceNotFoundError, SecurityError, SkillError, SkillNotFoundError, ) -from agentpool.utils.baseregistry import AgentPoolError +from wolfharness.utils.baseregistry import AgentPoolError pytestmark = pytest.mark.unit diff --git a/tests/skills/test_flat_uri.py b/tests/skills/test_flat_uri.py index ce98c0205..ba97d1992 100644 --- a/tests/skills/test_flat_uri.py +++ b/tests/skills/test_flat_uri.py @@ -15,15 +15,15 @@ import pytest -from agentpool.capabilities.extension_registry import ( +from wolfharness.capabilities.extension_registry import ( ExtensionRegistry, Scope, ScopeLevel, ) -from agentpool.capabilities.resource_protocols import SkillEntry, SkillResource -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.skills.skill import Skill -from agentpool.skills.uri_resolver import ResolvedSkillURI, SkillURIResolver +from wolfharness.capabilities.resource_protocols import SkillEntry, SkillResource +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.skills.skill import Skill +from wolfharness.skills.uri_resolver import ResolvedSkillURI, SkillURIResolver pytestmark = pytest.mark.unit @@ -176,7 +176,7 @@ async def test_flat_uri_resolver_raises_for_missing_skill() -> None: resolve('skill://nonexistent') is called, Then SkillNotFoundError is raised. """ - from agentpool.skills.exceptions import SkillNotFoundError + from wolfharness.skills.exceptions import SkillNotFoundError reg = ExtensionRegistry() skill_cap = FakeSkillResource(skills={"existing": "content"}) diff --git a/tests/skills/test_load_skill_propagation.py b/tests/skills/test_load_skill_propagation.py index f25ef1b0f..7201658f0 100644 --- a/tests/skills/test_load_skill_propagation.py +++ b/tests/skills/test_load_skill_propagation.py @@ -15,11 +15,11 @@ import pytest -from agentpool.host.factory import _inject_pool_providers +from wolfharness.host.factory import _inject_pool_providers if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.unit diff --git a/tests/skills/test_manager_config.py b/tests/skills/test_manager_config.py index 44cb38277..8a80f3dad 100644 --- a/tests/skills/test_manager_config.py +++ b/tests/skills/test_manager_config.py @@ -11,18 +11,18 @@ import structlog from upathtools import UPath -from agentpool.skills.manager import SkillsManager -from agentpool_config.skills import SkillsConfig +from wolfharness.skills.manager import SkillsManager +from wolfharness_config.skills import SkillsConfig pytestmark = pytest.mark.unit @pytest.fixture(autouse=True) -def _setup_agentpool_logging() -> None: - """Ensure agentpool loggers are configured for test capture. +def _setup_wolfharness_logging() -> None: + """Ensure wolfharness loggers are configured for test capture. - Resets structlog to a minimal configuration and sets the agentpool + Resets structlog to a minimal configuration and sets the wolfharness logger tree to DEBUG so that caplog reliably captures log messages regardless of structlog auto-configuration order in parallel runs. """ @@ -38,9 +38,9 @@ def _setup_agentpool_logging() -> None: cache_logger_on_first_use=True, ) - agentpool_root = logging.getLogger("agentpool") - agentpool_root.setLevel(logging.DEBUG) - agentpool_root.propagate = True + wolfharness_root = logging.getLogger("wolfharness") + wolfharness_root.setLevel(logging.DEBUG) + wolfharness_root.propagate = True logging.getLogger().setLevel(logging.DEBUG) @@ -122,7 +122,7 @@ async def test_discover_skills_logging( caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch ): """Test that missing custom paths log WARNING and missing default paths log DEBUG.""" - from agentpool_config import skills + from wolfharness_config import skills mock_default = [UPath("/non/existent/default/path")] monkeypatch.setattr(skills, "DEFAULT_SKILLS_PATHS", mock_default) diff --git a/tests/skills/test_mcp_skills_integration.py b/tests/skills/test_mcp_skills_integration.py index 27c880735..ab1213bdc 100644 --- a/tests/skills/test_mcp_skills_integration.py +++ b/tests/skills/test_mcp_skills_integration.py @@ -17,9 +17,9 @@ import pytest -from agentpool.capabilities.resource_protocols import SkillEntry, SkillResource -from agentpool.skills.skill import Skill -from agentpool_toolsets.builtin.skills import list_skills, load_skill +from wolfharness.capabilities.resource_protocols import SkillEntry, SkillResource +from wolfharness.skills.skill import Skill +from wolfharness_toolsets.builtin.skills import list_skills, load_skill pytestmark = pytest.mark.integration @@ -90,7 +90,7 @@ async def skill_exists(self, skill_name: str) -> bool: return True fake_child = _FakeSkillResource() - from agentpool.capabilities.skill_manager_cap import SkillManagerCap + from wolfharness.capabilities.skill_manager_cap import SkillManagerCap mock_cap = SkillManagerCap(local_skills={}, children=[fake_child], name="pool-skills") ctx_pool.skill_capabilities = [mock_cap] @@ -119,7 +119,7 @@ async def mock_resolve(uri: str): from types import SimpleNamespace - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext ctx = RuntimeAgentContext(node=SimpleNamespace(name="test"), pool=ctx_pool) diff --git a/tests/skills/test_registry_events.py b/tests/skills/test_registry_events.py index 400a1fc12..6324b670f 100644 --- a/tests/skills/test_registry_events.py +++ b/tests/skills/test_registry_events.py @@ -5,8 +5,8 @@ import pytest from upathtools import UPath -from agentpool.skills.registry import SkillsRegistry -from agentpool.skills.skill import Skill +from wolfharness.skills.registry import SkillsRegistry +from wolfharness.skills.skill import Skill pytestmark = pytest.mark.unit diff --git a/tests/skills/test_scratchpad_skill_reference_redflag.py b/tests/skills/test_scratchpad_skill_reference_redflag.py index 56fc33a87..dc42521d0 100644 --- a/tests/skills/test_scratchpad_skill_reference_redflag.py +++ b/tests/skills/test_scratchpad_skill_reference_redflag.py @@ -31,10 +31,10 @@ import pytest -from agentpool.skills.exceptions import SkillNotFoundError -from agentpool.skills.skill import Skill -from agentpool.skills.uri_resolver import SkillURIResolver -from agentpool_toolsets.builtin.skills import load_skill +from wolfharness.skills.exceptions import SkillNotFoundError +from wolfharness.skills.skill import Skill +from wolfharness.skills.uri_resolver import SkillURIResolver +from wolfharness_toolsets.builtin.skills import load_skill pytestmark = pytest.mark.integration @@ -56,7 +56,7 @@ def __init__(self, skills: list[Skill]) -> None: self._skills = skills async def list_skills(self): - from agentpool.capabilities.resource_protocols import SkillEntry + from wolfharness.capabilities.resource_protocols import SkillEntry return [ SkillEntry( @@ -245,7 +245,7 @@ async def test_load_skill_tool_with_uri_and_reference( ctx = mock_agent_context_with_resolver # We need to mock _load_reference_content to avoid actual MCP calls - with patch("agentpool_toolsets.builtin.skills._load_reference_content") as mock_load_ref: + with patch("wolfharness_toolsets.builtin.skills._load_reference_content") as mock_load_ref: mock_load_ref.return_value = ( "\n\n## Reference: expert_knowledge/excavator/excavator-hard-starting.md\n\n" "# Excavator Hard Starting\n\nDiagnostic procedure." diff --git a/tests/skills/test_skill_parsing.py b/tests/skills/test_skill_parsing.py index 90bac3361..7973165f2 100644 --- a/tests/skills/test_skill_parsing.py +++ b/tests/skills/test_skill_parsing.py @@ -9,7 +9,7 @@ import pytest from upathtools import UPath -from agentpool.skills.skill import Skill +from wolfharness.skills.skill import Skill pytestmark = pytest.mark.unit diff --git a/tests/skills/test_skill_tool_manager.py b/tests/skills/test_skill_tool_manager.py index fc34256ea..c86d0cc47 100644 --- a/tests/skills/test_skill_tool_manager.py +++ b/tests/skills/test_skill_tool_manager.py @@ -17,9 +17,9 @@ import pytest -from agentpool.skills.skill_tool_manager import SkillToolManager -from agentpool.tools.base import Tool -from agentpool_config.skills import SkillToolConfig +from wolfharness.skills.skill_tool_manager import SkillToolManager +from wolfharness.tools.base import Tool +from wolfharness_config.skills import SkillToolConfig pytestmark = pytest.mark.unit @@ -67,7 +67,7 @@ def test_import_nonexistent_module(self, manager: SkillToolManager) -> None: import_path="nonexistent_module_xyz__test:nonexistent_func", ) - with patch("agentpool.skills.skill_tool_manager.logger") as mock_logger: + with patch("wolfharness.skills.skill_tool_manager.logger") as mock_logger: tool = manager.import_tool(config) assert tool is None @@ -77,7 +77,7 @@ def test_import_path_without_colon(self, manager: SkillToolManager) -> None: """Import path without ':' logs warning and returns None.""" config = SkillToolConfig(type="python", import_path="os") - with patch("agentpool.skills.skill_tool_manager.logger") as mock_logger: + with patch("wolfharness.skills.skill_tool_manager.logger") as mock_logger: tool = manager.import_tool(config) assert tool is None @@ -89,7 +89,7 @@ def test_import_non_callable_attribute(self, manager: SkillToolManager) -> None: # os.sep is a plain string, not callable config = SkillToolConfig(type="python", import_path="os:sep") - with patch("agentpool.skills.skill_tool_manager.logger") as mock_logger: + with patch("wolfharness.skills.skill_tool_manager.logger") as mock_logger: tool = manager.import_tool(config) assert tool is None @@ -103,7 +103,7 @@ def test_import_nonexistent_attribute(self, manager: SkillToolManager) -> None: import_path="os:this_attr_does_not_exist_xyz", ) - with patch("agentpool.skills.skill_tool_manager.logger") as mock_logger: + with patch("wolfharness.skills.skill_tool_manager.logger") as mock_logger: tool = manager.import_tool(config) assert tool is None @@ -141,7 +141,7 @@ def test_batch_mixed_valid_invalid(self, manager: SkillToolManager) -> None: SkillToolConfig(type="python", import_path="os:sep"), # non-callable ] - with patch("agentpool.skills.skill_tool_manager.logger"): + with patch("wolfharness.skills.skill_tool_manager.logger"): tools = manager.import_tools(configs) assert len(tools) == 2 @@ -154,7 +154,7 @@ def test_batch_all_invalid(self, manager: SkillToolManager) -> None: SkillToolConfig(type="python", import_path="os:sep"), # non-callable ] - with patch("agentpool.skills.skill_tool_manager.logger"): + with patch("wolfharness.skills.skill_tool_manager.logger"): tools = manager.import_tools(configs) assert tools == [] diff --git a/tests/skills/test_skills.py b/tests/skills/test_skills.py index aa5f65b61..6163530ea 100644 --- a/tests/skills/test_skills.py +++ b/tests/skills/test_skills.py @@ -10,8 +10,8 @@ import pytest from upathtools import UPath -from agentpool.skills.registry import SkillsRegistry -from agentpool.skills.skill import Skill +from wolfharness.skills.registry import SkillsRegistry +from wolfharness.skills.skill import Skill pytestmark = pytest.mark.unit diff --git a/tests/skills/test_skills_config.py b/tests/skills/test_skills_config.py index 5737be361..e931fa173 100644 --- a/tests/skills/test_skills_config.py +++ b/tests/skills/test_skills_config.py @@ -5,7 +5,7 @@ import pytest from upathtools import UPath -from agentpool_config.skills import DEFAULT_SKILLS_PATHS, SkillsConfig +from wolfharness_config.skills import DEFAULT_SKILLS_PATHS, SkillsConfig pytestmark = pytest.mark.unit diff --git a/tests/skills/test_skills_integration.py b/tests/skills/test_skills_integration.py index a0d8cafef..2eeec88f3 100644 --- a/tests/skills/test_skills_integration.py +++ b/tests/skills/test_skills_integration.py @@ -12,7 +12,7 @@ from upathtools import UPath import yaml -from agentpool.delegation.pool import AgentPool +from wolfharness.delegation.pool import AgentPool pytestmark = pytest.mark.integration @@ -117,7 +117,7 @@ async def test_skills_disable_defaults(monkeypatch: pytest.MonkeyPatch): Assert that default paths are NOT searched. """ - from agentpool_config import skills + from wolfharness_config import skills # Mock default paths to something we can control with tempfile.TemporaryDirectory() as temp_dir: diff --git a/tests/skills/test_uri_resolver.py b/tests/skills/test_uri_resolver.py index e93ee0d82..f7d90cd0e 100644 --- a/tests/skills/test_uri_resolver.py +++ b/tests/skills/test_uri_resolver.py @@ -14,9 +14,9 @@ import pytest -from agentpool.capabilities.resource_protocols import SkillEntry, SkillResource -from agentpool.skills.exceptions import SecurityError, SkillNotFoundError -from agentpool.skills.uri_resolver import ( +from wolfharness.capabilities.resource_protocols import SkillEntry, SkillResource +from wolfharness.skills.exceptions import SecurityError, SkillNotFoundError +from wolfharness.skills.uri_resolver import ( ResolvedSkillURI, SkillURIResolver, _validate_skill_name, diff --git a/tests/storage/test_checkpoint_storage.py b/tests/storage/test_checkpoint_storage.py index 8813bd640..fb777e3b8 100644 --- a/tests/storage/test_checkpoint_storage.py +++ b/tests/storage/test_checkpoint_storage.py @@ -14,11 +14,11 @@ ) import pytest -from agentpool.sessions.models import PendingDeferredCall -from agentpool.storage.manager import StorageManager -from agentpool.storage.serialization import serialize_messages -from agentpool_config.storage import MemoryStorageConfig, StorageConfig -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness.sessions.models import PendingDeferredCall +from wolfharness.storage.manager import StorageManager +from wolfharness.storage.serialization import serialize_messages +from wolfharness_config.storage import MemoryStorageConfig, StorageConfig +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider pytestmark = pytest.mark.unit diff --git a/tests/storage/test_deferred_serialization.py b/tests/storage/test_deferred_serialization.py index 54f4223a0..d99868301 100644 --- a/tests/storage/test_deferred_serialization.py +++ b/tests/storage/test_deferred_serialization.py @@ -6,8 +6,8 @@ import pytest -from agentpool.sessions.models import PendingDeferredCall -from agentpool.storage.serialization import ( +from wolfharness.sessions.models import PendingDeferredCall +from wolfharness.storage.serialization import ( deserialize_pending_calls, serialize_pending_calls, ) diff --git a/tests/storage/test_memory_checkpoint.py b/tests/storage/test_memory_checkpoint.py index 51ef81f3c..378f94dc4 100644 --- a/tests/storage/test_memory_checkpoint.py +++ b/tests/storage/test_memory_checkpoint.py @@ -6,7 +6,7 @@ import pytest -from agentpool_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider @pytest.fixture @@ -59,7 +59,7 @@ async def test_delete_checkpoint_nonexistent(self, store: MemoryStorageProvider) @pytest.mark.unit async def test_session_delete_cleans_checkpoint(self, store: MemoryStorageProvider) -> None: """Deleting a session also cleans up its checkpoint data.""" - from agentpool.sessions import SessionData + from wolfharness.sessions import SessionData session_id = "s1" data = SessionData(session_id=session_id, agent_name="test_agent") diff --git a/tests/storage/test_protocols.py b/tests/storage/test_protocols.py index 1a98724ee..2d6fb2b0f 100644 --- a/tests/storage/test_protocols.py +++ b/tests/storage/test_protocols.py @@ -7,9 +7,9 @@ import pytest -from agentpool_storage.adapter import StorageProviderAdapter -from agentpool_storage.base import StorageProvider -from agentpool_storage.protocols import ( +from wolfharness_storage.adapter import StorageProviderAdapter +from wolfharness_storage.base import StorageProvider +from wolfharness_storage.protocols import ( CheckpointStore, CommandLog, MessagePersistence, @@ -189,19 +189,19 @@ class TestProviderProtocolConformance: """Test that concrete providers pass isinstance checks.""" def test_memory_storage_provider_satisfies_session_persistence(self) -> None: - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider provider = MemoryStorageProvider() assert isinstance(provider, SessionPersistence) def test_memory_storage_provider_satisfies_checkpoint_store(self) -> None: - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider provider = MemoryStorageProvider() assert isinstance(provider, CheckpointStore) def test_memory_storage_provider_isinstance(self) -> None: - from agentpool_storage.memory_provider.provider import MemoryStorageProvider + from wolfharness_storage.memory_provider.provider import MemoryStorageProvider provider = MemoryStorageProvider() protocols = [ diff --git a/tests/storage/test_sql_checkpoint.py b/tests/storage/test_sql_checkpoint.py index ba0c7ea97..4871c8b1e 100644 --- a/tests/storage/test_sql_checkpoint.py +++ b/tests/storage/test_sql_checkpoint.py @@ -11,9 +11,9 @@ import pytest -from agentpool.sessions import SessionData -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.sql_provider import SQLModelProvider +from wolfharness.sessions import SessionData +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider if TYPE_CHECKING: diff --git a/tests/storage/test_sql_truncate_messages.py b/tests/storage/test_sql_truncate_messages.py index 30c0fd680..9f7822178 100644 --- a/tests/storage/test_sql_truncate_messages.py +++ b/tests/storage/test_sql_truncate_messages.py @@ -14,9 +14,9 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select -from agentpool_config.storage import SQLStorageConfig -from agentpool_storage.sql_provider.models import Message -from agentpool_storage.sql_provider.sql_provider import SQLModelProvider +from wolfharness_config.storage import SQLStorageConfig +from wolfharness_storage.sql_provider.models import Message +from wolfharness_storage.sql_provider.sql_provider import SQLModelProvider if TYPE_CHECKING: diff --git a/tests/team_mode/conftest.py b/tests/team_mode/conftest.py index 9f27f52ba..28165cefa 100644 --- a/tests/team_mode/conftest.py +++ b/tests/team_mode/conftest.py @@ -25,9 +25,9 @@ if TYPE_CHECKING: from pydantic_ai.messages import ModelMessage, ModelResponse, ToolReturnPart - from agentpool import AgentPool - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool_config.team_mode import TeamModeConfig + from wolfharness import AgentPool + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness_config.team_mode import TeamModeConfig # --------------------------------------------------------------------------- @@ -89,10 +89,10 @@ def build_agent_context( invoke ``TeamCommCapability`` methods without going through ``Agent.run()``. """ - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool.capabilities.runloop_delegation import RunLoopDelegationService - from agentpool.host.context import RunScope - from agentpool.host.registry import AgentRegistry + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.runloop_delegation import RunLoopDelegationService + from wolfharness.host.context import RunScope + from wolfharness.host.registry import AgentRegistry session_pool = pool.session_pool assert session_pool is not None @@ -200,7 +200,7 @@ def make_enabled_config( notice_delivery_mode: str = "steer", ) -> TeamModeConfig: """Create an enabled TeamModeConfig for testing.""" - from agentpool_config.team_mode import TeamModeConfig + from wolfharness_config.team_mode import TeamModeConfig return TeamModeConfig( enabled=True, @@ -250,7 +250,7 @@ def make_run_context( If ``session_pool`` is provided, ensures its async methods and sub-attributes are properly mocked for team tool operations. """ - from agentpool.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.agent_context import AgentContextDeps cfg = config or make_enabled_config(base_dir=base_dir) @@ -289,7 +289,7 @@ def init_team( members: list[dict[str, str]] | None = None, ) -> None: """Initialize a real FileTeamState with a team and registered members.""" - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState if members is None: members = [ diff --git a/tests/team_mode/test_display_e2e.py b/tests/team_mode/test_display_e2e.py index 51f38d457..108027d47 100644 --- a/tests/team_mode/test_display_e2e.py +++ b/tests/team_mode/test_display_e2e.py @@ -12,22 +12,22 @@ import pytest -from agentpool.agents.events.events import SpawnSessionStart, StreamCompleteEvent +from wolfharness.agents.events.events import SpawnSessionStart, StreamCompleteEvent @pytest.mark.unit @pytest.mark.asyncio async def test_e2e_team_member_display_name_propagation(): # noqa: PLR0915 """Verify display_name flows from SpawnSessionStart through ToolPart and session title.""" - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( MessageWithParts, Session, ToolPart, ) - from agentpool_server.opencode_server.opencode_event_bridge import ( + from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( OpenCodeMessageBridgeMixin, ) @@ -104,7 +104,7 @@ async def test_e2e_team_member_display_name_propagation(): # noqa: PLR0915 mock_session.title = "New Session" # Simulate pre-existing default title with patch( - "agentpool_server.opencode_server.opencode_event_bridge.ensure_session", + "wolfharness_server.opencode_server.opencode_event_bridge.ensure_session", new_callable=AsyncMock, return_value=mock_session, ): @@ -169,14 +169,14 @@ async def test_e2e_team_member_display_name_propagation(): # noqa: PLR0915 @pytest.mark.asyncio async def test_e2e_lead_display_name_propagation(): """Verify lead agent gets 'Lead' role in display name.""" - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( MessageWithParts, Session, ) - from agentpool_server.opencode_server.opencode_event_bridge import ( + from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( OpenCodeMessageBridgeMixin, ) @@ -233,7 +233,7 @@ async def test_e2e_lead_display_name_propagation(): mock_session.title = "New Session" with patch( - "agentpool_server.opencode_server.opencode_event_bridge.ensure_session", + "wolfharness_server.opencode_server.opencode_event_bridge.ensure_session", new_callable=AsyncMock, return_value=mock_session, ): @@ -250,14 +250,14 @@ async def test_e2e_lead_display_name_propagation(): @pytest.mark.asyncio async def test_e2e_regular_subagent_no_team_context(): """Verify non-team subagents don't get team prefix.""" - from agentpool_server.opencode_server.models import ( + from wolfharness_server.opencode_server.models import ( MessageWithParts, Session, ) - from agentpool_server.opencode_server.opencode_event_bridge import ( + from wolfharness_server.opencode_server.opencode_event_bridge import ( OpenCodeEventBridgeMixin, ) - from agentpool_server.opencode_server.opencode_message_bridge import ( + from wolfharness_server.opencode_server.opencode_message_bridge import ( OpenCodeMessageBridgeMixin, ) @@ -310,7 +310,7 @@ async def test_e2e_regular_subagent_no_team_context(): mock_session.title = "New Session" with patch( - "agentpool_server.opencode_server.opencode_event_bridge.ensure_session", + "wolfharness_server.opencode_server.opencode_event_bridge.ensure_session", new_callable=AsyncMock, return_value=mock_session, ): diff --git a/tests/team_mode/test_display_enrichment.py b/tests/team_mode/test_display_enrichment.py index 2f4ff1fc3..ae9f9b22e 100644 --- a/tests/team_mode/test_display_enrichment.py +++ b/tests/team_mode/test_display_enrichment.py @@ -9,7 +9,7 @@ import pytest -from agentpool.agents.events.events import SpawnSessionStart +from wolfharness.agents.events.events import SpawnSessionStart def _make_team_spawn_event( diff --git a/tests/team_mode/test_flow_budget.py b/tests/team_mode/test_flow_budget.py index 1628b3f01..947da02c4 100644 --- a/tests/team_mode/test_flow_budget.py +++ b/tests/team_mode/test_flow_budget.py @@ -21,17 +21,17 @@ import pytest import yamling -from agentpool import AgentPool, AgentsManifest -from agentpool.capabilities.file_team_state import FileTeamState -from agentpool.capabilities.team_comm_capability import TeamCommCapability from tests.team_mode.conftest import build_agent_context, make_mock_run_context +from wolfharness import AgentPool, AgentsManifest +from wolfharness.capabilities.file_team_state import FileTeamState +from wolfharness.capabilities.team_comm_capability import TeamCommCapability if TYPE_CHECKING: from collections.abc import AsyncIterator from pathlib import Path - from agentpool_config.team_mode import TeamModeConfig + from wolfharness_config.team_mode import TeamModeConfig # --------------------------------------------------------------------------- diff --git a/tests/team_mode/test_flow_error_containment.py b/tests/team_mode/test_flow_error_containment.py index d6489aade..255d4d9cf 100644 --- a/tests/team_mode/test_flow_error_containment.py +++ b/tests/team_mode/test_flow_error_containment.py @@ -16,14 +16,14 @@ from pydantic_ai.messages import ToolReturn import pytest -from agentpool.capabilities.file_team_state import FileTeamState -from agentpool.capabilities.team_comm_capability import TeamCommCapability from tests.team_mode.conftest import build_agent_context, make_mock_run_context +from wolfharness.capabilities.file_team_state import FileTeamState +from wolfharness.capabilities.team_comm_capability import TeamCommCapability if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool_config.team_mode import TeamModeConfig + from wolfharness import AgentPool + from wolfharness_config.team_mode import TeamModeConfig # --------------------------------------------------------------------------- diff --git a/tests/team_mode/test_flow_lifecycle.py b/tests/team_mode/test_flow_lifecycle.py index f849d3665..1423a5932 100644 --- a/tests/team_mode/test_flow_lifecycle.py +++ b/tests/team_mode/test_flow_lifecycle.py @@ -16,8 +16,8 @@ from pydantic_ai.models.function import AgentInfo, FunctionModel import pytest -from agentpool.capabilities.team_comm_capability import TeamCommCapability -from agentpool_config.team_mode import TeamModeConfig +from wolfharness.capabilities.team_comm_capability import TeamCommCapability +from wolfharness_config.team_mode import TeamModeConfig # --------------------------------------------------------------------------- @@ -85,7 +85,7 @@ def _make_run_context( Returns: A MagicMock whose .deps is a mock AgentContextDeps. """ - from agentpool.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.agent_context import AgentContextDeps cfg = config or _make_enabled_config(base_dir=base_dir) @@ -109,7 +109,7 @@ def _init_team( members: list[dict[str, str]] | None = None, ) -> None: """Initialize a real FileTeamState with a team and registered members.""" - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState if members is None: members = [ diff --git a/tests/team_mode/test_flow_role_aware.py b/tests/team_mode/test_flow_role_aware.py index d59df3215..d379e0eac 100644 --- a/tests/team_mode/test_flow_role_aware.py +++ b/tests/team_mode/test_flow_role_aware.py @@ -17,13 +17,13 @@ from pydantic_ai.tools import ToolDefinition import pytest -from agentpool.capabilities.team_comm_capability import TeamCommCapability from tests.team_mode.conftest import build_agent_context, make_mock_run_context +from wolfharness.capabilities.team_comm_capability import TeamCommCapability if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool_config.team_mode import TeamModeConfig + from wolfharness import AgentPool + from wolfharness_config.team_mode import TeamModeConfig # --------------------------------------------------------------------------- diff --git a/tests/team_mode/test_integration_collab_flow.py b/tests/team_mode/test_integration_collab_flow.py index 53e72e648..8667ebe37 100644 --- a/tests/team_mode/test_integration_collab_flow.py +++ b/tests/team_mode/test_integration_collab_flow.py @@ -11,7 +11,6 @@ import pytest -from agentpool.capabilities.team_comm_capability import TeamCommCapability from tests.team_mode.conftest import ( init_team, make_enabled_config, @@ -21,6 +20,7 @@ make_mock_registry, make_run_context, ) +from wolfharness.capabilities.team_comm_capability import TeamCommCapability if TYPE_CHECKING: @@ -192,7 +192,7 @@ async def test_send_message_with_persist_to_blackboard_integration( assert "Persisted to blackboard key 'critical_findings'" in result.return_value # Verify blackboard was actually written. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) bb = team_state.read_blackboard("team_123", "critical_findings") @@ -256,7 +256,7 @@ async def test_batch_creation_with_hash_and_symbolic_refs(tmp_path: Path) -> Non assert "Created 3 tasks" in result.return_value # Verify dependencies were resolved. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) tasks = team_state.list_tasks("team_123") @@ -472,7 +472,7 @@ async def test_agent_in_the_loop_team_tools(team_mode_pool: Any) -> None: ] # Collect all messages from the agent run result. - from agentpool.agents.events.events import StreamCompleteEvent + from wolfharness.agents.events.events import StreamCompleteEvent final_message = None for e in events: diff --git a/tests/team_mode/test_integration_defaults.py b/tests/team_mode/test_integration_defaults.py index b5e079291..51342fcbc 100644 --- a/tests/team_mode/test_integration_defaults.py +++ b/tests/team_mode/test_integration_defaults.py @@ -12,8 +12,8 @@ import pytest -from agentpool.capabilities.team_comm_capability import TeamCommCapability -from agentpool_config.team_mode import MemberSpec, TeamDefaultsConfig, TeamModeConfig +from wolfharness.capabilities.team_comm_capability import TeamCommCapability +from wolfharness_config.team_mode import MemberSpec, TeamDefaultsConfig, TeamModeConfig def _make_defaults_config(base_dir: str) -> TeamModeConfig: @@ -42,7 +42,7 @@ def _make_run_context( session_id: str = "lead_session_001", ) -> MagicMock: """Create a mock RunContext with AgentContextDeps deps for integration tests.""" - from agentpool.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.agent_context import AgentContextDeps agent_ctx = MagicMock(spec=AgentContextDeps) agent_ctx.session.metadata = metadata @@ -64,7 +64,7 @@ async def test_team_create_with_config_default_members(tmp_path: Any) -> None: When: team_create is called with empty members. Then: uses defaults.members to create the team with child sessions. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState config = _make_defaults_config(str(tmp_path)) @@ -166,7 +166,7 @@ async def test_team_create_config_default_members_then_delete(tmp_path: Any) -> When: team_delete is called afterwards. Then: team is successfully deleted. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState config = _make_defaults_config(str(tmp_path)) diff --git a/tests/team_mode/test_integration_mode.py b/tests/team_mode/test_integration_mode.py index 308ba1980..d69348c15 100644 --- a/tests/team_mode/test_integration_mode.py +++ b/tests/team_mode/test_integration_mode.py @@ -13,19 +13,19 @@ import pytest import yamling -from agentpool.capabilities.file_team_state import FileTeamState -from agentpool.capabilities.team_comm_capability import TeamCommCapability -from agentpool.models.manifest import AgentsManifest -from agentpool_config.team_mode import ( +from tests.team_mode.conftest import build_agent_context, make_mock_run_context +from wolfharness.capabilities.file_team_state import FileTeamState +from wolfharness.capabilities.team_comm_capability import TeamCommCapability +from wolfharness.models.manifest import AgentsManifest +from wolfharness_config.team_mode import ( MemberSpec, TeamDefaultsConfig, TeamModeConfig, ) -from tests.team_mode.conftest import build_agent_context, make_mock_run_context if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool # --------------------------------------------------------------------------- @@ -103,7 +103,7 @@ def _make_run_context( session_id: Session ID string for the mock SessionState. delegation: Mock DelegationService (defaults to a generic MagicMock). """ - from agentpool.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.agent_context import AgentContextDeps cfg = config or _make_enabled_config(base_dir=base_dir) diff --git a/tests/team_mode/test_integration_parallel.py b/tests/team_mode/test_integration_parallel.py index c789e1108..09f6cd7fd 100644 --- a/tests/team_mode/test_integration_parallel.py +++ b/tests/team_mode/test_integration_parallel.py @@ -20,13 +20,13 @@ from pydantic_ai.models.function import AgentInfo, FunctionModel import pytest -from agentpool.capabilities.team_comm_capability import TeamCommCapability from tests.team_mode.conftest import build_agent_context, make_mock_run_context +from wolfharness.capabilities.team_comm_capability import TeamCommCapability if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool_config.team_mode import TeamModeConfig + from wolfharness import AgentPool + from wolfharness_config.team_mode import TeamModeConfig # --------------------------------------------------------------------------- diff --git a/tests/team_mode/test_integration_session.py b/tests/team_mode/test_integration_session.py index 791c1c373..9355db4ca 100644 --- a/tests/team_mode/test_integration_session.py +++ b/tests/team_mode/test_integration_session.py @@ -21,18 +21,18 @@ import pytest import yamling -from agentpool import AgentPool, AgentsManifest -from agentpool.capabilities.agent_context import AgentContextDeps -from agentpool.capabilities.runloop_delegation import RunLoopDelegationService -from agentpool.capabilities.team_comm_capability import TeamCommCapability -from agentpool.host.context import RunScope -from agentpool.host.registry import AgentRegistry -from agentpool.orchestrator.core import SessionState # noqa: TC001 -from agentpool.orchestrator.run import RunHandle +from wolfharness import AgentPool, AgentsManifest +from wolfharness.capabilities.agent_context import AgentContextDeps +from wolfharness.capabilities.runloop_delegation import RunLoopDelegationService +from wolfharness.capabilities.team_comm_capability import TeamCommCapability +from wolfharness.host.context import RunScope +from wolfharness.host.registry import AgentRegistry +from wolfharness.orchestrator.core import SessionState # noqa: TC001 +from wolfharness.orchestrator.run import RunHandle if TYPE_CHECKING: - from agentpool_config.team_mode import TeamModeConfig + from wolfharness_config.team_mode import TeamModeConfig def _make_manifest( @@ -205,7 +205,7 @@ async def test_member_sessions_closed_when_lead_idle_and_no_active_runs( assert "Team 'test_team' created with 2 members" in create_result.return_value # Extract member session IDs from team state (exclude lead). - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_id = create_result.return_value.split("team_id=")[1].strip() team_state = FileTeamState(str(tmp_path)) @@ -304,7 +304,7 @@ async def test_cleanup_deferred_when_lead_has_active_run( ) assert "Team 'test_team' created with 1 members" in create_result.return_value - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_id = create_result.return_value.split("team_id=")[1].strip() team_state = FileTeamState(str(tmp_path)) @@ -389,7 +389,7 @@ async def test_cleanup_deferred_when_member_has_active_run( ) assert "Team 'test_team' created with 1 members" in create_result.return_value - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_id = create_result.return_value.split("team_id=")[1].strip() team_state = FileTeamState(str(tmp_path)) diff --git a/tests/team_mode/test_live.py b/tests/team_mode/test_live.py index 396069f11..f41bde429 100644 --- a/tests/team_mode/test_live.py +++ b/tests/team_mode/test_live.py @@ -79,7 +79,7 @@ def _create_test_config(tmp_path: Path) -> Path: def _collect_tool_names(events: list[Any]) -> list[str]: """Extract tool names from ``ToolCallStartEvent`` entries.""" - from agentpool.agents.events.events import ToolCallStartEvent + from wolfharness.agents.events.events import ToolCallStartEvent return [e.tool_name for e in events if isinstance(e, ToolCallStartEvent)] @@ -88,7 +88,7 @@ def _collect_text(events: list[Any]) -> str: """Concatenate all text deltas from ``PartDeltaEvent`` entries.""" from pydantic_ai import TextPartDelta, ThinkingPartDelta - from agentpool.agents.events.events import PartDeltaEvent + from wolfharness.agents.events.events import PartDeltaEvent parts: list[str] = [] for e in events: @@ -148,7 +148,7 @@ async def test_live_full_team_lifecycle(tmp_path: Path) -> None: Then: all seven team tools are invoked and the final response contains "ALL TESTS PASSED". """ - from agentpool.delegation.pool import AgentPool + from wolfharness.delegation.pool import AgentPool config_path = _create_test_config(tmp_path) @@ -186,7 +186,7 @@ async def test_live_member_session_cleanup(tmp_path: Path) -> None: Then: after the run completes, no active child sessions with ``parent_session_id`` matching the lead session remain. """ - from agentpool.delegation.pool import AgentPool + from wolfharness.delegation.pool import AgentPool config_path = _create_test_config(tmp_path) @@ -232,7 +232,7 @@ async def test_live_team_status_reflects_members(tmp_path: Path) -> None: Then: the ``team_create`` and ``team_status`` tools are invoked, and the response text mentions both member names. """ - from agentpool.delegation.pool import AgentPool + from wolfharness.delegation.pool import AgentPool config_path = _create_test_config(tmp_path) diff --git a/tests/team_mode/test_unit_batch_progress.py b/tests/team_mode/test_unit_batch_progress.py index 5c5f0e662..e83206af6 100644 --- a/tests/team_mode/test_unit_batch_progress.py +++ b/tests/team_mode/test_unit_batch_progress.py @@ -11,7 +11,7 @@ import pytest -from agentpool.capabilities.file_team_state import ( +from wolfharness.capabilities.file_team_state import ( FileTeamState, TaskRecord, format_owner_summary, @@ -180,7 +180,7 @@ def test_batch_exceeding_max_tasks_atomic_failure( When: create_tasks_batch would exceed _MAX_TASKS. Then: ValueError raised with max tasks message. """ - from agentpool.capabilities.file_team_state import _MAX_TASKS + from wolfharness.capabilities.file_team_state import _MAX_TASKS for i in range(_MAX_TASKS - 1): initialized_team.create_task("team-1", {"subject": f"Filler {i}"}) diff --git a/tests/team_mode/test_unit_capability.py b/tests/team_mode/test_unit_capability.py index 0d9b5c533..c625594c9 100644 --- a/tests/team_mode/test_unit_capability.py +++ b/tests/team_mode/test_unit_capability.py @@ -9,8 +9,8 @@ from pydantic_ai.tools import ToolDefinition import pytest -from agentpool.capabilities.team_comm_capability import TeamCommCapability -from agentpool_config.team_mode import TeamBounds, TeamModeConfig +from wolfharness.capabilities.team_comm_capability import TeamCommCapability +from wolfharness_config.team_mode import TeamBounds, TeamModeConfig # ---- Helpers ---- @@ -233,7 +233,7 @@ def _make_factory() -> Any: Returns an AgentFactory with a mock pool (the method under test does not access self._pool). """ - from agentpool.host.factory import AgentFactory + from wolfharness.host.factory import AgentFactory mock_pool = MagicMock() return AgentFactory(mock_pool) @@ -263,7 +263,7 @@ def _make_native_config(team_mode: Any = None) -> Any: Returns: A NativeAgentConfig with model='openai:test' and no tools. """ - from agentpool.models.agents import NativeAgentConfig + from wolfharness.models.agents import NativeAgentConfig return NativeAgentConfig( name="test_agent", @@ -452,7 +452,7 @@ def _make_run_context( Returns: A MagicMock whose .deps is a mock AgentContextDeps. """ - from agentpool.capabilities.agent_context import AgentContextDeps + from wolfharness.capabilities.agent_context import AgentContextDeps cfg = config or _make_enabled_config(base_dir=base_dir) @@ -501,7 +501,7 @@ def _make_lead_metadata(team_id: str = "team_123") -> dict[str, Any]: def _init_team(base_dir: str, team_id: str = "team_123") -> None: """Initialize a real FileTeamState with a team and members.""" - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState state = FileTeamState(base_dir) state.init( @@ -650,7 +650,7 @@ async def test_send_message_uses_steer_by_default(tmp_path: Any) -> None: When: send_message is called. Then: session_pool.send_message called with DeliveryMode.STEER. """ - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode _init_team(str(tmp_path)) mock_pool = MagicMock() @@ -758,7 +758,7 @@ async def test_bounds_started_at_recorded(tmp_path: Any) -> None: When: team_create completes. Then: state.json contains a 'started_at' field with an ISO timestamp. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState config = _make_enabled_config( member_eligible=["worker", "reviewer"], @@ -1560,7 +1560,7 @@ async def test_shutdown_request_success(tmp_path: Any) -> None: assert result.return_value == "Shutdown completed for translator_agent" mock_pool.close_session.assert_awaited_once_with("sess_translator") # Verify member is removed from the members dict entirely (hard remove). - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) state = team_state._read_json(team_state._state_path("team_123")) @@ -1688,7 +1688,7 @@ async def test_send_message_queue_mode(tmp_path: Any) -> None: When: send_message is called. Then: DeliveryMode.QUEUE is used. """ - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode _init_team(str(tmp_path)) mock_pool = MagicMock() @@ -1803,7 +1803,7 @@ async def test_team_create_uses_config_default_members(tmp_path: Any) -> None: When: team_create is called with members=[]. Then: uses defaults.members from config to create the team. """ - from agentpool_config.team_mode import MemberSpec, TeamDefaultsConfig + from wolfharness_config.team_mode import MemberSpec, TeamDefaultsConfig config = _make_enabled_config( member_eligible=["translator", "reviewer"], @@ -1850,9 +1850,9 @@ async def test_resolve_agent_context_from_runtime_context(tmp_path: Any) -> None The tool functions receive ctx.deps = agents.context.AgentContext, and our capabilities.agent_context.AgentContextDeps is at ctx.deps.data. """ - from agentpool.agents.context import AgentContext as RuntimeAgentContext - from agentpool.capabilities.agent_context import AgentContextDeps - from agentpool.orchestrator.session_controller import SessionState + from wolfharness.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.capabilities.agent_context import AgentContextDeps + from wolfharness.orchestrator.session_controller import SessionState # Create our capabilities AgentContextDeps (the frozen dataclass). session = SessionState(session_id="test-session-123", agent_name="test_agent") @@ -2165,7 +2165,7 @@ async def test_team_add_member_success(tmp_path: Any) -> None: assert mock_pool.send_message.await_count == 3 # Verify agent field in team state is the actual agent type, not the member name. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) state = team_state._read_json(team_state._state_path("team_123")) @@ -2277,7 +2277,7 @@ async def test_team_add_member_ephemeral(tmp_path: Any) -> None: assert result.return_value == "Member 'temp_member' added to team (lifecycle=ephemeral)" # Verify the member was registered in team state. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) sid = team_state.get_member_session_id("team_123", "temp_member") @@ -2309,7 +2309,7 @@ async def test_shutdown_request_removes_member(tmp_path: Any) -> None: assert result.return_value == "Shutdown completed for translator_agent" mock_pool.close_session.assert_awaited_once_with("sess_translator") # Verify member removed from team state (hard remove, not soft shutdown). - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) sid = team_state.get_member_session_id("team_123", "translator_agent") @@ -2382,7 +2382,7 @@ async def test_team_add_member_non_ascii_name(tmp_path: Any) -> None: assert "added to team" in result.return_value # Verify blackboard key was sanitized (non-ASCII chars replaced with _). - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState safe_name = re.sub(r"[^a-zA-Z0-9_]", "_", "推理员B") team_state = FileTeamState(str(tmp_path)) @@ -2401,7 +2401,7 @@ async def test_team_add_member_hyphen_in_name(tmp_path: Any) -> None: assert "added to team" in result.return_value # Verify blackboard key was sanitized (hyphen replaced with _). - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState safe_name = re.sub(r"[^a-zA-Z0-9_]", "_", "logician-B") team_state = FileTeamState(str(tmp_path)) @@ -2415,7 +2415,7 @@ async def test_team_add_member_agent_field_correct(tmp_path: Any) -> None: cap, ctx, _mock_pool, _mock_delegation, _mock_registry = _make_add_member_setup(tmp_path) await cap.team_add_member(ctx, "my_member", "editor") - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) state = team_state._read_json(team_state._state_path("team_123")) @@ -2430,7 +2430,7 @@ async def test_team_add_member_roster_includes_work_summary(tmp_path: Any) -> No When: team_add_member is called. Then: the initial prompt to the new member includes work-status for each member. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState cap, ctx, mock_pool, _mock_delegation, _mock_registry = _make_add_member_setup(tmp_path) @@ -2536,7 +2536,7 @@ async def test_shutdown_request_non_ascii_name(tmp_path: Any) -> None: assert "Shutdown completed" in result.return_value # Verify blackboard was written with sanitized key. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState safe_name = re.sub(r"[^a-zA-Z0-9_]", "_", "推理员") team_state = FileTeamState(str(tmp_path)) @@ -2590,7 +2590,7 @@ async def test_send_message_to_nonexistent_member_no_phantom(tmp_path: Any) -> N assert "not found" in result.return_value # Verify NO phantom entry was created in team state. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) state = team_state._read_json(team_state._state_path("team_123")) @@ -3452,7 +3452,7 @@ async def test_after_run_reminder_for_unfinished_tasks(tmp_path: Any) -> None: Then: routes a reminder message to the member's own session via session_pool.send_message with QUEUE mode. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState _init_team(str(tmp_path)) team_state = FileTeamState(str(tmp_path)) @@ -3489,7 +3489,7 @@ async def test_after_run_reminder_for_unfinished_tasks(tmp_path: Any) -> None: # Verify QUEUE mode was used. call_args = mock_pool.send_message.await_args call_kwargs = call_args.kwargs - from agentpool.lifecycle.types import DeliveryMode + from wolfharness.lifecycle.types import DeliveryMode assert call_kwargs.get("mode") is DeliveryMode.QUEUE # Verify message content (2nd positional arg) mentions the unfinished task. @@ -3507,7 +3507,7 @@ async def test_after_run_no_reminder_for_lead(tmp_path: Any) -> None: When: after_run fires. Then: no reminder is sent (lead doesn't get task reminders). """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState _init_team(str(tmp_path)) team_state = FileTeamState(str(tmp_path)) @@ -3547,7 +3547,7 @@ async def test_after_run_no_reminder_when_session_closing(tmp_path: Any) -> None When: after_run fires. Then: no reminder is sent (shutdown path handles notification instead). """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState _init_team(str(tmp_path)) team_state = FileTeamState(str(tmp_path)) @@ -3588,7 +3588,7 @@ async def test_after_run_no_duplicate_reminder(tmp_path: Any) -> None: When: after_run fires again. Then: no second reminder is sent (max 1 per session). """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState _init_team(str(tmp_path)) team_state = FileTeamState(str(tmp_path)) @@ -3631,7 +3631,7 @@ async def test_after_run_no_reminder_when_no_unfinished_tasks(tmp_path: Any) -> When: after_run fires. Then: no reminder is sent. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState _init_team(str(tmp_path)) team_state = FileTeamState(str(tmp_path)) @@ -3678,7 +3678,7 @@ async def test_shutdown_request_warns_about_unfinished_tasks(tmp_path: Any) -> N When: shutdown_request is called. Then: return value includes a warning about the unfinished task. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState _init_team(str(tmp_path)) team_state = FileTeamState(str(tmp_path)) @@ -3719,7 +3719,7 @@ async def test_shutdown_request_no_warning_when_tasks_completed(tmp_path: Any) - When: shutdown_request is called. Then: return value is the standard message with no warning. """ - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState _init_team(str(tmp_path)) team_state = FileTeamState(str(tmp_path)) diff --git a/tests/team_mode/test_unit_collab_flow.py b/tests/team_mode/test_unit_collab_flow.py index 448c14fac..73f531a46 100644 --- a/tests/team_mode/test_unit_collab_flow.py +++ b/tests/team_mode/test_unit_collab_flow.py @@ -12,7 +12,6 @@ import pytest -from agentpool.capabilities.team_comm_capability import TeamCommCapability from tests.team_mode.conftest import ( init_team, make_enabled_config, @@ -21,6 +20,7 @@ make_mock_pool, make_run_context, ) +from wolfharness.capabilities.team_comm_capability import TeamCommCapability if TYPE_CHECKING: @@ -686,7 +686,7 @@ async def test_send_message_with_persist_to_blackboard(tmp_path: Path) -> None: assert "Message sent to reviewer_agent" in result.return_value assert "Persisted to blackboard key 'findings'" in result.return_value # Verify blackboard was written. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) bb = team_state.read_blackboard("team_123", "findings") @@ -1322,7 +1322,7 @@ async def test_task_create_batch_exceeding_max_tasks(tmp_path: Path) -> None: cap = TeamCommCapability(config, "coordinator", make_lead_metadata()) # Create tasks to near the limit (100 max). - from agentpool.capabilities.file_team_state import _MAX_TASKS, FileTeamState + from wolfharness.capabilities.file_team_state import _MAX_TASKS, FileTeamState team_state = FileTeamState(str(tmp_path)) for i in range(_MAX_TASKS - 1): @@ -1361,7 +1361,7 @@ async def test_task_create_batch_with_progress_total(tmp_path: Path) -> None: task_id = task_id_line.split("-> ")[1].strip() # Verify progress_total was stored. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState state = FileTeamState(str(tmp_path)) task = state.get_task("team_123", task_id) @@ -1438,7 +1438,7 @@ async def test_shutdown_request_success(tmp_path: Path) -> None: mock_pool.close_session.assert_awaited_once() # Verify member removed from team state. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) state_path = team_state._state_path("team_123") @@ -1513,7 +1513,7 @@ async def test_delete_blackboard_success(tmp_path: Path) -> None: await cap.write_blackboard(ctx, "test_key", "test_value") # Verify it exists. - from agentpool.capabilities.file_team_state import FileTeamState + from wolfharness.capabilities.file_team_state import FileTeamState team_state = FileTeamState(str(tmp_path)) assert team_state.read_blackboard("team_123", "test_key") is not None @@ -1577,7 +1577,7 @@ async def test_team_create_exceeds_max_members(tmp_path: Path) -> None: When: team_create is called with 3 members. Then: error returned about exceeding max_members. """ - from agentpool_config.team_mode import TeamBounds + from wolfharness_config.team_mode import TeamBounds config = make_enabled_config(base_dir=str(tmp_path)) config = config.model_copy(update={"bounds": TeamBounds(max_members=2)}) @@ -1614,7 +1614,7 @@ async def test_team_add_member_exceeds_max_members(tmp_path: Path) -> None: When: team_add_member is called to add a 3rd member. Then: error returned about exceeding max_members. """ - from agentpool_config.team_mode import TeamBounds + from wolfharness_config.team_mode import TeamBounds config = make_enabled_config( member_eligible=["worker", "reviewer", "editor"], diff --git a/tests/team_mode/test_unit_config.py b/tests/team_mode/test_unit_config.py index 23d6a0739..11975e002 100644 --- a/tests/team_mode/test_unit_config.py +++ b/tests/team_mode/test_unit_config.py @@ -5,7 +5,7 @@ from pydantic import ValidationError import pytest -from agentpool_config.team_mode import ( +from wolfharness_config.team_mode import ( BlackboardConfig, MemberSpec, TeamBounds, diff --git a/tests/team_mode/test_unit_file_state.py b/tests/team_mode/test_unit_file_state.py index 0965bfef4..d61fa369e 100644 --- a/tests/team_mode/test_unit_file_state.py +++ b/tests/team_mode/test_unit_file_state.py @@ -13,7 +13,7 @@ import pytest -from agentpool.capabilities.file_team_state import ( +from wolfharness.capabilities.file_team_state import ( FileTeamState, TaskRecord, format_task_xml, diff --git a/tests/teams/test_parallel_agents.py b/tests/teams/test_parallel_agents.py index 807fe3723..08238a5ae 100644 --- a/tests/teams/test_parallel_agents.py +++ b/tests/teams/test_parallel_agents.py @@ -7,8 +7,8 @@ from pydantic import BaseModel import pytest -from agentpool import AgentPool, AgentsManifest -from agentpool.delegation.base_team import BaseTeam +from wolfharness import AgentPool, AgentsManifest +from wolfharness.delegation.base_team import BaseTeam pytestmark = pytest.mark.integration diff --git a/tests/teams/test_team.py b/tests/teams/test_team.py index b6951b5f3..6c0650910 100644 --- a/tests/teams/test_team.py +++ b/tests/teams/test_team.py @@ -6,8 +6,8 @@ import pytest -from agentpool import Agent, ChatMessage -from agentpool.delegation.base_team import BaseTeam +from wolfharness import Agent, ChatMessage +from wolfharness.delegation.base_team import BaseTeam pytestmark = pytest.mark.unit diff --git a/tests/teams/test_team_run.py b/tests/teams/test_team_run.py index 903f8e7fc..4c058191d 100644 --- a/tests/teams/test_team_run.py +++ b/tests/teams/test_team_run.py @@ -6,9 +6,9 @@ import anyio import pytest -from agentpool import Agent, ChatMessage -from agentpool.utils.model_helpers import function_to_model -from agentpool.utils.time_utils import get_now +from wolfharness import Agent, ChatMessage +from wolfharness.utils.model_helpers import function_to_model +from wolfharness.utils.time_utils import get_now pytestmark = pytest.mark.integration diff --git a/tests/teams/test_team_streaming.py b/tests/teams/test_team_streaming.py index a1bbb996f..153f53808 100644 --- a/tests/teams/test_team_streaming.py +++ b/tests/teams/test_team_streaming.py @@ -17,21 +17,21 @@ import pytest -from agentpool import Agent -from agentpool.agents.events import ( +from wolfharness import Agent +from wolfharness.agents.events import ( SpawnSessionStart, StreamCompleteEvent, SubAgentEvent, ) -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError -from agentpool.delegation.base_team import BaseTeam -from agentpool.messaging import ChatMessage +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness.delegation.base_team import BaseTeam +from wolfharness.messaging import ChatMessage pytestmark = pytest.mark.integration -pytestmark = pytest.mark.filterwarnings("ignore::DeprecationWarning:agentpool.agents.base_agent") +pytestmark = pytest.mark.filterwarnings("ignore::DeprecationWarning:wolfharness.agents.base_agent") # ============================================================================ @@ -43,7 +43,7 @@ def _make_echo_agent(name: str, response: str = "hello") -> Agent[Any, str]: """Create an Agent that echoes a fixed response via function_to_model.""" from functools import partial - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def _echo(_msg: str, *, _response: str = response) -> str: return _response @@ -84,7 +84,7 @@ async def test_team_run_stream_depth_guard() -> None: async def test_team_run_stream_depth_at_limit_ok() -> None: """Team.run_stream() should NOT raise at depth = MAX - 1.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -104,7 +104,7 @@ async def echo(msg: str) -> str: async def test_team_run_stream_emits_spawn_session_start() -> None: """Each member should emit SpawnSessionStart before SubAgentEvent content.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -133,7 +133,7 @@ async def echo(msg: str) -> str: async def test_spawn_session_start_precedes_subagent_for_member() -> None: """For each member, SpawnSessionStart should appear before any SubAgentEvent.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -164,7 +164,7 @@ async def echo(msg: str) -> str: async def test_subagent_event_preserves_session_ids() -> None: """SubAgentEvent should carry child_session_id and parent_session_id.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -189,7 +189,7 @@ async def echo(msg: str) -> str: async def test_spawn_session_start_carries_session_ids() -> None: """SpawnSessionStart should carry child_session_id and parent_session_id.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -209,7 +209,7 @@ async def echo(msg: str) -> str: async def test_out_of_pool_team_generates_session_ids() -> None: """Team without pool should generate session IDs and not crash.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -239,7 +239,7 @@ async def echo(msg: str) -> str: @pytest.mark.skip(reason="L2 migration: requires mock internals — remains L1 unit test") async def test_pool_backed_team_creates_child_sessions() -> None: """Team with pool.sessions should call create_child_session for each member.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -298,7 +298,7 @@ async def echo(msg: str) -> str: async def test_team_kwargs_session_id_depth_popped() -> None: """Passing session_id/depth in kwargs should not cause duplicate keyword errors.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -324,7 +324,7 @@ async def echo(msg: str) -> str: async def test_team_run_unchanged() -> None: """Team.run() should not be affected by run_stream() changes.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg @@ -341,7 +341,7 @@ async def echo(msg: str) -> str: async def test_nested_subagent_event_session_ids_preserved() -> None: """Nested SubAgentEvent IDs should be preserved when a member is itself a Team.""" - from agentpool.utils.model_helpers import function_to_model + from wolfharness.utils.model_helpers import function_to_model async def echo(msg: str) -> str: return msg diff --git a/tests/test_acp_modality_capabilities.py b/tests/test_acp_modality_capabilities.py index fca7c0cf9..86b99b0b6 100644 --- a/tests/test_acp_modality_capabilities.py +++ b/tests/test_acp_modality_capabilities.py @@ -12,8 +12,8 @@ import pytest -from agentpool.models.model_configs import BaseModelConfig, StringModelConfig -from agentpool_config.model_capabilities import ModelCapabilities +from wolfharness.models.model_configs import BaseModelConfig, StringModelConfig +from wolfharness_config.model_capabilities import ModelCapabilities pytestmark = pytest.mark.unit diff --git a/tests/test_agent_factory_modality.py b/tests/test_agent_factory_modality.py index e393329fd..3930104a2 100644 --- a/tests/test_agent_factory_modality.py +++ b/tests/test_agent_factory_modality.py @@ -14,20 +14,20 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.agents.native_agent.agent import ( +from wolfharness.agents.native_agent.agent import ( Agent, _intersect_capabilities, _model_config_names, ) -from agentpool.capabilities.modality_filter import ModalityFilterCapability -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.model_configs import ( +from wolfharness.capabilities.modality_filter import ModalityFilterCapability +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.model_configs import ( FallbackModelConfig, OpenAIModelConfig, StringModelConfig, TestModelConfig as _TestModelConfig, ) -from agentpool_config.model_capabilities import ModelCapabilities +from wolfharness_config.model_capabilities import ModelCapabilities pytestmark = [pytest.mark.unit] @@ -305,7 +305,7 @@ async def test_resolve_capabilities_called_for_string_model() -> None: # Mock resolve_capabilities to avoid real tokonomics calls. mock_caps = _text_only_caps() with patch( - "agentpool.host.stubs.resolve_capabilities", + "wolfharness.host.stubs.resolve_capabilities", new_callable=AsyncMock, return_value=mock_caps, ) as mock_resolve: @@ -332,7 +332,7 @@ async def test_fallback_model_does_not_call_resolve_per_sub_model() -> None: agent = Agent(name="test", model="test", agent_config=config) with patch( - "agentpool.host.stubs.resolve_capabilities", + "wolfharness.host.stubs.resolve_capabilities", new_callable=AsyncMock, ) as mock_resolve: await agent.get_agentlet(model=None, output_type=str) @@ -366,7 +366,7 @@ async def test_fallback_model_user_filter_gets_declared_caps() -> None: ) with patch( - "agentpool.host.stubs.resolve_capabilities", + "wolfharness.host.stubs.resolve_capabilities", new_callable=AsyncMock, ): pydantic_agent = await agent.get_agentlet(model=None, output_type=str) @@ -398,7 +398,7 @@ async def test_agent_creation_no_tokonomics_query_for_custom_model() -> None: agent = Agent(name="test", model="test", agent_config=config) with patch( - "agentpool.host.stubs.CapabilityCache.get_capability", + "wolfharness.host.stubs.CapabilityCache.get_capability", new_callable=AsyncMock, ) as mock_get: pydantic_agent = await agent.get_agentlet(model=None, output_type=str) @@ -416,7 +416,7 @@ async def test_agent_creation_no_tokonomics_query_for_custom_model() -> None: @pytest.mark.integration async def test_agent_creation_uses_cached_values() -> None: """Agent creation should use cached capability values when available.""" - from agentpool.host.stubs import _get_default_cache + from wolfharness.host.stubs import _get_default_cache cache = _get_default_cache() # Pre-populate cache for the model. diff --git a/tests/test_capability_cache.py b/tests/test_capability_cache.py index 1d0a0d501..2275c5aaf 100644 --- a/tests/test_capability_cache.py +++ b/tests/test_capability_cache.py @@ -11,8 +11,8 @@ import pytest -from agentpool.host.stubs import CapabilityCache, resolve_capabilities -from agentpool_config.model_capabilities import ModelCapabilities +from wolfharness.host.stubs import CapabilityCache, resolve_capabilities +from wolfharness_config.model_capabilities import ModelCapabilities pytestmark = pytest.mark.unit diff --git a/tests/test_config/test_package_skill_scopes.py b/tests/test_config/test_package_skill_scopes.py index bb463ecc6..30d7fb6d8 100644 --- a/tests/test_config/test_package_skill_scopes.py +++ b/tests/test_config/test_package_skill_scopes.py @@ -3,14 +3,14 @@ import pytest import yaml -from agentpool_config import resolution +from wolfharness_config import resolution pytestmark = pytest.mark.unit def test_include_package_records_package_skill_scopes(tmp_path, monkeypatch): - config_path = tmp_path / "agentpool.yaml" + config_path = tmp_path / "wolfharness.yaml" config_path.write_text( yaml.safe_dump( { diff --git a/tests/test_event_bus_conditional_replay.py b/tests/test_event_bus_conditional_replay.py index 4bc08b5e7..fda36bf05 100644 --- a/tests/test_event_bus_conditional_replay.py +++ b/tests/test_event_bus_conditional_replay.py @@ -15,11 +15,11 @@ from pydantic_ai import TextPartDelta import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent, RunStartedEvent, ) -from agentpool.orchestrator.core import ( +from wolfharness.orchestrator.core import ( EventBus, drain_and_merge, ) diff --git a/tests/test_event_bus_mechanics.py b/tests/test_event_bus_mechanics.py index 6c36d1b30..50ae94f32 100644 --- a/tests/test_event_bus_mechanics.py +++ b/tests/test_event_bus_mechanics.py @@ -18,11 +18,11 @@ import pytest -from agentpool.agents.events import RunStartedEvent, StateUpdate -from agentpool.lifecycle.comm_channel import ProtocolChannel -from agentpool.lifecycle.journal import MemoryJournal -from agentpool.lifecycle.types import RunState -from agentpool.orchestrator.event_bus import EventBus, EventEnvelope, OverflowPolicy +from wolfharness.agents.events import RunStartedEvent, StateUpdate +from wolfharness.lifecycle.comm_channel import ProtocolChannel +from wolfharness.lifecycle.journal import MemoryJournal +from wolfharness.lifecycle.types import RunState +from wolfharness.orchestrator.event_bus import EventBus, EventEnvelope, OverflowPolicy pytestmark = [pytest.mark.unit, pytest.mark.anyio] diff --git a/tests/test_modality_e2e.py b/tests/test_modality_e2e.py index 9f6ed30e8..0c0e90773 100644 --- a/tests/test_modality_e2e.py +++ b/tests/test_modality_e2e.py @@ -22,14 +22,14 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.agents.native_agent.agent import Agent -from agentpool.capabilities.modality_filter import ModalityFilterCapability -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.model_configs import ( +from wolfharness.agents.native_agent.agent import Agent +from wolfharness.capabilities.modality_filter import ModalityFilterCapability +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.model_configs import ( FallbackModelConfig, TestModelConfig as _TestModelConfig, ) -from agentpool_config.model_capabilities import ModelCapabilities +from wolfharness_config.model_capabilities import ModelCapabilities pytestmark = [pytest.mark.integration] @@ -387,7 +387,7 @@ async def test_acp_agent_image_input_false_advertises_false() -> None: # The ACP agent derives image_prompts from # model_config.capabilities.image_input. We test this logic directly # by simulating the derivation that occurs in initialize(). - from agentpool.models.model_configs import BaseModelConfig + from wolfharness.models.model_configs import BaseModelConfig model_config = _TestModelConfig(capabilities=_text_only_caps()) assert isinstance(model_config, BaseModelConfig) diff --git a/tests/test_modality_filter.py b/tests/test_modality_filter.py index 988d518ea..de052647e 100644 --- a/tests/test_modality_filter.py +++ b/tests/test_modality_filter.py @@ -19,8 +19,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool.capabilities.modality_filter import ModalityFilterCapability -from agentpool_config.model_capabilities import ModelCapabilities +from wolfharness.capabilities.modality_filter import ModalityFilterCapability +from wolfharness_config.model_capabilities import ModelCapabilities pytestmark = pytest.mark.unit @@ -540,8 +540,8 @@ def test_get_ordering_wrapped_by_budget_and_dcp() -> None: assert ordering is not None assert isinstance(ordering, CapabilityOrdering) # wrapped_by means those capabilities are OUTER, self is INNER. - from agentpool.capabilities.dcp.capability import DynamicContextPruningCapability - from agentpool.capabilities.tool_output_budget import ToolOutputBudgetCapability + from wolfharness.capabilities.dcp.capability import DynamicContextPruningCapability + from wolfharness.capabilities.tool_output_budget import ToolOutputBudgetCapability assert ToolOutputBudgetCapability in ordering.wrapped_by assert DynamicContextPruningCapability in ordering.wrapped_by diff --git a/tests/test_modality_filter_config.py b/tests/test_modality_filter_config.py index 6bca8c54a..050e76215 100644 --- a/tests/test_modality_filter_config.py +++ b/tests/test_modality_filter_config.py @@ -15,7 +15,7 @@ from pydantic import TypeAdapter, ValidationError import pytest -from agentpool_config.capabilities import ( +from wolfharness_config.capabilities import ( KNOWN_CAPABILITY_TYPES, BuiltinCapabilityConfig, ModalityFilterCapabilityConfig, @@ -54,7 +54,7 @@ def test_build_capability_returns_modality_filter() -> None: """``build_capability()`` returns a ``ModalityFilterCapability`` instance.""" config = ModalityFilterCapabilityConfig() capability = build_capability(config) - from agentpool.capabilities.modality_filter import ModalityFilterCapability + from wolfharness.capabilities.modality_filter import ModalityFilterCapability assert isinstance(capability, ModalityFilterCapability) diff --git a/tests/test_modality_utils.py b/tests/test_modality_utils.py index 80eb08b74..9abee3495 100644 --- a/tests/test_modality_utils.py +++ b/tests/test_modality_utils.py @@ -12,7 +12,7 @@ ) import pytest -from agentpool.capabilities.modality_utils import ( +from wolfharness.capabilities.modality_utils import ( classify_binary_content, describe_multimodal_content, ) diff --git a/tests/test_model_capabilities.py b/tests/test_model_capabilities.py index 3e7e570fb..8120a0e60 100644 --- a/tests/test_model_capabilities.py +++ b/tests/test_model_capabilities.py @@ -4,7 +4,7 @@ import pytest -from agentpool.models.model_configs import ( +from wolfharness.models.model_configs import ( AnthropicModelConfig, BaseModelConfig, FallbackModelConfig, @@ -16,7 +16,7 @@ StringModelConfig, TestModelConfig, ) -from agentpool_config.model_capabilities import ModelCapabilities +from wolfharness_config.model_capabilities import ModelCapabilities pytestmark = pytest.mark.unit diff --git a/tests/test_opencode_display_propagation.py b/tests/test_opencode_display_propagation.py index 6632b230d..fc348f406 100644 --- a/tests/test_opencode_display_propagation.py +++ b/tests/test_opencode_display_propagation.py @@ -4,7 +4,7 @@ import pytest -from agentpool.agents.events.events import SpawnSessionStart +from wolfharness.agents.events.events import SpawnSessionStart @pytest.mark.unit @@ -60,7 +60,7 @@ def test_spawn_session_start_display_name_set_when_differs_from_source_name() -> @pytest.mark.unit def test_agent_model_has_display_name_field() -> None: """The OpenCode Agent model should have a display_name field.""" - from agentpool_server.opencode_server.models.agent import Agent + from wolfharness_server.opencode_server.models.agent import Agent agent = Agent(name="test_agent") assert agent.display_name is None @@ -74,7 +74,7 @@ def test_ensure_session_accepts_title_parameter() -> None: """ensure_session should accept an optional title parameter.""" import inspect - from agentpool_server.opencode_server.opencode_session_routes import ensure_session + from wolfharness_server.opencode_server.opencode_session_routes import ensure_session sig = inspect.signature(ensure_session) assert "title" in sig.parameters diff --git a/tests/test_opencode_provider_modality.py b/tests/test_opencode_provider_modality.py index 007e5e10e..8ca3bcd29 100644 --- a/tests/test_opencode_provider_modality.py +++ b/tests/test_opencode_provider_modality.py @@ -7,8 +7,8 @@ import pytest from tokonomics.model_discovery.model_info import ModelInfo, ModelPricing -from agentpool_config.model_capabilities import ModelCapabilities -from agentpool_server.opencode_server.models.provider import Model +from wolfharness_config.model_capabilities import ModelCapabilities +from wolfharness_server.opencode_server.models.provider import Model pytestmark = pytest.mark.unit diff --git a/tests/test_sse_conditional_replay_integration.py b/tests/test_sse_conditional_replay_integration.py index 8e23c1cd6..0ad07ebbe 100644 --- a/tests/test_sse_conditional_replay_integration.py +++ b/tests/test_sse_conditional_replay_integration.py @@ -18,10 +18,10 @@ import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunStartedEvent, ) -from agentpool.orchestrator.core import ( +from wolfharness.orchestrator.core import ( EventBus, ) @@ -219,7 +219,7 @@ async def test_user_messages_display_correctly_after_restart() -> None: session_id = "sess-int-4" # Simulate a user message with text and image parts - from agentpool.agents.events.events import CustomEvent + from wolfharness.agents.events.events import CustomEvent user_msg_parts = [ {"type": "text", "text": "Hello, analyze this image"}, diff --git a/tests/tool_impls/question/test_question_for_user.py b/tests/tool_impls/question/test_question_for_user.py index ff32571eb..e0c69ecad 100644 --- a/tests/tool_impls/question/test_question_for_user.py +++ b/tests/tool_impls/question/test_question_for_user.py @@ -9,8 +9,8 @@ from pydantic_ai import ModelRetry import pytest -from agentpool.tasks.exceptions import RunAbortedError -from agentpool_toolsets.builtin.question_tools import ( +from wolfharness.tasks.exceptions import RunAbortedError +from wolfharness_toolsets.builtin.question_tools import ( Question, QuestionTools, Suggest, diff --git a/tests/tool_impls/question/test_question_tool.py b/tests/tool_impls/question/test_question_tool.py index acac4f6d1..11f7cf92a 100644 --- a/tests/tool_impls/question/test_question_tool.py +++ b/tests/tool_impls/question/test_question_tool.py @@ -18,9 +18,9 @@ from pydantic_ai import ModelRetry import pytest -from agentpool.tasks.exceptions import RunAbortedError -from agentpool.tool_impls.question.tool import QuestionTool -from agentpool.tools.base import ToolResult +from wolfharness.tasks.exceptions import RunAbortedError +from wolfharness.tool_impls.question.tool import QuestionTool +from wolfharness.tools.base import ToolResult @pytest.mark.unit diff --git a/tests/tool_impls/test_tool_impls_imports.py b/tests/tool_impls/test_tool_impls_imports.py index c7f17c291..806756c94 100644 --- a/tests/tool_impls/test_tool_impls_imports.py +++ b/tests/tool_impls/test_tool_impls_imports.py @@ -15,15 +15,15 @@ TOOL_IMPLS_MODULES = [ - "agentpool.tool_impls.agent_cli", - "agentpool.tool_impls.bash", - "agentpool.tool_impls.delete_path", - "agentpool.tool_impls.download_file", - "agentpool.tool_impls.execute_code", - "agentpool.tool_impls.grep", - "agentpool.tool_impls.list_directory", - "agentpool.tool_impls.question", - "agentpool.tool_impls.read", + "wolfharness.tool_impls.agent_cli", + "wolfharness.tool_impls.bash", + "wolfharness.tool_impls.delete_path", + "wolfharness.tool_impls.download_file", + "wolfharness.tool_impls.execute_code", + "wolfharness.tool_impls.grep", + "wolfharness.tool_impls.list_directory", + "wolfharness.tool_impls.question", + "wolfharness.tool_impls.read", ] diff --git a/tests/tools/test_deferred_exports.py b/tests/tools/test_deferred_exports.py index 8f3ad3834..186c66ad4 100644 --- a/tests/tools/test_deferred_exports.py +++ b/tests/tools/test_deferred_exports.py @@ -1,4 +1,4 @@ -"""Tests for CallDeferred and ApprovalRequired re-exports from agentpool.tools. +"""Tests for CallDeferred and ApprovalRequired re-exports from wolfharness.tools. These are pydantic-ai exception classes used by tool authors for: - CallDeferred: Signal that tool execution should be deferred. @@ -11,7 +11,7 @@ import pytest -from agentpool.tools import ApprovalRequired, CallDeferred +from wolfharness.tools import ApprovalRequired, CallDeferred pytestmark = pytest.mark.unit @@ -65,9 +65,9 @@ def test_approval_required_is_pydantic_ai_class(): assert ApprovalRequired is PydanticApprovalRequired -def test_both_in_agentpool_tools_all(): - """Both exceptions should be listed in agentpool.tools.__all__.""" - from agentpool import tools +def test_both_in_wolfharness_tools_all(): + """Both exceptions should be listed in wolfharness.tools.__all__.""" + from wolfharness import tools assert "CallDeferred" in tools.__all__ assert "ApprovalRequired" in tools.__all__ diff --git a/tests/tools/test_deferred_tool.py b/tests/tools/test_deferred_tool.py index d9284b88e..eec53f02b 100644 --- a/tests/tools/test_deferred_tool.py +++ b/tests/tools/test_deferred_tool.py @@ -4,7 +4,7 @@ import pytest -from agentpool.tools.base import FunctionTool, Tool +from wolfharness.tools.base import FunctionTool, Tool pytestmark = pytest.mark.unit diff --git a/tests/tools/test_deferred_validation.py b/tests/tools/test_deferred_validation.py index 69e4c37ad..3cf162b87 100644 --- a/tests/tools/test_deferred_validation.py +++ b/tests/tools/test_deferred_validation.py @@ -11,8 +11,8 @@ import pytest -from agentpool.tools.base import Tool -from agentpool.tools.exceptions import ToolError +from wolfharness.tools.base import Tool +from wolfharness.tools.exceptions import ToolError pytestmark = pytest.mark.unit diff --git a/tests/tools/test_execution_environment_tools.py b/tests/tools/test_execution_environment_tools.py index dff9c36d7..1a8479638 100644 --- a/tests/tools/test_execution_environment_tools.py +++ b/tests/tools/test_execution_environment_tools.py @@ -10,11 +10,11 @@ from exxec.models import ExecutionResult import pytest -from agentpool import Agent, AgentContext -from agentpool.agents.context import AgentRunContext -from agentpool.tool_impls.bash import BashTool -from agentpool.tool_impls.execute_code import ExecuteCodeTool -from agentpool_toolsets.builtin.execution_environment import ProcessManagementTools +from wolfharness import Agent, AgentContext +from wolfharness.agents.context import AgentRunContext +from wolfharness.tool_impls.bash import BashTool +from wolfharness.tool_impls.execute_code import ExecuteCodeTool +from wolfharness_toolsets.builtin.execution_environment import ProcessManagementTools pytestmark = pytest.mark.unit diff --git a/tests/tools/test_file_editor.py b/tests/tools/test_file_editor.py index 7558d909c..fb778aceb 100644 --- a/tests/tools/test_file_editor.py +++ b/tests/tools/test_file_editor.py @@ -6,7 +6,7 @@ import pytest -from agentpool_toolsets.builtin.file_edit.file_edit import edit_file_tool +from wolfharness_toolsets.builtin.file_edit.file_edit import edit_file_tool pytestmark = pytest.mark.unit diff --git a/tests/tools/test_mcp_tools.py b/tests/tools/test_mcp_tools.py index ed4dd3228..461782803 100644 --- a/tests/tools/test_mcp_tools.py +++ b/tests/tools/test_mcp_tools.py @@ -5,7 +5,7 @@ from pydantic_ai import FunctionToolCallEvent import pytest -from agentpool import Agent +from wolfharness import Agent pytestmark = pytest.mark.unit diff --git a/tests/tools/test_openapi_toolsets.py b/tests/tools/test_openapi_toolsets.py index 5970c9f15..534875ffe 100644 --- a/tests/tools/test_openapi_toolsets.py +++ b/tests/tools/test_openapi_toolsets.py @@ -7,7 +7,7 @@ from openapi_spec_validator import validate import pytest -from agentpool_toolsets.openapi import OpenAPITools +from wolfharness_toolsets.openapi import OpenAPITools pytestmark = pytest.mark.unit diff --git a/tests/tools/test_pick.py b/tests/tools/test_pick.py index e025dcbc7..03fa135cd 100644 --- a/tests/tools/test_pick.py +++ b/tests/tools/test_pick.py @@ -2,7 +2,7 @@ import pytest -from agentpool import Agent +from wolfharness import Agent # All tests in this file create real Agent instances that need OPENAI_API_KEY. diff --git a/tests/tools/test_pydantic_ai_schema.py b/tests/tools/test_pydantic_ai_schema.py index 7d43e4b4b..ad175213d 100644 --- a/tests/tools/test_pydantic_ai_schema.py +++ b/tests/tools/test_pydantic_ai_schema.py @@ -5,7 +5,7 @@ import pytest from schemez import OpenAIFunctionDefinition -from agentpool.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability pytestmark = pytest.mark.unit diff --git a/tests/tools/test_runcontext.py b/tests/tools/test_runcontext.py index 6169a9fe3..ae57876ac 100644 --- a/tests/tools/test_runcontext.py +++ b/tests/tools/test_runcontext.py @@ -6,11 +6,11 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentContext -from agentpool.delegation import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_config.toolsets import SubagentToolsetConfig +from wolfharness import Agent, AgentContext +from wolfharness.delegation import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_config.toolsets import SubagentToolsetConfig async def run_ctx_tool(ctx: RunContext) -> str: diff --git a/tests/tools/test_tool_confirmation.py b/tests/tools/test_tool_confirmation.py index 81e067b30..170c21a23 100644 --- a/tests/tools/test_tool_confirmation.py +++ b/tests/tools/test_tool_confirmation.py @@ -3,8 +3,8 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, Tool -from agentpool.ui.mock_provider import MockInputProvider +from wolfharness import Agent, Tool +from wolfharness.ui.mock_provider import MockInputProvider pytestmark = pytest.mark.unit diff --git a/tests/tools/test_tool_schema.py b/tests/tools/test_tool_schema.py index 463bb4310..2f24669d0 100644 --- a/tests/tools/test_tool_schema.py +++ b/tests/tools/test_tool_schema.py @@ -20,15 +20,15 @@ import pytest from schemez import OpenAIFunctionDefinition -from agentpool.log import configure_logging -from agentpool.tools.base import FunctionTool, Tool +from wolfharness.log import configure_logging +from wolfharness.tools.base import FunctionTool, Tool pytestmark = pytest.mark.unit if TYPE_CHECKING: - from agentpool.agents.context import AgentContext + from wolfharness.agents.context import AgentContext @pytest.fixture(autouse=True) @@ -976,7 +976,7 @@ def test_get_json_schema_no_toolresult_return_warning_with_schema_override() -> """Dataclass ToolResult return + schema_override must not emit return-schema UserWarnings.""" import warnings - from agentpool.tools.base import FunctionTool, ToolResult + from wolfharness.tools.base import FunctionTool, ToolResult def returns_tool_result(x: int) -> ToolResult: return ToolResult(content=str(x)) diff --git a/tests/tools/test_workers.py b/tests/tools/test_workers.py index 9814c9a98..6be1c8137 100644 --- a/tests/tools/test_workers.py +++ b/tests/tools/test_workers.py @@ -9,9 +9,9 @@ from pydantic_ai.models.test import TestModel import pytest -from agentpool import Agent, AgentPool, AgentsManifest -from agentpool.agents.events import RunErrorEvent, SpawnSessionStart, StreamCompleteEvent -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness import Agent, AgentPool, AgentsManifest +from wolfharness.agents.events import RunErrorEvent, SpawnSessionStart, StreamCompleteEvent +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError pytestmark = pytest.mark.integration @@ -21,8 +21,8 @@ from collections.abc import AsyncIterator from pathlib import Path - from agentpool.agents.base_agent import BaseAgent - from agentpool.orchestrator.core import SessionPool + from wolfharness.agents.base_agent import BaseAgent + from wolfharness.orchestrator.core import SessionPool class StructuredResponse(BaseModel): @@ -189,7 +189,7 @@ async def _run_and_collect_events( This helper subscribes to the EventBus BEFORE starting the run, so all events — including spawn events — are received. """ - from agentpool.orchestrator.core import drain_and_merge + from wolfharness.orchestrator.core import drain_and_merge stream = await session_pool.event_bus.subscribe(session_id, scope=scope) diff --git a/tests/toolsets/builtin/test_as_capability.py b/tests/toolsets/builtin/test_as_capability.py index 233a17035..2bcec6541 100644 --- a/tests/toolsets/builtin/test_as_capability.py +++ b/tests/toolsets/builtin/test_as_capability.py @@ -13,8 +13,8 @@ from pydantic_ai.toolsets import AbstractToolset import pytest -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool_toolsets.builtin import ( +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness_toolsets.builtin import ( CodeTools, DebugTools, ProcessManagementTools, @@ -145,8 +145,8 @@ class TestSkillsToolsAsCapability: async def test_skill_manager_cap_exposes_load_and_list_tools(self) -> None: """SkillManagerCap toolset includes load_skill and list_skills tools.""" - from agentpool.capabilities.skill_manager_cap import SkillManagerCap - from agentpool.skills.skill import Skill + from wolfharness.capabilities.skill_manager_cap import SkillManagerCap + from wolfharness.skills.skill import Skill local_skill = Skill( name="test-skill", diff --git a/tests/toolsets/test_agentic_edit.py b/tests/toolsets/test_agentic_edit.py index 1bd99d29d..ab2e4be1a 100644 --- a/tests/toolsets/test_agentic_edit.py +++ b/tests/toolsets/test_agentic_edit.py @@ -8,8 +8,8 @@ import pytest -from agentpool import Agent -from agentpool_toolsets.fsspec_toolset import FSSpecTools +from wolfharness import Agent +from wolfharness_toolsets.fsspec_toolset import FSSpecTools if TYPE_CHECKING: diff --git a/tests/toolsets/test_builtin_handlers.py b/tests/toolsets/test_builtin_handlers.py index 889c69e3e..e8c849706 100644 --- a/tests/toolsets/test_builtin_handlers.py +++ b/tests/toolsets/test_builtin_handlers.py @@ -15,14 +15,14 @@ ) import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( RunErrorEvent, StreamCompleteEvent, ToolCallStartEvent, detailed_print_handler, simple_print_handler, ) -from agentpool.messaging import ChatMessage +from wolfharness.messaging import ChatMessage pytestmark = pytest.mark.unit diff --git a/tests/toolsets/test_custom_toolset.py b/tests/toolsets/test_custom_toolset.py index 98d1926a0..38a7888cf 100644 --- a/tests/toolsets/test_custom_toolset.py +++ b/tests/toolsets/test_custom_toolset.py @@ -6,8 +6,8 @@ import pytest -from agentpool.capabilities.function_toolset import FunctionToolsetCapability -from agentpool_config.toolsets import CustomToolsetConfig +from wolfharness.capabilities.function_toolset import FunctionToolsetCapability +from wolfharness_config.toolsets import CustomToolsetConfig pytestmark = pytest.mark.unit diff --git a/tests/toolsets/test_diff_application.py b/tests/toolsets/test_diff_application.py index 081feda03..f0de7d48c 100644 --- a/tests/toolsets/test_diff_application.py +++ b/tests/toolsets/test_diff_application.py @@ -10,7 +10,7 @@ import pytest from sublime_search import apply_diff_hunks, parse_locationless_diff -from agentpool_toolsets.fsspec_toolset.helpers import apply_diff_hunks_streaming +from wolfharness_toolsets.fsspec_toolset.helpers import apply_diff_hunks_streaming pytestmark = pytest.mark.unit diff --git a/tests/toolsets/test_input_provider_propagation.py b/tests/toolsets/test_input_provider_propagation.py index 1c9c84681..d86e81ec1 100644 --- a/tests/toolsets/test_input_provider_propagation.py +++ b/tests/toolsets/test_input_provider_propagation.py @@ -16,10 +16,10 @@ import pytest -from agentpool import AgentPool, AgentsManifest -from agentpool.agents.events import StreamCompleteEvent -from agentpool.messaging.messages import ChatMessage -from agentpool.ui.base import InputProvider +from wolfharness import AgentPool, AgentsManifest +from wolfharness.agents.events import StreamCompleteEvent +from wolfharness.messaging.messages import ChatMessage +from wolfharness.ui.base import InputProvider pytestmark = pytest.mark.integration @@ -240,10 +240,10 @@ async def test_input_provider_propagated_when_session_bound_only() -> None: ctx.get_input_provider() would return a provider via SessionState, the provider IS propagated to the child session. """ - from agentpool.agents.context import AgentContext - from agentpool.messaging.messagenode import MessageNode - from agentpool.orchestrator.core import SessionPool - from agentpool_toolsets.builtin.subagent_tools import SubagentTools + from wolfharness.agents.context import AgentContext + from wolfharness.messaging.messagenode import MessageNode + from wolfharness.orchestrator.core import SessionPool + from wolfharness_toolsets.builtin.subagent_tools import SubagentTools fake_provider = FakeInputProviderSession() diff --git a/tests/toolsets/test_mcp_discovery.py b/tests/toolsets/test_mcp_discovery.py index dcb307f6a..4c5344039 100644 --- a/tests/toolsets/test_mcp_discovery.py +++ b/tests/toolsets/test_mcp_discovery.py @@ -11,10 +11,10 @@ from pydantic_ai import RunContext import pytest -from agentpool.agents.context import AgentContext -from agentpool.mcp_server.client import MCPClient -from agentpool_config.mcp_server import StdioMCPServerConfig -from agentpool_toolsets.mcp_discovery.toolset import MCPDiscoveryToolset +from wolfharness.agents.context import AgentContext +from wolfharness.mcp_server.client import MCPClient +from wolfharness_config.mcp_server import StdioMCPServerConfig +from wolfharness_toolsets.mcp_discovery.toolset import MCPDiscoveryToolset pytestmark = [pytest.mark.asyncio, pytest.mark.unit] @@ -69,7 +69,7 @@ async def mock_sampling_callback(*args, **kwargs): with ( patch.object(toolset, "_get_server_config", return_value=mock_config), - patch("agentpool_toolsets.mcp_discovery.toolset.MCPClient") as mock_client_cls, + patch("wolfharness_toolsets.mcp_discovery.toolset.MCPClient") as mock_client_cls, ): mock_client_cls.return_value = mock_client diff --git a/tests/toolsets/test_notifications.py b/tests/toolsets/test_notifications.py index 54f8ded2f..4fd1ca5ba 100644 --- a/tests/toolsets/test_notifications.py +++ b/tests/toolsets/test_notifications.py @@ -7,8 +7,8 @@ import pytest -from agentpool_config.toolsets import NotificationsToolsetConfig -from agentpool_toolsets.notifications import NotificationsTools +from wolfharness_config.toolsets import NotificationsToolsetConfig +from wolfharness_toolsets.notifications import NotificationsTools pytestmark = pytest.mark.unit diff --git a/tests/toolsets/test_package_scoped_skills.py b/tests/toolsets/test_package_scoped_skills.py index ec53ec3fd..62c91b6fb 100644 --- a/tests/toolsets/test_package_scoped_skills.py +++ b/tests/toolsets/test_package_scoped_skills.py @@ -7,10 +7,10 @@ import pytest from upathtools import UPath -from agentpool.capabilities.resource_protocols import SkillEntry, SkillResource -from agentpool.capabilities.skill_manager_cap import SkillManagerCap -from agentpool.skills.skill import Skill -from agentpool_toolsets.builtin.skills import list_skills, load_skill, load_skill_for_node +from wolfharness.capabilities.resource_protocols import SkillEntry, SkillResource +from wolfharness.capabilities.skill_manager_cap import SkillManagerCap +from wolfharness.skills.skill import Skill +from wolfharness_toolsets.builtin.skills import list_skills, load_skill, load_skill_for_node def _write_skill(root, name: str, description: str) -> Skill: @@ -77,7 +77,7 @@ def is_skill_visible_to_node(self, skill: Skill, node_name: str | None) -> bool: def _ctx(pool: _FakePool, node_name: str) -> Any: """Build a RuntimeAgentContext (AgentContext) deps with pool + node name.""" - from agentpool.agents.context import AgentContext as RuntimeAgentContext + from wolfharness.agents.context import AgentContext as RuntimeAgentContext node = SimpleNamespace(name=node_name) return RuntimeAgentContext(node=node, pool=pool) diff --git a/tests/toolsets/test_process_integration.py b/tests/toolsets/test_process_integration.py index 3d8628ca7..48902a4c5 100644 --- a/tests/toolsets/test_process_integration.py +++ b/tests/toolsets/test_process_integration.py @@ -6,10 +6,10 @@ import pytest -from agentpool.delegation.pool import AgentPool -from agentpool.models.agents import NativeAgentConfig -from agentpool.models.manifest import AgentsManifest -from agentpool_config.toolsets import ( +from wolfharness.delegation.pool import AgentPool +from wolfharness.models.agents import NativeAgentConfig +from wolfharness.models.manifest import AgentsManifest +from wolfharness_config.toolsets import ( FSSpecToolsetConfig, ProcessManagementToolsetConfig, ) diff --git a/tests/toolsets/test_subagent_async.py b/tests/toolsets/test_subagent_async.py index ffdf0dcd7..51f4d822d 100644 --- a/tests/toolsets/test_subagent_async.py +++ b/tests/toolsets/test_subagent_async.py @@ -4,7 +4,7 @@ import pytest -from agentpool import AgentPool, AgentsManifest +from wolfharness import AgentPool, AgentsManifest pytestmark = pytest.mark.integration diff --git a/tests/toolsets/test_subagent_child_session.py b/tests/toolsets/test_subagent_child_session.py index 9f14cd688..1b46d5ddd 100644 --- a/tests/toolsets/test_subagent_child_session.py +++ b/tests/toolsets/test_subagent_child_session.py @@ -20,17 +20,17 @@ import pytest -from agentpool import AgentPool, AgentsManifest -from agentpool.agents.context import AgentContext, AgentRunContext -from agentpool.agents.events import ( +from wolfharness import AgentPool, AgentsManifest +from wolfharness.agents.context import AgentContext, AgentRunContext +from wolfharness.agents.events import ( RunStartedEvent, SpawnSessionStart, SubAgentEvent, ) -from agentpool.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError -from agentpool.orchestrator.core import EventEnvelope -from agentpool_storage.memory_provider.provider import MemoryStorageProvider -from agentpool_toolsets.builtin.subagent_tools import SubagentTools +from wolfharness.agents.exceptions import MAX_DELEGATION_DEPTH, DelegationDepthError +from wolfharness.orchestrator.core import EventEnvelope +from wolfharness_storage.memory_provider.provider import MemoryStorageProvider +from wolfharness_toolsets.builtin.subagent_tools import SubagentTools pytestmark = pytest.mark.integration @@ -357,7 +357,7 @@ async def test_task_uses_run_ctx_depth() -> None: async def test_subagent_tools_does_not_import_identifiers() -> None: """subagent_tools module does not import identifier — uses create_child_session instead.""" - import agentpool_toolsets.builtin.subagent_tools as mod + import wolfharness_toolsets.builtin.subagent_tools as mod # The module should not have 'identifier' in its namespace assert not hasattr(mod, "identifier"), ( diff --git a/tests/toolsets/test_tool_filtering.py b/tests/toolsets/test_tool_filtering.py index 9f6da6e8f..bc922d2cc 100644 --- a/tests/toolsets/test_tool_filtering.py +++ b/tests/toolsets/test_tool_filtering.py @@ -4,8 +4,12 @@ import pytest -from agentpool.capabilities.filtered_toolset import FilteredToolsetCapability -from agentpool_config.toolsets import CodeToolsetConfig, SkillsToolsetConfig, SubagentToolsetConfig +from wolfharness.capabilities.filtered_toolset import FilteredToolsetCapability +from wolfharness_config.toolsets import ( + CodeToolsetConfig, + SkillsToolsetConfig, + SubagentToolsetConfig, +) pytestmark = pytest.mark.unit diff --git a/tests/unit/test_open_code_config_cleanup.py b/tests/unit/test_open_code_config_cleanup.py index b423c2729..3e4b07040 100644 --- a/tests/unit/test_open_code_config_cleanup.py +++ b/tests/unit/test_open_code_config_cleanup.py @@ -2,7 +2,7 @@ import pytest -from agentpool_config.session_pool import OpenCodeConfig +from wolfharness_config.session_pool import OpenCodeConfig pytestmark = pytest.mark.unit diff --git a/tests/utils/test_context_wrapping.py b/tests/utils/test_context_wrapping.py index 19b6d429c..dea5ec760 100644 --- a/tests/utils/test_context_wrapping.py +++ b/tests/utils/test_context_wrapping.py @@ -8,7 +8,7 @@ from pydantic_ai import RunContext import pytest -from agentpool.agents.context import AgentContext +from wolfharness.agents.context import AgentContext pytestmark = pytest.mark.unit @@ -39,7 +39,7 @@ async def test_sync_function_no_context(self): def simple() -> str: return "Be helpful" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(simple) run_ctx = self._create_mock_run_context() @@ -52,7 +52,7 @@ async def test_async_function_no_context(self): async def simple_async() -> str: return "Be helpful and async" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(simple_async) run_ctx = self._create_mock_run_context() @@ -65,7 +65,7 @@ async def test_function_with_agent_context(self): def with_agent(ctx: AgentContext) -> str: return f"Tool: {ctx.tool_name or 'none'}" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(with_agent) @@ -83,7 +83,7 @@ async def test_async_function_with_agent_context(self): async def with_agent_async(ctx: AgentContext) -> str: return f"Tool (async): {ctx.tool_name or 'none'}" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(with_agent_async) @@ -101,7 +101,7 @@ async def test_function_with_run_context(self): def with_run(ctx: RunContext) -> str: return f"RunContext: {ctx.deps or 'none'}" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(with_run) run_ctx = self._create_mock_run_context() @@ -114,7 +114,7 @@ async def test_function_with_both_contexts(self): def with_both(agent_ctx: AgentContext, run_ctx: RunContext) -> str: return f"Agent: {agent_ctx.tool_name or 'none'}, Run: {run_ctx.deps or 'none'}" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(with_both) @@ -135,7 +135,7 @@ async def test_error_handling_with_fallback(self): def error_func() -> str: raise ValueError("This should be caught") - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(error_func, fallback="Fallback text") run_ctx = self._create_mock_run_context() @@ -148,7 +148,7 @@ async def test_default_fallback_empty_string(self): def error_func() -> str: raise ValueError("Error") - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(error_func) # No fallback specified run_ctx = self._create_mock_run_context() @@ -161,7 +161,7 @@ async def test_wrapped_preserves_function_name(self): def named_function() -> str: return "test" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction wrapped = wrap_instruction(named_function) @@ -183,7 +183,7 @@ def with_run(ctx: RunContext) -> str: def with_both(agent_ctx: AgentContext, run_ctx: RunContext) -> str: return "both" - from agentpool.utils.context_wrapping import wrap_instruction + from wolfharness.utils.context_wrapping import wrap_instruction # All should be accepted as InstructionFunc for func in [simple, with_agent, with_run, with_both]: diff --git a/tests/utils/test_dag.py b/tests/utils/test_dag.py index b19178c31..105a389ef 100644 --- a/tests/utils/test_dag.py +++ b/tests/utils/test_dag.py @@ -4,7 +4,7 @@ import pytest -from agentpool.utils.dag import DAGNode, dag_iterator, dag_to_list +from wolfharness.utils.dag import DAGNode, dag_iterator, dag_to_list pytestmark = pytest.mark.unit diff --git a/tests/utils/test_safe_args_as_dict.py b/tests/utils/test_safe_args_as_dict.py index 999e6e6c4..b765da763 100644 --- a/tests/utils/test_safe_args_as_dict.py +++ b/tests/utils/test_safe_args_as_dict.py @@ -10,7 +10,7 @@ from pydantic_ai.messages import ToolCallPart import pytest -from agentpool.utils.pydantic_ai_helpers import safe_args_as_dict +from wolfharness.utils.pydantic_ai_helpers import safe_args_as_dict pytestmark = pytest.mark.unit diff --git a/tests/utils/test_task_group.py b/tests/utils/test_task_group.py index d134aea29..e9d6f65bc 100644 --- a/tests/utils/test_task_group.py +++ b/tests/utils/test_task_group.py @@ -7,7 +7,7 @@ import anyio import pytest -from agentpool.utils.task_group import ManagedTaskGroup +from wolfharness.utils.task_group import ManagedTaskGroup pytestmark = pytest.mark.unit diff --git a/tests/utils/test_usage.py b/tests/utils/test_usage.py index 10ddd11e5..853786aac 100644 --- a/tests/utils/test_usage.py +++ b/tests/utils/test_usage.py @@ -9,7 +9,7 @@ from pydantic_ai import RunUsage import pytest -from agentpool.utils.usage import diff_usage +from wolfharness.utils.usage import diff_usage @pytest.mark.unit diff --git a/tests/vcr/_acp_helpers.py b/tests/vcr/_acp_helpers.py index e08bda337..e940ea8d0 100644 --- a/tests/vcr/_acp_helpers.py +++ b/tests/vcr/_acp_helpers.py @@ -21,13 +21,13 @@ from anyio.abc import ByteReceiveStream, ByteSendStream from acp import AgentSideConnection, ClientSideConnection, DefaultACPClient -from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent +from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent if TYPE_CHECKING: from typing import Any, Protocol - from agentpool import AgentPool + from wolfharness import AgentPool class _PoolWithGetAgent(Protocol): """Protocol for a pool with the ``get_agent`` compat shim attached.""" diff --git a/tests/vcr/conftest.py b/tests/vcr/conftest.py index 564afc065..e064f3a61 100644 --- a/tests/vcr/conftest.py +++ b/tests/vcr/conftest.py @@ -19,7 +19,7 @@ import pytest import yamling -from agentpool import AgentPool, AgentsManifest +from wolfharness import AgentPool, AgentsManifest if TYPE_CHECKING: diff --git a/tests/vcr/test_acp_protocol.py b/tests/vcr/test_acp_protocol.py index d2913ccf2..a8e925163 100644 --- a/tests/vcr/test_acp_protocol.py +++ b/tests/vcr/test_acp_protocol.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_acp_subagent.py b/tests/vcr/test_acp_subagent.py index c3a59c14c..97a5efa3c 100644 --- a/tests/vcr/test_acp_subagent.py +++ b/tests/vcr/test_acp_subagent.py @@ -31,7 +31,7 @@ if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_acp_tool_call.py b/tests/vcr/test_acp_tool_call.py index e185ae626..4696e1d4e 100644 --- a/tests/vcr/test_acp_tool_call.py +++ b/tests/vcr/test_acp_tool_call.py @@ -31,7 +31,7 @@ if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_agui_protocol.py b/tests/vcr/test_agui_protocol.py index 97bec1164..383947452 100644 --- a/tests/vcr/test_agui_protocol.py +++ b/tests/vcr/test_agui_protocol.py @@ -33,14 +33,14 @@ from httpx import ASGITransport, AsyncClient import pytest -from agentpool_server.agui_server import AGUIServer from tests.vcr.conftest import cassette_exists +from wolfharness_server.agui_server import AGUIServer if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_delegation.py b/tests/vcr/test_delegation.py index 5b7a57103..ea9032967 100644 --- a/tests/vcr/test_delegation.py +++ b/tests/vcr/test_delegation.py @@ -19,16 +19,16 @@ import pytest -from agentpool.agents.events import ( +from tests.vcr.conftest import cassette_exists +from wolfharness.agents.events import ( SpawnSessionStart, StreamCompleteEvent, SubAgentEvent, ) -from tests.vcr.conftest import cassette_exists if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_event_bus_sequences.py b/tests/vcr/test_event_bus_sequences.py index e775d317f..99f698726 100644 --- a/tests/vcr/test_event_bus_sequences.py +++ b/tests/vcr/test_event_bus_sequences.py @@ -22,8 +22,8 @@ if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.orchestrator.event_bus import EventBus + from wolfharness import AgentPool + from wolfharness.orchestrator.event_bus import EventBus pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_lifecycle_recovery.py b/tests/vcr/test_lifecycle_recovery.py index 77ff038c6..4f3835b60 100644 --- a/tests/vcr/test_lifecycle_recovery.py +++ b/tests/vcr/test_lifecycle_recovery.py @@ -18,12 +18,12 @@ import pytest -from agentpool.lifecycle.journal import MemoryJournal from tests.vcr.conftest import cassette_exists +from wolfharness.lifecycle.journal import MemoryJournal if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_native_basic.py b/tests/vcr/test_native_basic.py index 4a7f6deed..d63a76393 100644 --- a/tests/vcr/test_native_basic.py +++ b/tests/vcr/test_native_basic.py @@ -16,12 +16,12 @@ from dirty_equals import IsStr import pytest -from agentpool.agents.events import StreamCompleteEvent from tests.vcr.conftest import cassette_exists +from wolfharness.agents.events import StreamCompleteEvent if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.vcr diff --git a/tests/vcr/test_native_streaming.py b/tests/vcr/test_native_streaming.py index d5488743f..4fb5941d1 100644 --- a/tests/vcr/test_native_streaming.py +++ b/tests/vcr/test_native_streaming.py @@ -18,17 +18,17 @@ from pydantic_ai.messages import TextPart, ThinkingPart import pytest -from agentpool.agents.events import ( +from tests.vcr.conftest import cassette_exists +from wolfharness.agents.events import ( PartDeltaEvent, PartStartEvent, RunStartedEvent, StreamCompleteEvent, ) -from tests.vcr.conftest import cassette_exists if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.vcr diff --git a/tests/vcr/test_native_tool_call.py b/tests/vcr/test_native_tool_call.py index 3ad36df8c..3345a14b1 100644 --- a/tests/vcr/test_native_tool_call.py +++ b/tests/vcr/test_native_tool_call.py @@ -16,16 +16,16 @@ from dirty_equals import IsPartialDict, IsStr import pytest -from agentpool.agents.events import ( +from tests.vcr.conftest import cassette_exists +from wolfharness.agents.events import ( StreamCompleteEvent, ToolCallCompleteEvent, ToolCallStartEvent, ) -from tests.vcr.conftest import cassette_exists if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.vcr diff --git a/tests/vcr/test_openai_api_protocol.py b/tests/vcr/test_openai_api_protocol.py index 24eb3ca2b..eb9dbab9a 100644 --- a/tests/vcr/test_openai_api_protocol.py +++ b/tests/vcr/test_openai_api_protocol.py @@ -20,14 +20,14 @@ from fastapi.testclient import TestClient import pytest -from agentpool_server.openai_api_server.server import OpenAIAPIServer from tests.vcr.conftest import cassette_exists +from wolfharness_server.openai_api_server.server import OpenAIAPIServer if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_opencode_protocol.py b/tests/vcr/test_opencode_protocol.py index bc2d6d74e..074918635 100644 --- a/tests/vcr/test_opencode_protocol.py +++ b/tests/vcr/test_opencode_protocol.py @@ -22,19 +22,19 @@ from fastapi.testclient import TestClient import pytest -from agentpool_server.opencode_server.dependencies import get_state -from agentpool_server.opencode_server.routes import agent_router, file_router, session_router -from agentpool_server.opencode_server.routes.global_routes import router as global_router -from agentpool_server.opencode_server.routes.message_routes import router as message_router -from agentpool_server.opencode_server.state import ServerState from tests.vcr.conftest import cassette_exists +from wolfharness_server.opencode_server.dependencies import get_state +from wolfharness_server.opencode_server.routes import agent_router, file_router, session_router +from wolfharness_server.opencode_server.routes.global_routes import router as global_router +from wolfharness_server.opencode_server.routes.message_routes import router as message_router +from wolfharness_server.opencode_server.state import ServerState if TYPE_CHECKING: from collections.abc import AsyncIterator from pathlib import Path - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_runloop_streaming.py b/tests/vcr/test_runloop_streaming.py index 374550f4f..fe53ff654 100644 --- a/tests/vcr/test_runloop_streaming.py +++ b/tests/vcr/test_runloop_streaming.py @@ -21,7 +21,7 @@ if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_session_controller.py b/tests/vcr/test_session_controller.py index 4d37c734f..fcb8498ac 100644 --- a/tests/vcr/test_session_controller.py +++ b/tests/vcr/test_session_controller.py @@ -21,7 +21,7 @@ if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_skill_command_vcr.py b/tests/vcr/test_skill_command_vcr.py index 8d37a2075..4c138c038 100644 --- a/tests/vcr/test_skill_command_vcr.py +++ b/tests/vcr/test_skill_command_vcr.py @@ -22,17 +22,17 @@ import pytest -from agentpool.agents.events import ( +from tests.vcr.conftest import cassette_exists +from wolfharness.agents.events import ( PartStartEvent, StreamCompleteEvent, UserMessageInsertedEvent, ) -from tests.vcr.conftest import cassette_exists if TYPE_CHECKING: - from agentpool import AgentPool - from agentpool.orchestrator.event_bus import EventBus + from wolfharness import AgentPool + from wolfharness.orchestrator.event_bus import EventBus pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/vcr/test_team_mode.py b/tests/vcr/test_team_mode.py index 056b6d5ae..a343a5040 100644 --- a/tests/vcr/test_team_mode.py +++ b/tests/vcr/test_team_mode.py @@ -30,7 +30,7 @@ import pytest -from agentpool.agents.events import ( +from wolfharness.agents.events import ( PartDeltaEvent, StreamCompleteEvent, ToolCallCompleteEvent, @@ -41,7 +41,7 @@ if TYPE_CHECKING: from collections.abc import AsyncIterator - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = pytest.mark.vcr diff --git a/tests/vcr/test_turn_tool_calls.py b/tests/vcr/test_turn_tool_calls.py index 9be481316..03de50747 100644 --- a/tests/vcr/test_turn_tool_calls.py +++ b/tests/vcr/test_turn_tool_calls.py @@ -18,16 +18,16 @@ from dirty_equals import IsStr import pytest -from agentpool.agents.events import ( +from tests.vcr.conftest import cassette_exists +from wolfharness.agents.events import ( StreamCompleteEvent, ToolCallCompleteEvent, ToolCallStartEvent, ) -from tests.vcr.conftest import cassette_exists if TYPE_CHECKING: - from agentpool import AgentPool + from wolfharness import AgentPool pytestmark = [pytest.mark.vcr, pytest.mark.integration] diff --git a/tests/verification/test_acp_display_config.py b/tests/verification/test_acp_display_config.py index 2b81d9e25..0bd449d17 100755 --- a/tests/verification/test_acp_display_config.py +++ b/tests/verification/test_acp_display_config.py @@ -44,7 +44,7 @@ def test_config_model() -> bool: print_section("Test 1: Config Model Field") try: - from agentpool_config.pool_server import ACPPoolServerConfig + from wolfharness_config.pool_server import ACPPoolServerConfig # Test 1.1: Field accepts "zed" (new mode) config_zed = ACPPoolServerConfig(subagent_display_mode="zed") @@ -98,7 +98,7 @@ def test_default_value() -> bool: print_section("Test 2: Default Value") try: - from agentpool_config.pool_server import ACPPoolServerConfig + from wolfharness_config.pool_server import ACPPoolServerConfig # Test default value config_default = ACPPoolServerConfig() @@ -119,7 +119,7 @@ def test_cli_option() -> bool: try: # Test that help shows the option result = subprocess.run( - ["uv", "run", "agentpool", "serve-acp", "--help"], + ["uv", "run", "wolfharness", "serve-acp", "--help"], capture_output=True, text=True, timeout=30, @@ -147,10 +147,10 @@ def test_server_initialization() -> bool: print_section("Test 4: Server Initialization") try: - from agentpool import AgentPool - from agentpool.models.manifest import AgentsManifest - from agentpool_config.pool_server import ACPPoolServerConfig - from agentpool_server.acp_server.server import ACPServer + from wolfharness import AgentPool + from wolfharness.models.manifest import AgentsManifest + from wolfharness_config.pool_server import ACPPoolServerConfig + from wolfharness_server.acp_server.server import ACPServer # Create a minimal manifest manifest_dict = { @@ -220,7 +220,7 @@ def test_agent_display_mode() -> bool: from dataclasses import fields import inspect - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent # Test 5.1: AgentPoolACPAgent has subagent_display_mode field field_names = [f.name for f in fields(AgentPoolACPAgent)] @@ -262,7 +262,7 @@ def test_session_display_mode() -> bool: try: from dataclasses import fields - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession # Test 6.1: ACPSession has subagent_display_mode field field_names = [f.name for f in fields(ACPSession)] @@ -295,8 +295,8 @@ def test_end_to_end_flow() -> bool: try: from dataclasses import fields - from agentpool.models.manifest import AgentsManifest - from agentpool_server.acp_server.server import ACPServer + from wolfharness.models.manifest import AgentsManifest + from wolfharness_server.acp_server.server import ACPServer # Create minimal manifest manifest_dict = { @@ -317,7 +317,7 @@ def test_end_to_end_flow() -> bool: # Test 7.2: Verify agent has access to mode via server reference # (AgentPoolACPAgent gets subagent_display_mode from server at instantiation) - from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent + from wolfharness_server.acp_server.acp_agent import AgentPoolACPAgent # Check that AgentPoolACPAgent stores the mode sig_fields = {f.name: f for f in fields(AgentPoolACPAgent)} @@ -328,7 +328,7 @@ def test_end_to_end_flow() -> bool: # SessionManager.create_session accepts subagent_display_mode parameter import inspect - from agentpool_server.acp_server.session_manager import ACPSessionManager + from wolfharness_server.acp_server.session_manager import ACPSessionManager sig = inspect.signature(ACPSessionManager.create_session) params = sig.parameters @@ -349,7 +349,7 @@ def test_end_to_end_flow() -> bool: return False # Test 7.4: Verify ACPSession stores the mode - from agentpool_server.acp_server.session import ACPSession + from wolfharness_server.acp_server.session import ACPSession sig_fields = {f.name: f for f in fields(ACPSession)} assert "subagent_display_mode" in sig_fields diff --git a/tests/verification/test_rfc0011_lineage.py b/tests/verification/test_rfc0011_lineage.py index 8a1710c16..e133e7656 100644 --- a/tests/verification/test_rfc0011_lineage.py +++ b/tests/verification/test_rfc0011_lineage.py @@ -5,12 +5,12 @@ import pytest from sqlalchemy import select -from agentpool import Agent, AgentPool, AgentsManifest, NativeAgentConfig -from agentpool.agents.events import RunStartedEvent -from agentpool_config.storage import SQLStorageConfig, StorageConfig -from agentpool_storage.sql_provider import SQLModelProvider -from agentpool_storage.sql_provider.models import Conversation -from agentpool_toolsets.builtin.subagent_tools import SubagentTools +from wolfharness import Agent, AgentPool, AgentsManifest, NativeAgentConfig +from wolfharness.agents.events import RunStartedEvent +from wolfharness_config.storage import SQLStorageConfig, StorageConfig +from wolfharness_storage.sql_provider import SQLModelProvider +from wolfharness_storage.sql_provider.models import Conversation +from wolfharness_toolsets.builtin.subagent_tools import SubagentTools pytestmark = pytest.mark.integration @@ -123,7 +123,7 @@ async def test_subagent_event_lineage(test_pool): def test_conversation_model_defines_parent_id() -> None: """Regression: ORM must expose parent_id so log_session INSERT and DB schema stay aligned.""" - from agentpool_storage.sql_provider.models import Conversation + from wolfharness_storage.sql_provider.models import Conversation assert "parent_id" in Conversation.model_fields diff --git a/ty.toml b/ty.toml index 8e1714b61..b252da728 100644 --- a/ty.toml +++ b/ty.toml @@ -108,8 +108,8 @@ invalid-await = "ignore" # mock awaitable patterns # (.where(), .asc(), column comparisons). Suppress broadly. [[overrides]] include = [ - "src/agentpool_storage/sql_provider/**", - "src/agentpool/lifecycle/journal.py", + "src/wolfharness_storage/sql_provider/**", + "src/wolfharness/lifecycle/journal.py", ] [overrides.rules] @@ -117,14 +117,14 @@ invalid-argument-type = "ignore" # .where() column expressions unresolved-attribute = "ignore" # .asc() on column objects # Wrapper/decorator files: ty doesn't follow __wrapped__, __signature__, -# __agentpool_wrapped__ dynamic attributes set on wrapper functions. +# __wolfharness_wrapped__ dynamic attributes set on wrapper functions. [[overrides]] include = [ - "src/agentpool/tools/tool_wrapping.py", - "src/agentpool/utils/signatures.py", - "src/agentpool/talk/talk.py", - "src/agentpool/running/discovery.py", - "src/agentpool/mcp_server/client.py", + "src/wolfharness/tools/tool_wrapping.py", + "src/wolfharness/utils/signatures.py", + "src/wolfharness/talk/talk.py", + "src/wolfharness/running/discovery.py", + "src/wolfharness/mcp_server/client.py", ] [overrides.rules] @@ -154,8 +154,8 @@ invalid-assignment = "ignore" # content union assignment # Pydantic model fields, nor anyio Timeout constructor signatures. [[overrides]] include = [ - "src/agentpool_config/graph_translation.py", - "src/agentpool_config/mcp_server.py", + "src/wolfharness_config/graph_translation.py", + "src/wolfharness_config/mcp_server.py", ] [overrides.rules] @@ -163,7 +163,7 @@ invalid-argument-type = "ignore" # dict-spread to Pydantic, Timeout init # CLI ui: anyio's _parallel_safe attribute not recognized by ty. [[overrides]] -include = ["src/agentpool_cli/ui.py"] +include = ["src/wolfharness_cli/ui.py"] [overrides.rules] unresolved-attribute = "ignore" # _parallel_safe on anyio decorator @@ -172,8 +172,8 @@ unresolved-attribute = "ignore" # _parallel_safe on anyio decorator # the actual session/proxy type after protocol dispatch. [[overrides]] include = [ - "src/agentpool_server/acp_server/input_provider.py", - "src/agentpool_server/acp_server/session_agent_mgmt.py", + "src/wolfharness_server/acp_server/input_provider.py", + "src/wolfharness_server/acp_server/session_agent_mgmt.py", ] [overrides.rules] diff --git a/uv.lock b/uv.lock index 5bd842928..2889b0667 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 1 requires-python = "==3.13.*" resolution-markers = [ "sys_platform == 'win32'", @@ -12,24 +12,23 @@ constraints = [{ name = "extism-sys", specifier = "<1.13.0" }] [[package]] name = "ag-ui-protocol" version = "0.1.19" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/10/4ad299267a7d04b89935aa99eef62979758fcf95aee9f8bb5d70c35b1be1/ag_ui_protocol-0.1.19.tar.gz", hash = "sha256:43c27f60d41712dcad0e9e0a203cbdf1c8e248b22417374c5c68321c448af4ea", size = 10720, upload-time = "2026-06-02T17:26:15.627Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/10/4ad299267a7d04b89935aa99eef62979758fcf95aee9f8bb5d70c35b1be1/ag_ui_protocol-0.1.19.tar.gz", hash = "sha256:43c27f60d41712dcad0e9e0a203cbdf1c8e248b22417374c5c68321c448af4ea", size = 10720 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/0a/bcad8116eb058e4b4a305e3fc37ebd7efc879deeb86b854f1c5b8b6e97dd/ag_ui_protocol-0.1.19-py3-none-any.whl", hash = "sha256:898843b1410d378824da0c6a776486288b9c5828689d0bf563118868e37f390f", size = 13490, upload-time = "2026-06-02T17:26:16.313Z" }, + { url = "https://files.pythonhosted.org/packages/4c/0a/bcad8116eb058e4b4a305e3fc37ebd7efc879deeb86b854f1c5b8b6e97dd/ag_ui_protocol-0.1.19-py3-none-any.whl", hash = "sha256:898843b1410d378824da0c6a776486288b9c5828689d0bf563118868e37f390f", size = 13490 }, ] [[package]] name = "agentpool" version = "2.9.5" -source = { editable = "." } +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ag-ui-protocol" }, { name = "alembic" }, { name = "anyenv", extra = ["httpx"] }, - { name = "anyio" }, + { name = "clawd-code-sdk" }, { name = "docler" }, { name = "docstring-parser" }, { name = "epregistry" }, @@ -37,12 +36,12 @@ dependencies = [ { name = "exxec" }, { name = "fastapi" }, { name = "fastmcp" }, - { name = "filelock" }, { name = "fsspec" }, { name = "httpx" }, { name = "jinja2" }, { name = "jinjarope" }, { name = "keyring" }, + { name = "llmling-models" }, { name = "logfire", extra = ["fastapi"] }, { name = "mcp" }, { name = "pillow" }, @@ -52,8 +51,6 @@ dependencies = [ { name = "py-key-value-aio", extra = ["disk"] }, { name = "pydantic" }, { name = "pydantic-ai-slim", extra = ["anthropic", "google", "mistral", "openai", "retries"] }, - { name = "pydantic-graph" }, - { name = "pydantic-xml" }, { name = "pydocket" }, { name = "python-dotenv" }, { name = "rich" }, @@ -63,7 +60,6 @@ dependencies = [ { name = "slashed" }, { name = "sqlalchemy", extra = ["aiosqlite"] }, { name = "sqlmodel" }, - { name = "starlette" }, { name = "structlog" }, { name = "sublime-search" }, { name = "tokonomics" }, @@ -75,279 +71,36 @@ dependencies = [ { name = "websockets" }, { name = "yamling" }, ] - -[package.optional-dependencies] -a2a = [ - { name = "fasta2a" }, - { name = "starlette" }, -] -bot = [ - { name = "croniter" }, - { name = "python-telegram-bot", extra = ["socks"] }, - { name = "slack-sdk" }, - { name = "slackify-markdown" }, -] -braintrust = [ - { name = "autoevals" }, - { name = "braintrust" }, -] -clipboard = [ - { name = "copykitten" }, -] -coding = [ - { name = "ast-grep-py" }, - { name = "grep-ast" }, - { name = "rustworkx" }, - { name = "tree-sitter" }, - { name = "tree-sitter-c" }, - { name = "tree-sitter-cpp" }, - { name = "tree-sitter-go" }, - { name = "tree-sitter-javascript" }, - { name = "tree-sitter-json" }, - { name = "tree-sitter-python" }, - { name = "tree-sitter-rust" }, - { name = "tree-sitter-typescript" }, - { name = "tree-sitter-yaml" }, -] -composio = [ - { name = "composio" }, -] -events = [ - { name = "evented", extra = ["all"] }, -] -langfuse = [ - { name = "langfuse" }, -] -markitdown = [ - { name = "markitdown" }, -] -mcp-discovery = [ - { name = "fastembed" }, - { name = "lancedb" }, - { name = "pyarrow" }, -] -mcp-run = [ - { name = "mcpx-py" }, -] -notifications = [ - { name = "apprise" }, -] -promptlayer = [ - { name = "promptlayer" }, -] -tiktoken = [ - { name = "tiktoken" }, -] -tts = [ - { name = "anyvoice", extra = ["openai", "tts-edge"] }, -] -viking = [ - { name = "openviking-sdk" }, -] -watchdog = [ - { name = "watchdog" }, -] -zed = [ - { name = "zstandard" }, -] - -[package.dev-dependencies] -benchmark = [ - { name = "pyinstrument" }, -] -dev = [ - { name = "check-jsonschema" }, - { name = "devtools" }, - { name = "dirty-equals" }, - { name = "fastembed" }, - { name = "import-linter" }, - { name = "inline-snapshot" }, - { name = "lancedb" }, - { name = "openapi-spec-validator" }, - { name = "pyinstaller" }, - { name = "pyreadline3" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-recording" }, - { name = "pytest-rerunfailures" }, - { name = "pytest-timeout" }, - { name = "pytest-xdist" }, - { name = "syrupy" }, - { name = "vcrpy" }, -] -docs = [ - { name = "beautifulsoup4" }, - { name = "json-schema-for-humans" }, - { name = "mkdocs-material" }, - { name = "mknodes" }, - { name = "zensical" }, -] -lint = [ - { name = "mypy", extra = ["faster-cache"] }, - { name = "ruff" }, - { name = "ty" }, - { name = "types-croniter" }, - { name = "types-jsonschema" }, - { name = "types-pygments" }, - { name = "types-pyyaml" }, -] - -[package.metadata] -requires-dist = [ - { name = "ag-ui-protocol", specifier = ">=0.1.10" }, - { name = "alembic", specifier = ">=1.16.5" }, - { name = "anyenv", extras = ["httpx"], specifier = ">=0.3.0" }, - { name = "anyio", specifier = ">=4.0" }, - { name = "anyvoice", extras = ["tts-edge", "openai"], marker = "extra == 'tts'", specifier = ">=0.0.2" }, - { name = "apprise", marker = "extra == 'notifications'", specifier = ">=1.9.5" }, - { name = "ast-grep-py", marker = "extra == 'coding'", specifier = ">=0.40.0" }, - { name = "autoevals", marker = "extra == 'braintrust'" }, - { name = "braintrust", marker = "extra == 'braintrust'" }, - { name = "composio", marker = "extra == 'composio'" }, - { name = "copykitten", marker = "extra == 'clipboard'" }, - { name = "croniter", marker = "extra == 'bot'", specifier = ">=2.0.0" }, - { name = "docler", specifier = ">=1.0.3" }, - { name = "docstring-parser", specifier = ">=0.17.0" }, - { name = "epregistry" }, - { name = "evented", specifier = ">=1.0.5" }, - { name = "evented", extras = ["all"], marker = "extra == 'events'" }, - { name = "exxec", specifier = ">=0.1.0" }, - { name = "fasta2a", marker = "extra == 'a2a'" }, - { name = "fastapi" }, - { name = "fastembed", marker = "python_full_version < '3.14' and extra == 'mcp-discovery'", specifier = ">=0.7.4" }, - { name = "fastmcp", specifier = ">=2.12.4" }, - { name = "filelock", specifier = ">=3.13" }, - { name = "fsspec" }, - { name = "grep-ast", marker = "extra == 'coding'" }, - { name = "httpx" }, - { name = "jinja2" }, - { name = "jinjarope" }, - { name = "keyring", specifier = ">=25.6.0" }, - { name = "lancedb", marker = "python_full_version < '3.14' and extra == 'mcp-discovery'", specifier = ">=0.26.0" }, - { name = "langfuse", marker = "extra == 'langfuse'" }, - { name = "logfire", extras = ["fastapi"] }, - { name = "markitdown", marker = "python_full_version < '3.14' and extra == 'markitdown'" }, - { name = "mcp", specifier = ">=1.2.0" }, - { name = "mcpx-py", marker = "extra == 'mcp-run'", specifier = ">=0.7.0" }, - { name = "openviking-sdk", marker = "extra == 'viking'", specifier = ">=0.1.0" }, - { name = "pillow", specifier = ">=11.3.0" }, - { name = "platformdirs" }, - { name = "promptantic", specifier = ">=0.4.5" }, - { name = "promptlayer", marker = "extra == 'promptlayer'" }, - { name = "psygnal", specifier = ">=0.11.1" }, - { name = "py-key-value-aio", extras = ["disk"] }, - { name = "pyarrow", marker = "python_full_version < '3.14' and extra == 'mcp-discovery'", specifier = ">=19.0.0" }, - { name = "pydantic", specifier = ">=2.10.0" }, - { name = "pydantic-ai-slim", extras = ["openai", "google", "anthropic", "mistral", "retries"], specifier = ">=2.12.0" }, - { name = "pydantic-graph", specifier = ">=2.12.0" }, - { name = "pydantic-xml", specifier = ">=2.6.0" }, - { name = "pydocket", specifier = ">=0.16.1" }, - { name = "python-dotenv", specifier = ">=1.0.1" }, - { name = "python-telegram-bot", extras = ["socks"], marker = "extra == 'bot'", specifier = ">=21.0" }, - { name = "rich" }, - { name = "ripgrep-rs", specifier = ">=0.3.0" }, - { name = "rustworkx", marker = "extra == 'coding'", specifier = ">=0.17.1" }, - { name = "schemez", extras = ["codegen"] }, - { name = "searchly", extras = ["all"], specifier = ">=2.0.1" }, - { name = "slack-sdk", marker = "extra == 'bot'", specifier = ">=3.26.0" }, - { name = "slackify-markdown", marker = "extra == 'bot'", specifier = ">=0.2.0" }, - { name = "slashed", specifier = ">=0.1.0" }, - { name = "sqlalchemy", extras = ["aiosqlite"] }, - { name = "sqlmodel", specifier = ">=0.0.22" }, - { name = "starlette", specifier = ">=0.40" }, - { name = "starlette", marker = "extra == 'a2a'" }, - { name = "structlog", specifier = ">=25.5.0" }, - { name = "sublime-search", specifier = ">=0.3.1" }, - { name = "tiktoken", marker = "extra == 'tiktoken'" }, - { name = "tokonomics", specifier = ">=0.1.2" }, - { name = "toprompt", specifier = ">=0.0.1" }, - { name = "tree-sitter", marker = "extra == 'coding'", specifier = ">=0.25.2" }, - { name = "tree-sitter-c", marker = "extra == 'coding'", specifier = ">=0.24.1" }, - { name = "tree-sitter-cpp", marker = "extra == 'coding'", specifier = ">=0.23.0" }, - { name = "tree-sitter-go", marker = "extra == 'coding'", specifier = ">=0.23.0" }, - { name = "tree-sitter-javascript", marker = "extra == 'coding'", specifier = ">=0.25.0" }, - { name = "tree-sitter-json", marker = "extra == 'coding'", specifier = ">=0.24.0" }, - { name = "tree-sitter-python", marker = "extra == 'coding'", specifier = ">=0.25.0" }, - { name = "tree-sitter-rust", marker = "extra == 'coding'", specifier = ">=0.23.0" }, - { name = "tree-sitter-typescript", marker = "extra == 'coding'", specifier = ">=0.23.0" }, - { name = "tree-sitter-yaml", marker = "extra == 'coding'", specifier = ">=0.6.0" }, - { name = "typer" }, - { name = "upathtools", extras = ["httpx"], specifier = ">=0.1.0" }, - { name = "uvicorn", extras = ["standard"] }, - { name = "watchdog", marker = "extra == 'watchdog'", specifier = ">=4.0.0" }, - { name = "watchfiles", specifier = ">=1.1.1" }, - { name = "websockets", specifier = ">=15.0" }, - { name = "yamling", specifier = ">=2.0.2" }, - { name = "zstandard", marker = "extra == 'zed'", specifier = ">=0.23.0" }, -] -provides-extras = ["a2a", "bot", "braintrust", "clipboard", "coding", "composio", "events", "langfuse", "markitdown", "mcp-discovery", "mcp-run", "notifications", "promptlayer", "tiktoken", "tts", "watchdog", "zed", "viking"] - -[package.metadata.requires-dev] -benchmark = [{ name = "pyinstrument" }] -dev = [ - { name = "check-jsonschema", specifier = ">=0.35.0" }, - { name = "devtools" }, - { name = "dirty-equals", specifier = ">=0.9.0" }, - { name = "fastembed", marker = "python_full_version < '3.14'", specifier = ">=0.7.4" }, - { name = "import-linter", specifier = ">=2.12" }, - { name = "inline-snapshot", specifier = ">=0.13.0" }, - { name = "lancedb", marker = "python_full_version < '3.14'", specifier = ">=0.26.0" }, - { name = "openapi-spec-validator" }, - { name = "pyinstaller", specifier = ">=6.17.0" }, - { name = "pyreadline3" }, - { name = "pytest" }, - { name = "pytest-asyncio", specifier = ">=0.24.0" }, - { name = "pytest-cov" }, - { name = "pytest-recording", specifier = ">=0.13.0" }, - { name = "pytest-rerunfailures", specifier = ">=16.1" }, - { name = "pytest-timeout", specifier = ">=2.4.0" }, - { name = "pytest-xdist" }, - { name = "syrupy", specifier = ">=4.0.0" }, - { name = "vcrpy", specifier = ">=7.0.0" }, -] -docs = [ - { name = "beautifulsoup4", specifier = ">=4.14.3" }, - { name = "json-schema-for-humans", specifier = ">=1.0.0" }, - { name = "mkdocs-material" }, - { name = "mknodes" }, - { name = "zensical", specifier = ">=0.0.51" }, -] -lint = [ - { name = "mypy", extras = ["faster-cache"] }, - { name = "ruff" }, - { name = "ty" }, - { name = "types-croniter" }, - { name = "types-jsonschema", specifier = ">=4.25.1.20251009" }, - { name = "types-pygments" }, - { name = "types-pyyaml" }, +sdist = { url = "https://files.pythonhosted.org/packages/6c/5a/e858b0724d7637993d4a8c8a901d5c0e2611ee01d62222a55e97c96d79cf/agentpool-2.9.5.tar.gz", hash = "sha256:e3c60adbd77ea56c7639c533aba4ef3ec871730f807b55b5b693911c48f85121", size = 3858635 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/7c/690b675a468b10e09614ddc64944182c43f58828ae5adef1790e4dbfc14d/agentpool-2.9.5-py3-none-any.whl", hash = "sha256:81c5ae37e30e46e064b35ec73fb3233aaa0eeb547011c1a7f9a2914b48b27257", size = 4170375 }, ] [[package]] name = "aiofile" version = "3.11.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "caio" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/41/2fea7e193e061ce54eacc3b7bc0e6a99e4fcff43c78cf0a76dd781ed8334/aiofile-3.11.1.tar.gz", hash = "sha256:1f91912c6643d2a4e49ca4ae3514f0bf3867ce948a36d99a6411b8f4755f4cf9", size = 19342, upload-time = "2026-05-16T08:18:33.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/41/2fea7e193e061ce54eacc3b7bc0e6a99e4fcff43c78cf0a76dd781ed8334/aiofile-3.11.1.tar.gz", hash = "sha256:1f91912c6643d2a4e49ca4ae3514f0bf3867ce948a36d99a6411b8f4755f4cf9", size = 19342 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/cd/0d76dfc5de72bde52f55f53e925c7d152d9c7906634ec1e0cbc7e8d4ad93/aiofile-3.11.1-py3-none-any.whl", hash = "sha256:ce77d14ac07f77bc2b757834a5c129321f3f705c474593deed5ab209079a52c9", size = 20446, upload-time = "2026-05-16T08:18:32.051Z" }, + { url = "https://files.pythonhosted.org/packages/67/cd/0d76dfc5de72bde52f55f53e925c7d152d9c7906634ec1e0cbc7e8d4ad93/aiofile-3.11.1-py3-none-any.whl", hash = "sha256:ce77d14ac07f77bc2b757834a5c129321f3f705c474593deed5ab209079a52c9", size = 20446 }, ] [[package]] name = "aiohappyeyeballs" version = "2.7.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038 }, ] [[package]] name = "aiohttp" version = "3.14.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, { name = "aiosignal" }, @@ -357,117 +110,117 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/be/5afd201cc0ab139029aadb75392efe85a293403d9dd3a3226161c21ce00c/aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86", size = 506269, upload-time = "2026-07-23T01:54:49.075Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/09/dec8189d62b45ade009f6792a2264b942a90cb88aeaf181239933cd72c3c/aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627", size = 515166, upload-time = "2026-07-23T01:54:51.894Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/24/2854869d29ed8a8b19d74f9ec6629515f7e04d02dd329d9d179201e58e47/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82", size = 486263, upload-time = "2026-07-23T01:54:54.223Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/dd/57187c8be2a35aea65eaee3bd2c3dcbbcf0204f5106c89637e3610380cd1/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c", size = 492299, upload-time = "2026-07-23T01:54:56.236Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/11/06ae6ed8f0d414edf4068861e233d8fe23ee699bfd4b3ceb8663db948a62/aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f", size = 502235, upload-time = "2026-07-23T01:54:58.377Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/a3/559639c34a345d2cf7c52dff6838119f2eaf29eb508227b5b83f573af813/aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80", size = 750883, upload-time = "2026-07-23T01:55:00.65Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/cd/41e131f13afd1e7b0172a9d9eda085ef90eb8439f41f0d279db81ed3ae60/aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0", size = 508473, upload-time = "2026-07-23T01:55:02.945Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/6b/e7f13410d391c6e55b4c007a8de024355389d7d459e3d64c42b2d33617e5/aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf", size = 509190, upload-time = "2026-07-23T01:55:05.173Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/21/6464573e53d69672cc1eada3e5c5cb2d2efa82701e8305a0f2047a576967/aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd", size = 1761478, upload-time = "2026-07-23T01:55:07.383Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/81/d217043a4c17fbce360905e3b2bdd20139ebc9a2de836d035d179c4da006/aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807", size = 1735092, upload-time = "2026-07-23T01:55:09.803Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/66/e13a02d0eeb1a9a502402a977abb4e4abff9fe4051c26f80558c57a7c975/aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8", size = 1800546, upload-time = "2026-07-23T01:55:12.012Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/5e/57d42fca1d18cb5acc1cad945d017fabc5d6ae71d8a08ad66be8dc3ee544/aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24", size = 1895250, upload-time = "2026-07-23T01:55:14.357Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/1c/7da8d08e74d56f00070822f9638ff3f1c563f8ad87d1efa996c87bfc8644/aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5", size = 1789289, upload-time = "2026-07-23T01:55:16.668Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/0f/cf16bcf56896981c1a0319f5d5db9337994b5165730c48a8fa07e9b34be6/aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4", size = 1586706, upload-time = "2026-07-23T01:55:18.913Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/6f/76eac12a7f2480e1e304f842efdb07db33256b0d9165b866b6ef0806c202/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9", size = 1724652, upload-time = "2026-07-23T01:55:21.296Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/b6/19c8c592baeeb94b75f966547d40c02ac7590902306ec5863d5c027cf506/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1", size = 1756239, upload-time = "2026-07-23T01:55:23.705Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/c9/4e9383150296f97f873b680c4de8fb2cd88608fb9f48c79edcb111611abc/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371", size = 1769161, upload-time = "2026-07-23T01:55:26.082Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/1e/147bdc6cc5de5f3ab011be8bf5d6e786633249f22c20bae06f85e45f5387/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde", size = 1578759, upload-time = "2026-07-23T01:55:28.846Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/31/78388a9d6040ece2e11df62ea229a822cf5e52d238374b220ae9975b2623/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e", size = 1792025, upload-time = "2026-07-23T01:55:31.457Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/51/a3d29fdf2c25d796746af8ad6fe56a45d6256c38b0a8a2ed752e1160b3a2/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71", size = 1768477, upload-time = "2026-07-23T01:55:33.87Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/a6/442e18b5afeade534d877a2dc3c3e392aff8d49787890b0cf84790410267/aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0", size = 451069, upload-time = "2026-07-23T01:55:36.121Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/69/3d876ac02659f271cf7f6769f14a8e3de5b6e888ed8b5a7e998086a4cec8/aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883", size = 476518, upload-time = "2026-07-23T01:55:38.303Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/0e/50d6e6471cd31edce8b282bdec59375a3a69124d8a989a0b1313355cae52/aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2", size = 447676, upload-time = "2026-07-23T01:55:40.451Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/be/5afd201cc0ab139029aadb75392efe85a293403d9dd3a3226161c21ce00c/aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86", size = 506269 }, + { url = "https://files.pythonhosted.org/packages/22/09/dec8189d62b45ade009f6792a2264b942a90cb88aeaf181239933cd72c3c/aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627", size = 515166 }, + { url = "https://files.pythonhosted.org/packages/28/24/2854869d29ed8a8b19d74f9ec6629515f7e04d02dd329d9d179201e58e47/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82", size = 486263 }, + { url = "https://files.pythonhosted.org/packages/d4/dd/57187c8be2a35aea65eaee3bd2c3dcbbcf0204f5106c89637e3610380cd1/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c", size = 492299 }, + { url = "https://files.pythonhosted.org/packages/b9/11/06ae6ed8f0d414edf4068861e233d8fe23ee699bfd4b3ceb8663db948a62/aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f", size = 502235 }, + { url = "https://files.pythonhosted.org/packages/7e/a3/559639c34a345d2cf7c52dff6838119f2eaf29eb508227b5b83f573af813/aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80", size = 750883 }, + { url = "https://files.pythonhosted.org/packages/91/cd/41e131f13afd1e7b0172a9d9eda085ef90eb8439f41f0d279db81ed3ae60/aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0", size = 508473 }, + { url = "https://files.pythonhosted.org/packages/bc/6b/e7f13410d391c6e55b4c007a8de024355389d7d459e3d64c42b2d33617e5/aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf", size = 509190 }, + { url = "https://files.pythonhosted.org/packages/97/21/6464573e53d69672cc1eada3e5c5cb2d2efa82701e8305a0f2047a576967/aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd", size = 1761478 }, + { url = "https://files.pythonhosted.org/packages/1a/81/d217043a4c17fbce360905e3b2bdd20139ebc9a2de836d035d179c4da006/aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807", size = 1735092 }, + { url = "https://files.pythonhosted.org/packages/a1/66/e13a02d0eeb1a9a502402a977abb4e4abff9fe4051c26f80558c57a7c975/aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8", size = 1800546 }, + { url = "https://files.pythonhosted.org/packages/26/5e/57d42fca1d18cb5acc1cad945d017fabc5d6ae71d8a08ad66be8dc3ee544/aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24", size = 1895250 }, + { url = "https://files.pythonhosted.org/packages/ca/1c/7da8d08e74d56f00070822f9638ff3f1c563f8ad87d1efa996c87bfc8644/aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5", size = 1789289 }, + { url = "https://files.pythonhosted.org/packages/cd/0f/cf16bcf56896981c1a0319f5d5db9337994b5165730c48a8fa07e9b34be6/aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4", size = 1586706 }, + { url = "https://files.pythonhosted.org/packages/fe/6f/76eac12a7f2480e1e304f842efdb07db33256b0d9165b866b6ef0806c202/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9", size = 1724652 }, + { url = "https://files.pythonhosted.org/packages/39/b6/19c8c592baeeb94b75f966547d40c02ac7590902306ec5863d5c027cf506/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1", size = 1756239 }, + { url = "https://files.pythonhosted.org/packages/dc/c9/4e9383150296f97f873b680c4de8fb2cd88608fb9f48c79edcb111611abc/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371", size = 1769161 }, + { url = "https://files.pythonhosted.org/packages/aa/1e/147bdc6cc5de5f3ab011be8bf5d6e786633249f22c20bae06f85e45f5387/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde", size = 1578759 }, + { url = "https://files.pythonhosted.org/packages/fd/31/78388a9d6040ece2e11df62ea229a822cf5e52d238374b220ae9975b2623/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e", size = 1792025 }, + { url = "https://files.pythonhosted.org/packages/03/51/a3d29fdf2c25d796746af8ad6fe56a45d6256c38b0a8a2ed752e1160b3a2/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71", size = 1768477 }, + { url = "https://files.pythonhosted.org/packages/29/a6/442e18b5afeade534d877a2dc3c3e392aff8d49787890b0cf84790410267/aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0", size = 451069 }, + { url = "https://files.pythonhosted.org/packages/9d/69/3d876ac02659f271cf7f6769f14a8e3de5b6e888ed8b5a7e998086a4cec8/aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883", size = 476518 }, + { url = "https://files.pythonhosted.org/packages/b2/0e/50d6e6471cd31edce8b282bdec59375a3a69124d8a989a0b1313355cae52/aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2", size = 447676 }, ] [[package]] name = "aioimaplib" version = "2.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/da/a454c47fb8522e607425e15bf1f49ccfdb3d75f4071f40b63ebd49573495/aioimaplib-2.0.1.tar.gz", hash = "sha256:5a494c3b75f220977048f5eb2c7ba9c0570a3148aaf38bee844e37e4d7af8648", size = 35555, upload-time = "2025-01-16T10:38:23.14Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/da/a454c47fb8522e607425e15bf1f49ccfdb3d75f4071f40b63ebd49573495/aioimaplib-2.0.1.tar.gz", hash = "sha256:5a494c3b75f220977048f5eb2c7ba9c0570a3148aaf38bee844e37e4d7af8648", size = 35555 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/52/48aaa287fb3c4c995edcb602370b10d182dc5c48371df7cb3a404356733f/aioimaplib-2.0.1-py3-none-any.whl", hash = "sha256:727e00c35cf25106bd34611dddd6e2ddf91a5f1a7e72d9269f3ce62486b31e14", size = 34729, upload-time = "2025-01-16T10:38:20.427Z" }, + { url = "https://files.pythonhosted.org/packages/13/52/48aaa287fb3c4c995edcb602370b10d182dc5c48371df7cb3a404356733f/aioimaplib-2.0.1-py3-none-any.whl", hash = "sha256:727e00c35cf25106bd34611dddd6e2ddf91a5f1a7e72d9269f3ce62486b31e14", size = 34729 }, ] [[package]] name = "aioitertools" version = "0.13.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322, upload-time = "2025-11-06T22:17:07.609Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182, upload-time = "2025-11-06T22:17:06.502Z" }, + { url = "https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182 }, ] [[package]] name = "aiosignal" version = "1.4.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, ] [[package]] name = "aiosqlite" version = "0.22.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821, upload-time = "2025-12-23T19:25:43.997Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405, upload-time = "2025-12-23T19:25:42.139Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405 }, ] [[package]] name = "alembic" version = "1.18.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/cc/ac0bed8e562e7407fe55c3ba85a4dce86e6dbd8730887bd1e406a6c5c18a/alembic-1.18.5.tar.gz", hash = "sha256:1554982221dd17e9a749b53902407578eb305e453f71999e8c7f0a48389fff8e", size = 2060480, upload-time = "2026-06-25T15:20:54.888Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/cc/ac0bed8e562e7407fe55c3ba85a4dce86e6dbd8730887bd1e406a6c5c18a/alembic-1.18.5.tar.gz", hash = "sha256:1554982221dd17e9a749b53902407578eb305e453f71999e8c7f0a48389fff8e", size = 2060480 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/78/5fe6dc3a3a5b2f5a2a4faef8bfe336d5fa049a38884ab3172e0098160c01/alembic-1.18.5-py3-none-any.whl", hash = "sha256:06d8ba9d04558022f5395e9317de03d270f3dced49cee01f89fe7a13c26f14bc", size = 264664, upload-time = "2026-06-25T15:20:56.673Z" }, + { url = "https://files.pythonhosted.org/packages/96/78/5fe6dc3a3a5b2f5a2a4faef8bfe336d5fa049a38884ab3172e0098160c01/alembic-1.18.5-py3-none-any.whl", hash = "sha256:06d8ba9d04558022f5395e9317de03d270f3dced49cee01f89fe7a13c26f14bc", size = 264664 }, ] [[package]] name = "altgraph" version = "0.17.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428, upload-time = "2025-11-21T20:35:50.583Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228, upload-time = "2025-11-21T20:35:49.444Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228 }, ] [[package]] name = "annotated-doc" version = "0.0.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, ] [[package]] name = "annotated-types" version = "0.8.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427 }, ] [[package]] name = "anthropic" version = "0.119.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -478,36 +231,36 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/c5/f1d53b7d27b25a7f491d39c5cd9d39805be2d115d7f7431a8bf22f286afe/anthropic-0.119.0.tar.gz", hash = "sha256:40a81b094ae2a0056a77771257943b19bd46edce1326e22f85c2794492097f0a", size = 999050, upload-time = "2026-07-23T17:34:40.233Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/c5/f1d53b7d27b25a7f491d39c5cd9d39805be2d115d7f7431a8bf22f286afe/anthropic-0.119.0.tar.gz", hash = "sha256:40a81b094ae2a0056a77771257943b19bd46edce1326e22f85c2794492097f0a", size = 999050 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/2e/5e90df53b474e856a3ae065c0df77e62bf58cb2df8819ec850d08b82518a/anthropic-0.119.0-py3-none-any.whl", hash = "sha256:2b39107077489d19d7c66000e9d9263293d431550fcf6b543ead785c6ac85962", size = 1012756, upload-time = "2026-07-23T17:34:41.896Z" }, + { url = "https://files.pythonhosted.org/packages/dc/2e/5e90df53b474e856a3ae065c0df77e62bf58cb2df8819ec850d08b82518a/anthropic-0.119.0-py3-none-any.whl", hash = "sha256:2b39107077489d19d7c66000e9d9263293d431550fcf6b543ead785c6ac85962", size = 1012756 }, ] [[package]] name = "anybadge" version = "1.16.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/08/ddad0d5398d0961d506b0489e737a06e29a963eff0f2f0a2bb2cfb36dd1f/anybadge-1.16.0.tar.gz", hash = "sha256:f4e95eca834482f9932f9020ac2fe04a5ca863728b446324a8d35b1e67faab71", size = 34616, upload-time = "2025-01-11T23:03:27.966Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/08/ddad0d5398d0961d506b0489e737a06e29a963eff0f2f0a2bb2cfb36dd1f/anybadge-1.16.0.tar.gz", hash = "sha256:f4e95eca834482f9932f9020ac2fe04a5ca863728b446324a8d35b1e67faab71", size = 34616 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/7d/01b2ac2fec808dea667b8678938156c3910219f2c45ee2e0b01e72786d72/anybadge-1.16.0-py3-none-any.whl", hash = "sha256:bc9ef2e20d875ee09237a15250a17b6fd7e67276f083d32a297963cdec179918", size = 28412, upload-time = "2025-01-11T23:03:24.857Z" }, + { url = "https://files.pythonhosted.org/packages/83/7d/01b2ac2fec808dea667b8678938156c3910219f2c45ee2e0b01e72786d72/anybadge-1.16.0-py3-none-any.whl", hash = "sha256:bc9ef2e20d875ee09237a15250a17b6fd7e67276f083d32a297963cdec179918", size = 28412 }, ] [[package]] name = "anyenv" version = "2.0.15" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aioitertools" }, { name = "anyio" }, { name = "appdirs" }, { name = "universal-pathlib" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/79/470d6fc8b16172292fd27d9c5ed112fc5efd0fa6c6636c9828071c21743f/anyenv-2.0.15.tar.gz", hash = "sha256:e55d27982a42099034d1ea424f943255d61b9dcc4e073dd1b574993c4d09acae", size = 97848, upload-time = "2026-01-12T14:27:58.99Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/79/470d6fc8b16172292fd27d9c5ed112fc5efd0fa6c6636c9828071c21743f/anyenv-2.0.15.tar.gz", hash = "sha256:e55d27982a42099034d1ea424f943255d61b9dcc4e073dd1b574993c4d09acae", size = 97848 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/32/d7fbc87a89fe8f1e2ab871a8cc02839e900ac440c118d2101e12e322d5eb/anyenv-2.0.15-py3-none-any.whl", hash = "sha256:eba488219d9865b0ea2bdd120f6e41c0ab33619185d8ba20a5fa28148d3c030f", size = 161016, upload-time = "2026-01-12T14:28:01.283Z" }, + { url = "https://files.pythonhosted.org/packages/d4/32/d7fbc87a89fe8f1e2ab871a8cc02839e900ac440c118d2101e12e322d5eb/anyenv-2.0.15-py3-none-any.whl", hash = "sha256:eba488219d9865b0ea2bdd120f6e41c0ab33619185d8ba20a5fa28148d3c030f", size = 161016 }, ] [package.optional-dependencies] @@ -519,38 +272,38 @@ httpx = [ [[package]] name = "anyio" version = "4.14.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813 }, ] [[package]] name = "anysqlite" version = "0.0.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/4b/cd5d66b9f87e773bc71344a368b9472987e33514e6627e28342b9c3e7c43/anysqlite-0.0.5.tar.gz", hash = "sha256:9dfcf87baf6b93426ad1d9118088c41dbf24ef01b445eea4a5d486bac2755cce" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4b/cd5d66b9f87e773bc71344a368b9472987e33514e6627e28342b9c3e7c43/anysqlite-0.0.5.tar.gz", hash = "sha256:9dfcf87baf6b93426ad1d9118088c41dbf24ef01b445eea4a5d486bac2755cce", size = 3432 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/31/349eae2bc9d9331dd8951684cf94528d91efaa71129dc30822ac111dfc66/anysqlite-0.0.5-py3-none-any.whl", hash = "sha256:cb345dc4f76f6b37f768d7a0b3e9cf5c700dfcb7a6356af8ab46a11f666edbe7" }, + { url = "https://files.pythonhosted.org/packages/0b/31/349eae2bc9d9331dd8951684cf94528d91efaa71129dc30822ac111dfc66/anysqlite-0.0.5-py3-none-any.whl", hash = "sha256:cb345dc4f76f6b37f768d7a0b3e9cf5c700dfcb7a6356af8ab46a11f666edbe7", size = 3907 }, ] [[package]] name = "anyvoice" version = "0.0.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "schemez" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/6d/b875a56c88b83b2707f02b4afc38c77e2fc3acba3519c64a114681e9edca/anyvoice-0.0.2.tar.gz", hash = "sha256:0a45e8d20b2e26e115c86ce3b8b64f37702d44bfd7ef3f02b518452d25797e35", size = 11072, upload-time = "2025-12-24T15:48:03.869Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6d/b875a56c88b83b2707f02b4afc38c77e2fc3acba3519c64a114681e9edca/anyvoice-0.0.2.tar.gz", hash = "sha256:0a45e8d20b2e26e115c86ce3b8b64f37702d44bfd7ef3f02b518452d25797e35", size = 11072 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/7e/9d782e979af721ea066123a83ae6aa391f050c98bb59a2f3696b90560dd1/anyvoice-0.0.2-py3-none-any.whl", hash = "sha256:d2fb884c3d755f115a74a850ce0f7e1098cb71d6d663049cc31322792f5dc3e7", size = 12813, upload-time = "2025-12-24T15:48:02.203Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7e/9d782e979af721ea066123a83ae6aa391f050c98bb59a2f3696b90560dd1/anyvoice-0.0.2-py3-none-any.whl", hash = "sha256:d2fb884c3d755f115a74a850ce0f7e1098cb71d6d663049cc31322792f5dc3e7", size = 12813 }, ] [package.optional-dependencies] @@ -568,16 +321,16 @@ tts-edge = [ [[package]] name = "appdirs" version = "1.4.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566 }, ] [[package]] name = "apprise" version = "1.12.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "click" }, @@ -587,182 +340,182 @@ dependencies = [ { name = "requests-oauthlib" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/f7/95a52cda9355f32c5db9ef77bac18e85977472d29c555fafd825cf60c309/apprise-1.12.0.tar.gz", hash = "sha256:ffe9860d99cbde05fd156c610d4bcb49d953200c4ed3cb2db001ed50722fc142", size = 2477874, upload-time = "2026-07-04T16:15:36.67Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/f7/95a52cda9355f32c5db9ef77bac18e85977472d29c555fafd825cf60c309/apprise-1.12.0.tar.gz", hash = "sha256:ffe9860d99cbde05fd156c610d4bcb49d953200c4ed3cb2db001ed50722fc142", size = 2477874 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/e9/acfea54abdd66a059ff82995d3703eca3a27df3dc1bdc6afaf8a30a854fe/apprise-1.12.0-py3-none-any.whl", hash = "sha256:28edabfec5a9d5dbcd9aa28bdfd8928ecb68764e40214b6e648eab6d974b5a93", size = 1797824, upload-time = "2026-07-04T16:15:34.228Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e9/acfea54abdd66a059ff82995d3703eca3a27df3dc1bdc6afaf8a30a854fe/apprise-1.12.0-py3-none-any.whl", hash = "sha256:28edabfec5a9d5dbcd9aa28bdfd8928ecb68764e40214b6e648eab6d974b5a93", size = 1797824 }, ] [[package]] name = "argcomplete" version = "3.7.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/c0/c8e94135e66fabf89a120d9b4b123fe6993506beca6c1938a74c24cfa5fd/argcomplete-3.7.0.tar.gz", hash = "sha256:afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913", size = 73284, upload-time = "2026-06-30T22:28:22.249Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/c0/c8e94135e66fabf89a120d9b4b123fe6993506beca6c1938a74c24cfa5fd/argcomplete-3.7.0.tar.gz", hash = "sha256:afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913", size = 73284 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/f6/5b8ec087cd9cfa9449491ec83f76fb6b7006b4dff57d2ba8aaab330fe8e4/argcomplete-3.7.0-py3-none-any.whl", hash = "sha256:d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c", size = 42575, upload-time = "2026-06-30T22:28:20.547Z" }, + { url = "https://files.pythonhosted.org/packages/12/f6/5b8ec087cd9cfa9449491ec83f76fb6b7006b4dff57d2ba8aaab330fe8e4/argcomplete-3.7.0-py3-none-any.whl", hash = "sha256:d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c", size = 42575 }, ] [[package]] name = "asgiref" version = "3.12.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/26/3b59f2bdae5f640389becb1f673cded775287f5fc4f816309d9ca9a3f93d/asgiref-3.12.1.tar.gz", hash = "sha256:59dcb51c272ad209d59bed5708a64a333083e86017d7fcdd67498eeab7784340", size = 42378, upload-time = "2026-07-14T09:56:18.087Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/26/3b59f2bdae5f640389becb1f673cded775287f5fc4f816309d9ca9a3f93d/asgiref-3.12.1.tar.gz", hash = "sha256:59dcb51c272ad209d59bed5708a64a333083e86017d7fcdd67498eeab7784340", size = 42378 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/1b/54f4ad77cd8a584fa70746c47df988e002cf1ee1eba43364d46f87803647/asgiref-3.12.1-py3-none-any.whl", hash = "sha256:fe386d1c2bff7259ea95929266d12a8cf9a8b5a1c2598402967d8792e7a7c094", size = 25478, upload-time = "2026-07-14T09:56:16.926Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/54f4ad77cd8a584fa70746c47df988e002cf1ee1eba43364d46f87803647/asgiref-3.12.1-py3-none-any.whl", hash = "sha256:fe386d1c2bff7259ea95929266d12a8cf9a8b5a1c2598402967d8792e7a7c094", size = 25478 }, ] [[package]] name = "ast-grep-py" version = "0.45.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/01/675c994c5309f59f0a3a1a1f8e00beb5a40bc51c45aeffafb27b00d51743/ast_grep_py-0.45.0.tar.gz", hash = "sha256:f96ec0175c296a04b2119ef4ff526aa14573db2a2dd35d24b22175390b2cbab9", size = 155313, upload-time = "2026-07-23T16:20:59.22Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/01/675c994c5309f59f0a3a1a1f8e00beb5a40bc51c45aeffafb27b00d51743/ast_grep_py-0.45.0.tar.gz", hash = "sha256:f96ec0175c296a04b2119ef4ff526aa14573db2a2dd35d24b22175390b2cbab9", size = 155313 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/30/15ac9b3eb165b7b4223beaf35814cb5791482360c08708bafcdf572c8a05/ast_grep_py-0.45.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4fdba29578f1b34d9186d6a446a2c687e683cb409df6bbf5d7d60b8dcdc7abee", size = 5358555, upload-time = "2026-07-23T16:20:39.165Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/be/da3954eaf57e57451747b5f5581acdd96bc16d49d57d09748fdf895e3259/ast_grep_py-0.45.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b04d1bd7f0144e9ab23a1eeab6b271288a59938d2864e3afeb4ed4dd74fddfe0", size = 5530668, upload-time = "2026-07-23T16:20:40.677Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/d1/d06a16325268bb66565497b4dc2f6b69670ba5ea5825826331e6cc724cc5/ast_grep_py-0.45.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:8237ee9049afc02cb40f090928db283a1f0843dee93c41f747ec978571e514c5", size = 5346125, upload-time = "2026-07-23T16:20:42.45Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/b7/64d9b9ff8b950890a86ea94b8a669e73fd14318f958ec50a19a72ed85ed0/ast_grep_py-0.45.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:dfda851a6be563a86518c93bb8be0bdc5a400ba262ea56fd2a2d9619c70b3f81", size = 5458633, upload-time = "2026-07-23T16:20:43.977Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/c0/02157953adc01798d73210bccb0f1102a9817137f23b74df3f0c725ff210/ast_grep_py-0.45.0-cp313-cp313-win32.whl", hash = "sha256:d6f1cdadd015a9b0806403fcb44c921ae40bb615fe96ce8e28b0957b6139efb2", size = 5070101, upload-time = "2026-07-23T16:20:45.654Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/4f/9673e3a7b7dcc12e1118e813a01a6b380ab6c7d05e81175fbcfc91465cde/ast_grep_py-0.45.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df945e6c112b6d21860c0d074f780dad6a25c72cb1988c40eb66b7ed701272b", size = 5206071, upload-time = "2026-07-23T16:20:47.181Z" }, + { url = "https://files.pythonhosted.org/packages/f1/30/15ac9b3eb165b7b4223beaf35814cb5791482360c08708bafcdf572c8a05/ast_grep_py-0.45.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4fdba29578f1b34d9186d6a446a2c687e683cb409df6bbf5d7d60b8dcdc7abee", size = 5358555 }, + { url = "https://files.pythonhosted.org/packages/34/be/da3954eaf57e57451747b5f5581acdd96bc16d49d57d09748fdf895e3259/ast_grep_py-0.45.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b04d1bd7f0144e9ab23a1eeab6b271288a59938d2864e3afeb4ed4dd74fddfe0", size = 5530668 }, + { url = "https://files.pythonhosted.org/packages/8d/d1/d06a16325268bb66565497b4dc2f6b69670ba5ea5825826331e6cc724cc5/ast_grep_py-0.45.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:8237ee9049afc02cb40f090928db283a1f0843dee93c41f747ec978571e514c5", size = 5346125 }, + { url = "https://files.pythonhosted.org/packages/7b/b7/64d9b9ff8b950890a86ea94b8a669e73fd14318f958ec50a19a72ed85ed0/ast_grep_py-0.45.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:dfda851a6be563a86518c93bb8be0bdc5a400ba262ea56fd2a2d9619c70b3f81", size = 5458633 }, + { url = "https://files.pythonhosted.org/packages/8c/c0/02157953adc01798d73210bccb0f1102a9817137f23b74df3f0c725ff210/ast_grep_py-0.45.0-cp313-cp313-win32.whl", hash = "sha256:d6f1cdadd015a9b0806403fcb44c921ae40bb615fe96ce8e28b0957b6139efb2", size = 5070101 }, + { url = "https://files.pythonhosted.org/packages/2b/4f/9673e3a7b7dcc12e1118e813a01a6b380ab6c7d05e81175fbcfc91465cde/ast_grep_py-0.45.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df945e6c112b6d21860c0d074f780dad6a25c72cb1988c40eb66b7ed701272b", size = 5206071 }, ] [[package]] name = "ast-serialize" version = "0.6.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/ad/0d70a3a2d6e01968d985415259e8ec7ad3f777903f9b1c1f3c8c44642c60/ast_serialize-0.6.0.tar.gz", hash = "sha256:aadd3ffcf4858c9726bf3515f7b199c7eadbe504f96028e4a87172c0da65a8fe", size = 61489, upload-time = "2026-06-30T20:02:55.555Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/19/ac8348ae8711c9b5ae834634f635780cab62a0f5e6f988882e048b89c2ae/ast_serialize-0.6.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:093cb8bb91b720d8523580498d031791bb1bbaa048599c3d21085d380e11a596", size = 1185367, upload-time = "2026-06-30T20:02:30.427Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/f6/ec7ec652c51db77c2f61d8573338e13e4704303265ccc658cb4031d9f354/ast_serialize-0.6.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e61580a69faf47e3689795367ed211f2a10fd741478cc0f36a0f128793360aad", size = 1178657, upload-time = "2026-06-30T20:02:31.964Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/02/613a7534a41d0122f37d1e0c64aa8ac78bfb831f8c92f6db057a311abb3c/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305802f2ce2a7c4e87835078ea85c58b586ddda8095b92fe2ead9364ae19c80a", size = 1238620, upload-time = "2026-06-30T20:02:33.664Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/21/087957bba486242afc52f49b2d9e21c9dad00289356cf9efe67084015a9d/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7b8b8f0c42f752ea00b2b7d7c090b3f80d9c1c5c75cadf16423790a0cc74081", size = 1236075, upload-time = "2026-06-30T20:02:34.936Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/04/78128bbb170071c2c72a210a181f1c00e11cc1cec60a8beef747b07f9201/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd5b91b9e6f2356ace3a556963b0cd783b395fbbb0bb17b4defc283415466e77", size = 1441348, upload-time = "2026-06-30T20:02:36.245Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/64/62fb99d6faf199b4c3e5b08a07136e9a0d7664bb249c6de3670e5b63e9b6/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d6ef91590258ada18909b9caea344dac4de2013906b035473cd674a43f4b790", size = 1258580, upload-time = "2026-06-30T20:02:37.53Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/87/b4d6c38e0ccd5e85dc54cecdf933a152c60b28fe5d993a6d8a72fa6d5896/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcbed41e9386059fc0261d602445ede0976c2ecec2939688bcbcb9ed0b6f28b7", size = 1261693, upload-time = "2026-06-30T20:02:39.123Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/4b/3676ca2191f39bafb75f93f99b2f429ec464586158fece2165f3572805dc/ast_serialize-0.6.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:cdc4e6f930b9090c2f92c9036ad12ffb8e6e44d4a5ba06f1458a05d60f203f7b", size = 1252517, upload-time = "2026-06-30T20:02:40.511Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/58/494ef8c4b4acb2f4a265ac934caf45f792a08fe27d6b853de35ad991941a/ast_serialize-0.6.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:897ac47b5637be41c0c07061c8a912fafa967ef1dc73fa115e4bfa70882a093b", size = 1304843, upload-time = "2026-06-30T20:02:41.961Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/f2/13736d920ab3d49bbee80ef1a277dd7b7aaf3b3545efd9d2a8114fe05525/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c4af9a1386166e40ed01464991806f89038a2d89782576c7774876fa77034e32", size = 1413698, upload-time = "2026-06-30T20:02:44.179Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/5a/e046f3899e2acba4677d7427b76431443a1aa1a0e583dfb05b55b69d55cf/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c901adbd750029b9ac4ad3d6aa56853e0ad4875119fbf52b7b8298afc223828b", size = 1512209, upload-time = "2026-06-30T20:02:45.584Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/c7/e42aaca7bb2d22a7c06d5a8c7930086c5a334e93d716e6fa5e6647a4515f/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae22a366b752ab4496191525b78b097b5b72d531752e3c1dd7e383a8f2c8a1a", size = 1508464, upload-time = "2026-06-30T20:02:46.942Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/93/5524a3dc6c3f593de3228ed9cbef73afa047625b7000ec21b7f58e6eb4d4/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ed29121da8b3fdc291002801a1de0f76248fa07dce89157a5f277842cf6126e", size = 1457164, upload-time = "2026-06-30T20:02:48.294Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/c0/36a6ffb4d653cf621427b4c4928671f53ad800c453474de2b82564a44ad9/ast_serialize-0.6.0-cp39-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b1dac4e09d341c1300ba69cdcbe62867b32a8c75d90db9bf4d083bec3b039f0b", size = 863014, upload-time = "2026-06-30T20:02:49.742Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/c7/7d5ad8b49e1278e1c2a1e0274bd7850560b3f09313aa00c13bc8d5544792/ast_serialize-0.6.0-cp39-abi3-win32.whl", hash = "sha256:82c312a7844d2fdeb4d5c48bd3d215bf940dafd4704e1a9bcf252a99010a99b1", size = 1063165, upload-time = "2026-06-30T20:02:50.98Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/ae/6710c14ecb276031cf10249f6adf5a59e2d3fdb3b5183bd59f70524067ee/ast_serialize-0.6.0-cp39-abi3-win_amd64.whl", hash = "sha256:113b58346f9ceb664352032770caca817d4a3c86f611c6088e6ef65ddaa70f0e", size = 1101444, upload-time = "2026-06-30T20:02:52.554Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965, upload-time = "2026-06-30T20:02:54.097Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/ad/0d70a3a2d6e01968d985415259e8ec7ad3f777903f9b1c1f3c8c44642c60/ast_serialize-0.6.0.tar.gz", hash = "sha256:aadd3ffcf4858c9726bf3515f7b199c7eadbe504f96028e4a87172c0da65a8fe", size = 61489 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/19/ac8348ae8711c9b5ae834634f635780cab62a0f5e6f988882e048b89c2ae/ast_serialize-0.6.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:093cb8bb91b720d8523580498d031791bb1bbaa048599c3d21085d380e11a596", size = 1185367 }, + { url = "https://files.pythonhosted.org/packages/c1/f6/ec7ec652c51db77c2f61d8573338e13e4704303265ccc658cb4031d9f354/ast_serialize-0.6.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e61580a69faf47e3689795367ed211f2a10fd741478cc0f36a0f128793360aad", size = 1178657 }, + { url = "https://files.pythonhosted.org/packages/6f/02/613a7534a41d0122f37d1e0c64aa8ac78bfb831f8c92f6db057a311abb3c/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305802f2ce2a7c4e87835078ea85c58b586ddda8095b92fe2ead9364ae19c80a", size = 1238620 }, + { url = "https://files.pythonhosted.org/packages/4d/21/087957bba486242afc52f49b2d9e21c9dad00289356cf9efe67084015a9d/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7b8b8f0c42f752ea00b2b7d7c090b3f80d9c1c5c75cadf16423790a0cc74081", size = 1236075 }, + { url = "https://files.pythonhosted.org/packages/82/04/78128bbb170071c2c72a210a181f1c00e11cc1cec60a8beef747b07f9201/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd5b91b9e6f2356ace3a556963b0cd783b395fbbb0bb17b4defc283415466e77", size = 1441348 }, + { url = "https://files.pythonhosted.org/packages/64/64/62fb99d6faf199b4c3e5b08a07136e9a0d7664bb249c6de3670e5b63e9b6/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d6ef91590258ada18909b9caea344dac4de2013906b035473cd674a43f4b790", size = 1258580 }, + { url = "https://files.pythonhosted.org/packages/ca/87/b4d6c38e0ccd5e85dc54cecdf933a152c60b28fe5d993a6d8a72fa6d5896/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcbed41e9386059fc0261d602445ede0976c2ecec2939688bcbcb9ed0b6f28b7", size = 1261693 }, + { url = "https://files.pythonhosted.org/packages/0e/4b/3676ca2191f39bafb75f93f99b2f429ec464586158fece2165f3572805dc/ast_serialize-0.6.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:cdc4e6f930b9090c2f92c9036ad12ffb8e6e44d4a5ba06f1458a05d60f203f7b", size = 1252517 }, + { url = "https://files.pythonhosted.org/packages/f3/58/494ef8c4b4acb2f4a265ac934caf45f792a08fe27d6b853de35ad991941a/ast_serialize-0.6.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:897ac47b5637be41c0c07061c8a912fafa967ef1dc73fa115e4bfa70882a093b", size = 1304843 }, + { url = "https://files.pythonhosted.org/packages/b1/f2/13736d920ab3d49bbee80ef1a277dd7b7aaf3b3545efd9d2a8114fe05525/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c4af9a1386166e40ed01464991806f89038a2d89782576c7774876fa77034e32", size = 1413698 }, + { url = "https://files.pythonhosted.org/packages/a8/5a/e046f3899e2acba4677d7427b76431443a1aa1a0e583dfb05b55b69d55cf/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c901adbd750029b9ac4ad3d6aa56853e0ad4875119fbf52b7b8298afc223828b", size = 1512209 }, + { url = "https://files.pythonhosted.org/packages/cc/c7/e42aaca7bb2d22a7c06d5a8c7930086c5a334e93d716e6fa5e6647a4515f/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae22a366b752ab4496191525b78b097b5b72d531752e3c1dd7e383a8f2c8a1a", size = 1508464 }, + { url = "https://files.pythonhosted.org/packages/95/93/5524a3dc6c3f593de3228ed9cbef73afa047625b7000ec21b7f58e6eb4d4/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ed29121da8b3fdc291002801a1de0f76248fa07dce89157a5f277842cf6126e", size = 1457164 }, + { url = "https://files.pythonhosted.org/packages/4f/c0/36a6ffb4d653cf621427b4c4928671f53ad800c453474de2b82564a44ad9/ast_serialize-0.6.0-cp39-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b1dac4e09d341c1300ba69cdcbe62867b32a8c75d90db9bf4d083bec3b039f0b", size = 863014 }, + { url = "https://files.pythonhosted.org/packages/09/c7/7d5ad8b49e1278e1c2a1e0274bd7850560b3f09313aa00c13bc8d5544792/ast_serialize-0.6.0-cp39-abi3-win32.whl", hash = "sha256:82c312a7844d2fdeb4d5c48bd3d215bf940dafd4704e1a9bcf252a99010a99b1", size = 1063165 }, + { url = "https://files.pythonhosted.org/packages/47/ae/6710c14ecb276031cf10249f6adf5a59e2d3fdb3b5183bd59f70524067ee/ast_serialize-0.6.0-cp39-abi3-win_amd64.whl", hash = "sha256:113b58346f9ceb664352032770caca817d4a3c86f611c6088e6ef65ddaa70f0e", size = 1101444 }, + { url = "https://files.pythonhosted.org/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965 }, ] [[package]] name = "asttokens" version = "2.4.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/1d/f03bcb60c4a3212e15f99a56085d93093a497718adf828d050b9d675da81/asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0", size = 62284, upload-time = "2023-10-26T10:03:05.06Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/1d/f03bcb60c4a3212e15f99a56085d93093a497718adf828d050b9d675da81/asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0", size = 62284 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764, upload-time = "2023-10-26T10:03:01.789Z" }, + { url = "https://files.pythonhosted.org/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764 }, ] [[package]] name = "attrs" version = "26.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548 }, ] [[package]] name = "authlib" version = "1.7.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "joserfc" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511, upload-time = "2026-05-06T08:10:23.116Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" }, + { url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548 }, ] [[package]] name = "autoevals" version = "0.3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chevron" }, { name = "jsonschema" }, { name = "polyleven" }, { name = "pyyaml" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/bd/cbb2d2d53e991341fa63355248ed32870e2e60686fe11c066613f0df3a3c/autoevals-0.3.0.tar.gz", hash = "sha256:8def35d33146f80f09d24fa11d446c44d0be9471cce2836de437d82e88c6538a", size = 66952, upload-time = "2026-06-09T17:29:51.944Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/bd/cbb2d2d53e991341fa63355248ed32870e2e60686fe11c066613f0df3a3c/autoevals-0.3.0.tar.gz", hash = "sha256:8def35d33146f80f09d24fa11d446c44d0be9471cce2836de437d82e88c6538a", size = 66952 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/7d/142140e894c67bd69c9dbde15b2e929b4495647b5907e73e87f28d0b82ed/autoevals-0.3.0-py3-none-any.whl", hash = "sha256:c2292ab059d4617ae706658c5df51969eb8c6435a65fc930c61abb918ac2eb2d", size = 73778, upload-time = "2026-06-09T17:29:50.278Z" }, + { url = "https://files.pythonhosted.org/packages/99/7d/142140e894c67bd69c9dbde15b2e929b4495647b5907e73e87f28d0b82ed/autoevals-0.3.0-py3-none-any.whl", hash = "sha256:c2292ab059d4617ae706658c5df51969eb8c6435a65fc930c61abb918ac2eb2d", size = 73778 }, ] [[package]] name = "babel" version = "2.18.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 }, ] [[package]] name = "backoff" version = "2.2.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, ] [[package]] name = "backrefs" version = "7.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491, upload-time = "2026-04-28T16:28:00.928Z" }, + { url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824 }, + { url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626 }, + { url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537 }, + { url = "https://files.pythonhosted.org/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491 }, ] [[package]] name = "bashlex" version = "0.18" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/60/aae0bb54f9af5e0128ba90eb83d8d0d506ee8f0475c4fdda3deeda20b1d2/bashlex-0.18.tar.gz", hash = "sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee", size = 68742, upload-time = "2023-01-18T15:21:26.402Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/60/aae0bb54f9af5e0128ba90eb83d8d0d506ee8f0475c4fdda3deeda20b1d2/bashlex-0.18.tar.gz", hash = "sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee", size = 68742 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/be/6985abb1011fda8a523cfe21ed9629e397d6e06fb5bae99750402b25c95b/bashlex-0.18-py2.py3-none-any.whl", hash = "sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa", size = 69539, upload-time = "2023-01-18T15:21:24.167Z" }, + { url = "https://files.pythonhosted.org/packages/f4/be/6985abb1011fda8a523cfe21ed9629e397d6e06fb5bae99750402b25c95b/bashlex-0.18-py2.py3-none-any.whl", hash = "sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa", size = 69539 }, ] [[package]] name = "beartype" version = "0.22.9" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658 }, ] [[package]] name = "beautifulsoup4" version = "4.15.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571, upload-time = "2026-06-07T16:44:20.453Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" }, + { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924 }, ] [[package]] name = "black" version = "26.5.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "mypy-extensions" }, @@ -771,20 +524,20 @@ dependencies = [ { name = "platformdirs" }, { name = "pytokens" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020, upload-time = "2026-05-18T17:05:28.132Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335, upload-time = "2026-05-18T17:05:31.266Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614, upload-time = "2026-05-18T17:05:32.718Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925, upload-time = "2026-05-18T17:05:34.259Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883, upload-time = "2026-05-18T17:05:36.019Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020 }, + { url = "https://files.pythonhosted.org/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335 }, + { url = "https://files.pythonhosted.org/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614 }, + { url = "https://files.pythonhosted.org/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925 }, + { url = "https://files.pythonhosted.org/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883 }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693 }, ] [[package]] name = "braintrust" version = "0.30.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chevron" }, { name = "exceptiongroup" }, @@ -798,168 +551,168 @@ dependencies = [ { name = "typing-extensions" }, { name = "wrapt" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/26/775bb8a476d7921dfb86fa3e2740e2cd48c1b22602a09369fef413868e9a/braintrust-0.30.1.tar.gz", hash = "sha256:c8f43c2db18ad21a816307011964b0fd0b6f8e59c8a16092fd65bb50480d97b6", size = 694843, upload-time = "2026-07-21T18:46:49.045Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/26/775bb8a476d7921dfb86fa3e2740e2cd48c1b22602a09369fef413868e9a/braintrust-0.30.1.tar.gz", hash = "sha256:c8f43c2db18ad21a816307011964b0fd0b6f8e59c8a16092fd65bb50480d97b6", size = 694843 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/ba/850abbf73a38eacda46ced4267736f9ac5e826927b9908c67693f6b514ce/braintrust-0.30.1-py3-none-any.whl", hash = "sha256:7f5e6491f95bc047ecd121ad9dfb75d9f4ee56a5c20d851cf6d9be297fa0acd2", size = 808827, upload-time = "2026-07-21T18:46:47.21Z" }, + { url = "https://files.pythonhosted.org/packages/69/ba/850abbf73a38eacda46ced4267736f9ac5e826927b9908c67693f6b514ce/braintrust-0.30.1-py3-none-any.whl", hash = "sha256:7f5e6491f95bc047ecd121ad9dfb75d9f4ee56a5c20d851cf6d9be297fa0acd2", size = 808827 }, ] [[package]] name = "brave-search-python-client" version = "0.4.27" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic-settings" }, { name = "tenacity" }, { name = "typer" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/56/bbd47494ebf93742a4ccb40d758a93d0cc1bc0a9513f9106ac9b6f0fa7a2/brave_search_python_client-0.4.27.tar.gz", hash = "sha256:3b8803dd8d4bac8110de2c5ba3014610e6bb36da7ea19ad9ce62b5f1deb38d5a", size = 124413, upload-time = "2025-04-27T17:04:17.641Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/56/bbd47494ebf93742a4ccb40d758a93d0cc1bc0a9513f9106ac9b6f0fa7a2/brave_search_python_client-0.4.27.tar.gz", hash = "sha256:3b8803dd8d4bac8110de2c5ba3014610e6bb36da7ea19ad9ce62b5f1deb38d5a", size = 124413 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/d3/ae16180d52456d8fedcb288e4e4927dd806ce0a43d8ec9b9388ea6c6b072/brave_search_python_client-0.4.27-py3-none-any.whl", hash = "sha256:5b7b7a93f46a825517f81b42dc6c0c6ddcabee4a0fe44ce92b9ceae6a37699ec", size = 128788, upload-time = "2025-04-27T17:04:15.772Z" }, + { url = "https://files.pythonhosted.org/packages/98/d3/ae16180d52456d8fedcb288e4e4927dd806ce0a43d8ec9b9388ea6c6b072/brave_search_python_client-0.4.27-py3-none-any.whl", hash = "sha256:5b7b7a93f46a825517f81b42dc6c0c6ddcabee4a0fe44ce92b9ceae6a37699ec", size = 128788 }, ] [[package]] name = "brotli" version = "1.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/d4/4ad5432ac98c73096159d9ce7ffeb82d151c2ac84adcc6168e476bb54674/brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab", size = 861523, upload-time = "2025-11-05T18:38:34.67Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/9f/9cc5bd03ee68a85dc4bc89114f7067c056a3c14b3d95f171918c088bf88d/brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c", size = 444289, upload-time = "2025-11-05T18:38:35.6Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/b6/fe84227c56a865d16a6614e2c4722864b380cb14b13f3e6bef441e73a85a/brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f", size = 1528076, upload-time = "2025-11-05T18:38:36.639Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/de/de4ae0aaca06c790371cf6e7ee93a024f6b4bb0568727da8c3de112e726c/brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6", size = 1626880, upload-time = "2025-11-05T18:38:37.623Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/16/a1b22cbea436642e071adcaf8d4b350a2ad02f5e0ad0da879a1be16188a0/brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c", size = 1419737, upload-time = "2025-11-05T18:38:38.729Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/63/c968a97cbb3bdbf7f974ef5a6ab467a2879b82afbc5ffb65b8acbb744f95/brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48", size = 1484440, upload-time = "2025-11-05T18:38:39.916Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/9d/102c67ea5c9fc171f423e8399e585dabea29b5bc79b05572891e70013cdd/brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18", size = 1593313, upload-time = "2025-11-05T18:38:41.24Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/4a/9526d14fa6b87bc827ba1755a8440e214ff90de03095cacd78a64abe2b7d/brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5", size = 1487945, upload-time = "2025-11-05T18:38:42.277Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/e8/3fe1ffed70cbef83c5236166acaed7bb9c766509b157854c80e2f766b38c/brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a", size = 334368, upload-time = "2025-11-05T18:38:43.345Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/91/e739587be970a113b37b821eae8097aac5a48e5f0eca438c22e4c7dd8648/brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8", size = 369116, upload-time = "2025-11-05T18:38:44.609Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d4/4ad5432ac98c73096159d9ce7ffeb82d151c2ac84adcc6168e476bb54674/brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab", size = 861523 }, + { url = "https://files.pythonhosted.org/packages/91/9f/9cc5bd03ee68a85dc4bc89114f7067c056a3c14b3d95f171918c088bf88d/brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c", size = 444289 }, + { url = "https://files.pythonhosted.org/packages/2e/b6/fe84227c56a865d16a6614e2c4722864b380cb14b13f3e6bef441e73a85a/brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f", size = 1528076 }, + { url = "https://files.pythonhosted.org/packages/55/de/de4ae0aaca06c790371cf6e7ee93a024f6b4bb0568727da8c3de112e726c/brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6", size = 1626880 }, + { url = "https://files.pythonhosted.org/packages/5f/16/a1b22cbea436642e071adcaf8d4b350a2ad02f5e0ad0da879a1be16188a0/brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c", size = 1419737 }, + { url = "https://files.pythonhosted.org/packages/46/63/c968a97cbb3bdbf7f974ef5a6ab467a2879b82afbc5ffb65b8acbb744f95/brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48", size = 1484440 }, + { url = "https://files.pythonhosted.org/packages/06/9d/102c67ea5c9fc171f423e8399e585dabea29b5bc79b05572891e70013cdd/brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18", size = 1593313 }, + { url = "https://files.pythonhosted.org/packages/9e/4a/9526d14fa6b87bc827ba1755a8440e214ff90de03095cacd78a64abe2b7d/brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5", size = 1487945 }, + { url = "https://files.pythonhosted.org/packages/5b/e8/3fe1ffed70cbef83c5236166acaed7bb9c766509b157854c80e2f766b38c/brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a", size = 334368 }, + { url = "https://files.pythonhosted.org/packages/ff/91/e739587be970a113b37b821eae8097aac5a48e5f0eca438c22e4c7dd8648/brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8", size = 369116 }, ] [[package]] name = "build" version = "1.5.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "os_name == 'nt'" }, { name = "packaging" }, { name = "pyproject-hooks" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/e0/df5e171f685f82f37b12e1f208064e24244911079d7b767447d1af7e0d70/build-1.5.0.tar.gz", hash = "sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647", size = 89796, upload-time = "2026-04-30T03:18:25.17Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/e0/df5e171f685f82f37b12e1f208064e24244911079d7b767447d1af7e0d70/build-1.5.0.tar.gz", hash = "sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647", size = 89796 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl", hash = "sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f", size = 26018, upload-time = "2026-04-30T03:18:23.644Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl", hash = "sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f", size = 26018 }, ] [[package]] name = "burner-redis" version = "0.1.7" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/89/54706febafc135095b2a9d797cfbd4eed2ab1ad7819808b99b587020471b/burner_redis-0.1.7.tar.gz", hash = "sha256:7474ff092669fd11ef765411572cdafcc3d89b8054aef4ca0617be6d6be4c680", size = 638644, upload-time = "2026-05-08T15:01:42.961Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/89/54706febafc135095b2a9d797cfbd4eed2ab1ad7819808b99b587020471b/burner_redis-0.1.7.tar.gz", hash = "sha256:7474ff092669fd11ef765411572cdafcc3d89b8054aef4ca0617be6d6be4c680", size = 638644 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/5d/198bd1d22e504b3034353430703afbdb3efe6e25cb90bf52d896e1d266a7/burner_redis-0.1.7-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f80c866996e0455d584eb3c0f3b067e411c632fb0519eab454e0968edf01e62c", size = 1288888, upload-time = "2026-05-08T15:01:26.103Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/4e/ce5c91b884ac37fcd380756402536f8810964014097950900517ce8bd30c/burner_redis-0.1.7-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a3d9569a376b690fb5876d454e4904443332dc3ad5c0057e149fc2ad220bf599", size = 1234282, upload-time = "2026-05-08T15:01:28.286Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/c0/31c25cc88143eac2dddcc394151a0db627923d44c94376a83768552c9f13/burner_redis-0.1.7-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20eba1917e3bca9eea5957d5700ff8defcb5a209e57a7841d005549aa0151f44", size = 1337341, upload-time = "2026-05-08T15:01:30.397Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/32/95cfa1833316ca2b6b2e58150a4900bc1ad256043cdd36198f1887618ccc/burner_redis-0.1.7-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39111467059b8a28f15ea061d2414ec25c3e57c65759983f90f4d358e7d6a72d", size = 1366800, upload-time = "2026-05-08T15:01:32.891Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/ad/93c3916f053f89b7b5760da5bf855cd78b7885d480f9cfcc64f3732c1dc2/burner_redis-0.1.7-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9b5adfe99aeb8407f468078f3769b2a63e9168fea12f7709df5d2a3b152706e4", size = 1538160, upload-time = "2026-05-08T15:01:34.667Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/b9/19bae42cb124932d71168bc8e5bcb1da33aa62b908e5e632b3d298d7cb15/burner_redis-0.1.7-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:591a9d20685f9d6d22bf0c863b50b12dfcf328b06111b3f62c33cd3185d48ce0", size = 1591491, upload-time = "2026-05-08T15:01:36.708Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/30/207f47f406619a5b564355d2946c3171f84231a28b800709b5645b06a5ae/burner_redis-0.1.7-cp310-abi3-win_amd64.whl", hash = "sha256:f6cf4ac666766b32fd63940aad0c120847905fd3102c17e5b6b305f91a21d079", size = 1117564, upload-time = "2026-05-08T15:01:39.221Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/6f/e9beaf46c5e9fd10dfcdb889ebf7d3aa85142c650c0ab17ab284194f58e1/burner_redis-0.1.7-cp310-abi3-win_arm64.whl", hash = "sha256:458f88feeddfb40a586cc3fcbd8e9384bbdfd2a4512a695af4900e06052570d4", size = 1040407, upload-time = "2026-05-08T15:01:41.235Z" }, + { url = "https://files.pythonhosted.org/packages/6c/5d/198bd1d22e504b3034353430703afbdb3efe6e25cb90bf52d896e1d266a7/burner_redis-0.1.7-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f80c866996e0455d584eb3c0f3b067e411c632fb0519eab454e0968edf01e62c", size = 1288888 }, + { url = "https://files.pythonhosted.org/packages/2f/4e/ce5c91b884ac37fcd380756402536f8810964014097950900517ce8bd30c/burner_redis-0.1.7-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a3d9569a376b690fb5876d454e4904443332dc3ad5c0057e149fc2ad220bf599", size = 1234282 }, + { url = "https://files.pythonhosted.org/packages/6f/c0/31c25cc88143eac2dddcc394151a0db627923d44c94376a83768552c9f13/burner_redis-0.1.7-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20eba1917e3bca9eea5957d5700ff8defcb5a209e57a7841d005549aa0151f44", size = 1337341 }, + { url = "https://files.pythonhosted.org/packages/e1/32/95cfa1833316ca2b6b2e58150a4900bc1ad256043cdd36198f1887618ccc/burner_redis-0.1.7-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39111467059b8a28f15ea061d2414ec25c3e57c65759983f90f4d358e7d6a72d", size = 1366800 }, + { url = "https://files.pythonhosted.org/packages/34/ad/93c3916f053f89b7b5760da5bf855cd78b7885d480f9cfcc64f3732c1dc2/burner_redis-0.1.7-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9b5adfe99aeb8407f468078f3769b2a63e9168fea12f7709df5d2a3b152706e4", size = 1538160 }, + { url = "https://files.pythonhosted.org/packages/5c/b9/19bae42cb124932d71168bc8e5bcb1da33aa62b908e5e632b3d298d7cb15/burner_redis-0.1.7-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:591a9d20685f9d6d22bf0c863b50b12dfcf328b06111b3f62c33cd3185d48ce0", size = 1591491 }, + { url = "https://files.pythonhosted.org/packages/f5/30/207f47f406619a5b564355d2946c3171f84231a28b800709b5645b06a5ae/burner_redis-0.1.7-cp310-abi3-win_amd64.whl", hash = "sha256:f6cf4ac666766b32fd63940aad0c120847905fd3102c17e5b6b305f91a21d079", size = 1117564 }, + { url = "https://files.pythonhosted.org/packages/76/6f/e9beaf46c5e9fd10dfcdb889ebf7d3aa85142c650c0ab17ab284194f58e1/burner_redis-0.1.7-cp310-abi3-win_arm64.whl", hash = "sha256:458f88feeddfb40a586cc3fcbd8e9384bbdfd2a4512a695af4900e06052570d4", size = 1040407 }, ] [[package]] name = "cachetools" version = "5.5.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 }, ] [[package]] name = "caio" version = "0.9.25" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/88/b8527e1b00c1811db339a1df8bd1ae49d146fcea9d6a5c40e3a80aaeb38d/caio-0.9.25.tar.gz", hash = "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10", size = 26781, upload-time = "2025-12-26T15:21:36.501Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/88/b8527e1b00c1811db339a1df8bd1ae49d146fcea9d6a5c40e3a80aaeb38d/caio-0.9.25.tar.gz", hash = "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10", size = 26781 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/57/5e6ff127e6f62c9f15d989560435c642144aa4210882f9494204bc892305/caio-0.9.25-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451", size = 36979, upload-time = "2025-12-26T15:21:35.484Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/9f/f21af50e72117eb528c422d4276cbac11fb941b1b812b182e0a9c70d19c5/caio-0.9.25-cp313-cp313-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6", size = 81900, upload-time = "2025-12-26T15:22:21.919Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/12/c39ae2a4037cb10ad5eb3578eb4d5f8c1a2575c62bba675f3406b7ef0824/caio-0.9.25-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:1a177d4777141b96f175fe2c37a3d96dec7911ed9ad5f02bac38aaa1c936611f", size = 81523, upload-time = "2026-03-04T22:08:25.187Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/59/f8f2e950eb4f1a5a3883e198dca514b9d475415cb6cd7b78b9213a0dd45a/caio-0.9.25-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:9ed3cfb28c0e99fec5e208c934e5c157d0866aa9c32aa4dc5e9b6034af6286b7", size = 80243, upload-time = "2026-03-04T22:08:26.449Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087, upload-time = "2025-12-26T15:22:00.221Z" }, + { url = "https://files.pythonhosted.org/packages/31/57/5e6ff127e6f62c9f15d989560435c642144aa4210882f9494204bc892305/caio-0.9.25-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451", size = 36979 }, + { url = "https://files.pythonhosted.org/packages/a3/9f/f21af50e72117eb528c422d4276cbac11fb941b1b812b182e0a9c70d19c5/caio-0.9.25-cp313-cp313-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6", size = 81900 }, + { url = "https://files.pythonhosted.org/packages/9c/12/c39ae2a4037cb10ad5eb3578eb4d5f8c1a2575c62bba675f3406b7ef0824/caio-0.9.25-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:1a177d4777141b96f175fe2c37a3d96dec7911ed9ad5f02bac38aaa1c936611f", size = 81523 }, + { url = "https://files.pythonhosted.org/packages/22/59/f8f2e950eb4f1a5a3883e198dca514b9d475415cb6cd7b78b9213a0dd45a/caio-0.9.25-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:9ed3cfb28c0e99fec5e208c934e5c157d0866aa9c32aa4dc5e9b6034af6286b7", size = 80243 }, + { url = "https://files.pythonhosted.org/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087 }, ] [[package]] name = "centrifuge-python" version = "0.4.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, { name = "websockets" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/5e/d04f0af3d9a74e93c4916d2656156ecc9930f4f5a09e1d2bb061ee817853/centrifuge_python-0.4.4.tar.gz", hash = "sha256:e18e33af9e984825b01ec85c96436e5c701703cc36c8c8f6d8bf00052a720e83", size = 44121, upload-time = "2026-04-18T15:19:39.657Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/5e/d04f0af3d9a74e93c4916d2656156ecc9930f4f5a09e1d2bb061ee817853/centrifuge_python-0.4.4.tar.gz", hash = "sha256:e18e33af9e984825b01ec85c96436e5c701703cc36c8c8f6d8bf00052a720e83", size = 44121 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/f5/a5bfa538d31aa006dd501b16523df37e5faa56765cf0bb755a87cc741398/centrifuge_python-0.4.4-py3-none-any.whl", hash = "sha256:6d30ce617354401e6e7496ddb47d86e5538ee83dbc4a3b314dfec2e6fb68660e", size = 26442, upload-time = "2026-04-18T15:19:38.644Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f5/a5bfa538d31aa006dd501b16523df37e5faa56765cf0bb755a87cc741398/centrifuge_python-0.4.4-py3-none-any.whl", hash = "sha256:6d30ce617354401e6e7496ddb47d86e5538ee83dbc4a3b314dfec2e6fb68660e", size = 26442 }, ] [[package]] name = "certifi" version = "2026.7.22" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983 }, ] [[package]] name = "cffi" version = "2.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331 }, + { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966 }, + { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795 }, + { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746 }, + { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747 }, + { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392 }, + { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285 }, + { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801 }, + { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808 }, + { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241 }, + { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588 }, + { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248 }, + { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717 }, + { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114 }, ] [[package]] name = "charset-normalizer" version = "3.4.9" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688 }, + { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982 }, + { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460 }, + { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003 }, + { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149 }, + { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901 }, + { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176 }, + { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356 }, + { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614 }, + { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991 }, + { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622 }, + { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947 }, + { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594 }, + { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538 }, ] [[package]] name = "check-jsonschema" version = "0.37.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "jsonschema" }, @@ -967,76 +720,93 @@ dependencies = [ { name = "requests" }, { name = "ruamel-yaml" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/dd/27b7f575586505c3d71b66b1fd60076feeba1ba7ffd1303fbf5a70cedad6/check_jsonschema-0.37.4.tar.gz", hash = "sha256:f933521f14cbeaaf113270751185b01379f453ac9ce9fb01d6190ce1352ccaa6", size = 428594, upload-time = "2026-06-29T21:10:23.726Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/dd/27b7f575586505c3d71b66b1fd60076feeba1ba7ffd1303fbf5a70cedad6/check_jsonschema-0.37.4.tar.gz", hash = "sha256:f933521f14cbeaaf113270751185b01379f453ac9ce9fb01d6190ce1352ccaa6", size = 428594 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/82/6a929238ec352eadd4f4ce6e1be98c575fa8cbebe45b7c519f8016ad2860/check_jsonschema-0.37.4-py3-none-any.whl", hash = "sha256:e4d1647992674d6b1ed2ca46eee6697760d17a5cc0c167ba09c5b503b883d041", size = 412508, upload-time = "2026-06-29T21:10:22.041Z" }, + { url = "https://files.pythonhosted.org/packages/92/82/6a929238ec352eadd4f4ce6e1be98c575fa8cbebe45b7c519f8016ad2860/check_jsonschema-0.37.4-py3-none-any.whl", hash = "sha256:e4d1647992674d6b1ed2ca46eee6697760d17a5cc0c167ba09c5b503b883d041", size = 412508 }, ] [[package]] name = "chevron" version = "0.14.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/1f/ca74b65b19798895d63a6e92874162f44233467c9e7c1ed8afd19016ebe9/chevron-0.14.0.tar.gz", hash = "sha256:87613aafdf6d77b6a90ff073165a61ae5086e21ad49057aa0e53681601800ebf", size = 11440, upload-time = "2021-01-02T22:47:59.233Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/1f/ca74b65b19798895d63a6e92874162f44233467c9e7c1ed8afd19016ebe9/chevron-0.14.0.tar.gz", hash = "sha256:87613aafdf6d77b6a90ff073165a61ae5086e21ad49057aa0e53681601800ebf", size = 11440 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/93/342cc62a70ab727e093ed98e02a725d85b746345f05d2b5e5034649f4ec8/chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443", size = 11595, upload-time = "2021-01-02T22:47:57.847Z" }, + { url = "https://files.pythonhosted.org/packages/52/93/342cc62a70ab727e093ed98e02a725d85b746345f05d2b5e5034649f4ec8/chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443", size = 11595 }, ] [[package]] -name = "click" -version = "8.4.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +name = "clawd-code-sdk" +version = "1.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anthropic" }, + { name = "anyenv" }, + { name = "anyio" }, + { name = "logfire" }, + { name = "mcp" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/b7/158079d9c76f2247eb934c4792a6fefbd10069326d4b435d69613a92044f/clawd_code_sdk-1.1.6.tar.gz", hash = "sha256:e85b834bba7efde466d8f82c873004fbd92debb6657c1ed49360c196d6dd4fea", size = 198815 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/23/2ad1586d809669f7161365b8aa639b4a7fbf3d4635f1e9fe5f95352b2cc5/clawd_code_sdk-1.1.6-py3-none-any.whl", hash = "sha256:b5dec10cbe8bd09475b05cbce985874e2f4ab5f3b614f3a46cf0c109cfc09721", size = 159138 }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243 }, ] [[package]] name = "clinspector" version = "1.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv", extra = ["httpx"] }, { name = "schemez" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/da/5523bbfb94a8f961b7ee998fcbdf3efdeea14225c848f35e520383d35369/clinspector-1.0.1.tar.gz", hash = "sha256:2854ea75c8b70c4a1c5273dcb9d44497e8a29c0d4d9dcb7299e551ac20e0c99a", size = 23099, upload-time = "2025-11-09T11:22:31.128Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/da/5523bbfb94a8f961b7ee998fcbdf3efdeea14225c848f35e520383d35369/clinspector-1.0.1.tar.gz", hash = "sha256:2854ea75c8b70c4a1c5273dcb9d44497e8a29c0d4d9dcb7299e551ac20e0c99a", size = 23099 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/06/c8508a6a7b8dfa1600c57e2227194ffc0877958b1bf97bc0e2985b360c7c/clinspector-1.0.1-py3-none-any.whl", hash = "sha256:67616bbbb6e5c7a50a5e7760c0a6022ca31f612910aab21770296513e4a98492", size = 33078, upload-time = "2025-11-09T11:22:29.707Z" }, + { url = "https://files.pythonhosted.org/packages/0e/06/c8508a6a7b8dfa1600c57e2227194ffc0877958b1bf97bc0e2985b360c7c/clinspector-1.0.1-py3-none-any.whl", hash = "sha256:67616bbbb6e5c7a50a5e7760c0a6022ca31f612910aab21770296513e4a98492", size = 33078 }, ] [[package]] name = "cloudpickle" version = "3.1.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, ] [[package]] name = "coloraide" version = "8.10" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/b2/ff53a8c7921197177a4011cc1079b2229678280afd7a8f652c6b46944af8/coloraide-8.10.tar.gz", hash = "sha256:0120503a297181d7236c378297b6ad98b0da4e11bd99fd2500778b8485557a9e", size = 22407815, upload-time = "2026-06-26T01:39:07.708Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/b2/ff53a8c7921197177a4011cc1079b2229678280afd7a8f652c6b46944af8/coloraide-8.10.tar.gz", hash = "sha256:0120503a297181d7236c378297b6ad98b0da4e11bd99fd2500778b8485557a9e", size = 22407815 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/a7/94b95381ebcf3954cd726db7bc4e53cd414a830849ad47db870b2469627f/coloraide-8.10-py3-none-any.whl", hash = "sha256:8a55379769807ce50a1a5b3e973620708fd40b2552a016b89b7fa70e60dd58cc", size = 361350, upload-time = "2026-06-26T01:39:05.522Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a7/94b95381ebcf3954cd726db7bc4e53cd414a830849ad47db870b2469627f/coloraide-8.10-py3-none-any.whl", hash = "sha256:8a55379769807ce50a1a5b3e973620708fd40b2552a016b89b7fa70e60dd58cc", size = 361350 }, ] [[package]] name = "colorama" version = "0.4.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] name = "composio" version = "0.18.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "composio-client" }, { name = "json-schema-to-pydantic" }, @@ -1045,15 +815,15 @@ dependencies = [ { name = "pysher" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/80/0071e855e9b8e06d2a5f4d87b9f5a65e2a7b3573b72e707256c4b6ee4fc3/composio-0.18.0.tar.gz", hash = "sha256:d8277412eabbb29e357025f1611d59dbb8f410ded5a1bed9a3a8fad09c630366", size = 239594, upload-time = "2026-07-15T20:40:09.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/80/0071e855e9b8e06d2a5f4d87b9f5a65e2a7b3573b72e707256c4b6ee4fc3/composio-0.18.0.tar.gz", hash = "sha256:d8277412eabbb29e357025f1611d59dbb8f410ded5a1bed9a3a8fad09c630366", size = 239594 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/8f/0f08f2698cfcdc03d53d8e4810256fbe16e4731c41b70b1fdbb9897f13f8/composio-0.18.0-py3-none-any.whl", hash = "sha256:8cf09b5f9494ae8b3f14103bab2a820a744e9007b0ed2cf9c36f575f9b63a522", size = 155940, upload-time = "2026-07-15T20:39:56.498Z" }, + { url = "https://files.pythonhosted.org/packages/42/8f/0f08f2698cfcdc03d53d8e4810256fbe16e4731c41b70b1fdbb9897f13f8/composio-0.18.0-py3-none-any.whl", hash = "sha256:8cf09b5f9494ae8b3f14103bab2a820a744e9007b0ed2cf9c36f575f9b63a522", size = 155940 }, ] [[package]] name = "composio-client" version = "1.42.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -1062,137 +832,137 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/d3/c47df669397e672172ff31090d186ff36e2158d33cc6c07cf5ec7dd05c39/composio_client-1.42.0.tar.gz", hash = "sha256:74f4635befaa4761e0ca83ed68ff63b0a62312ebea08cf0a2088a47cfe9d5154", size = 246045, upload-time = "2026-06-30T18:14:22.206Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/d3/c47df669397e672172ff31090d186ff36e2158d33cc6c07cf5ec7dd05c39/composio_client-1.42.0.tar.gz", hash = "sha256:74f4635befaa4761e0ca83ed68ff63b0a62312ebea08cf0a2088a47cfe9d5154", size = 246045 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/b5/f43e74632a91ae6516b713f7d2918391c98276967aa332bd8360457ec940/composio_client-1.42.0-py3-none-any.whl", hash = "sha256:4edfe2b3753fbf63fe2739a4f6705ffc8d4313f7fc98192efb12bac7990600a5", size = 277044, upload-time = "2026-06-30T18:14:20.857Z" }, + { url = "https://files.pythonhosted.org/packages/fe/b5/f43e74632a91ae6516b713f7d2918391c98276967aa332bd8360457ec940/composio_client-1.42.0-py3-none-any.whl", hash = "sha256:4edfe2b3753fbf63fe2739a4f6705ffc8d4313f7fc98192efb12bac7990600a5", size = 277044 }, ] [[package]] name = "copykitten" version = "2.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/72/57a5dee794b29c940f6bc429ff731c3d0b9f7af0aad94bd88bafbc3db07f/copykitten-2.0.0.tar.gz", hash = "sha256:bda13d614ffb38147d4fce5217d4f2397967514f5ea09fb8d5c5cf5d16028e58", size = 19712, upload-time = "2025-09-21T02:48:24.848Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/72/57a5dee794b29c940f6bc429ff731c3d0b9f7af0aad94bd88bafbc3db07f/copykitten-2.0.0.tar.gz", hash = "sha256:bda13d614ffb38147d4fce5217d4f2397967514f5ea09fb8d5c5cf5d16028e58", size = 19712 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/24/2ff5084b1fe4825d47246de8f81fa738245ca6d6645b31dfc686bcbed51e/copykitten-2.0.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:191c202d19962fa5dbed5296d83bfa844b36609e30f0fb19cf1706b136f170e8", size = 381118, upload-time = "2025-09-21T02:48:17.65Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/aa/51d0d24435846eb8c41a5f68874a2a39d367e22c58eb3a955455b1e0c7bf/copykitten-2.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:1fb3118f5b3d9bc70442ae62f5d0b5fdd22243f2b4f2811e0680cbb4dfca112a", size = 359839, upload-time = "2025-09-21T02:48:19.422Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/3c/ba4ce92ffe21c695d1492fe495032eb2356a5ade4a497d238f7efd277cea/copykitten-2.0.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6be53c8c45bb85bda1679ea2d890e24303096d2a9d9a1022e159b7cfd350b2c0", size = 453897, upload-time = "2025-09-21T02:48:21.055Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/59/5800f845f87f5841f467082698ccbe40e7eb000b1e069bdf24134c91c14c/copykitten-2.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b799e46da487e3380248065e5684d39e9f8f0f53b1124edba5e4fd2dc5a273", size = 502975, upload-time = "2025-09-21T02:48:22.428Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/45/22e1d8ffecadcfb0f57ba5189b6c2a7850621ad2045cbdf23a4d7f1f1849/copykitten-2.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:00d09e79fe8031dbdfaefd843d60e48f2b63e6ea9a823d25e07a17469f25017e", size = 267324, upload-time = "2025-09-21T02:48:23.512Z" }, + { url = "https://files.pythonhosted.org/packages/3f/24/2ff5084b1fe4825d47246de8f81fa738245ca6d6645b31dfc686bcbed51e/copykitten-2.0.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:191c202d19962fa5dbed5296d83bfa844b36609e30f0fb19cf1706b136f170e8", size = 381118 }, + { url = "https://files.pythonhosted.org/packages/b5/aa/51d0d24435846eb8c41a5f68874a2a39d367e22c58eb3a955455b1e0c7bf/copykitten-2.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:1fb3118f5b3d9bc70442ae62f5d0b5fdd22243f2b4f2811e0680cbb4dfca112a", size = 359839 }, + { url = "https://files.pythonhosted.org/packages/3e/3c/ba4ce92ffe21c695d1492fe495032eb2356a5ade4a497d238f7efd277cea/copykitten-2.0.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6be53c8c45bb85bda1679ea2d890e24303096d2a9d9a1022e159b7cfd350b2c0", size = 453897 }, + { url = "https://files.pythonhosted.org/packages/98/59/5800f845f87f5841f467082698ccbe40e7eb000b1e069bdf24134c91c14c/copykitten-2.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b799e46da487e3380248065e5684d39e9f8f0f53b1124edba5e4fd2dc5a273", size = 502975 }, + { url = "https://files.pythonhosted.org/packages/60/45/22e1d8ffecadcfb0f57ba5189b6c2a7850621ad2045cbdf23a4d7f1f1849/copykitten-2.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:00d09e79fe8031dbdfaefd843d60e48f2b63e6ea9a823d25e07a17469f25017e", size = 267324 }, ] [[package]] name = "coverage" version = "7.15.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/d0/55fe630f4cf94e3fcba868240fad8c8cdd1f764e2a932f8926347e6ec4cd/coverage-7.15.2.tar.gz", hash = "sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d", size = 927741, upload-time = "2026-07-15T18:56:19.558Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/d5/f8c838e6b7282976f7c918884b792df7a0c42c5bba5d99c60ad2d221d56d/coverage-7.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8", size = 221606, upload-time = "2026-07-15T18:54:45.448Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/37/97c926376364f66298cc44893b89cdf17b8bc406376497c4061ae4b8a8ff/coverage-7.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1", size = 221982, upload-time = "2026-07-15T18:54:47.341Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/30/a36050a6e83c2135ee0776f452ca3948224befc6d7f26acecc082d0c106a/coverage-7.15.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578", size = 252972, upload-time = "2026-07-15T18:54:49.2Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/d3/06b5f1daf95f0f15ab05bd75f26ba5f3c8b33d0bb72f3aaa3cf41d1bad3a/coverage-7.15.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1", size = 255569, upload-time = "2026-07-15T18:54:51.098Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/1c/9afb3f8de2b8d36960391c48559a2e3ff96594b58099f115921549ea8d0d/coverage-7.15.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6", size = 256806, upload-time = "2026-07-15T18:54:53.145Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/d8/b989f96061a5e32d82fddd1b1b9ff48a7c8f8ae7606f0e80fd9de54b1e33/coverage-7.15.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7", size = 258936, upload-time = "2026-07-15T18:54:55.015Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/fa/f99771f5110457c7b511c1935ca49ddf288218eaa84322e028b9334146ae/coverage-7.15.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d", size = 253178, upload-time = "2026-07-15T18:54:57.527Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/96/c098a6044d119c751ceede7be91035fa8310170ec24a6523aff72f0a5793/coverage-7.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026", size = 254934, upload-time = "2026-07-15T18:54:59.41Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/a2/1457b3a7a50c8d77500103b97a046db863e2f59a1cf6d2f814595f349885/coverage-7.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa", size = 252898, upload-time = "2026-07-15T18:55:01.338Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/0e/76958874c471ecfcdde0d2b2747bb2c61bdbf34a40636f4ce9db9923e643/coverage-7.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d", size = 257056, upload-time = "2026-07-15T18:55:03.243Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/7c/3d7c4e3bf58baa40327dc7edc2272b17cf02299366d52763db1b0ca1556a/coverage-7.15.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b", size = 252718, upload-time = "2026-07-15T18:55:05.029Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/b8/1cecffed9ce14fb25be9ba42d37b6bb61485c9a3ddd43cd3dde36b6087d8/coverage-7.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188", size = 254490, upload-time = "2026-07-15T18:55:06.889Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/2c/42984561bc7f4c045dca67516a0c50ee5ef8d84352dbeb5559dc86c4823e/coverage-7.15.2-cp313-cp313-win32.whl", hash = "sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050", size = 223647, upload-time = "2026-07-15T18:55:08.941Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/9f/39c7c9245efc583beddf89a87683574e663ed93637f3afb6cd7b88405676/coverage-7.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c", size = 224190, upload-time = "2026-07-15T18:55:10.789Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/de/3a2883cf8a213659280ef4b403059e17a9acaeb7fc7fd4105e1226ff2e6d/coverage-7.15.2-cp313-cp313-win_arm64.whl", hash = "sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b", size = 223583, upload-time = "2026-07-15T18:55:12.678Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/82/32e3bd191d498e64f6f911ad55d14006a0861e54869d2d32452326399e65/coverage-7.15.2-py3-none-any.whl", hash = "sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c", size = 213375, upload-time = "2026-07-15T18:56:17.305Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d0/55fe630f4cf94e3fcba868240fad8c8cdd1f764e2a932f8926347e6ec4cd/coverage-7.15.2.tar.gz", hash = "sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d", size = 927741 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/d5/f8c838e6b7282976f7c918884b792df7a0c42c5bba5d99c60ad2d221d56d/coverage-7.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8", size = 221606 }, + { url = "https://files.pythonhosted.org/packages/bf/37/97c926376364f66298cc44893b89cdf17b8bc406376497c4061ae4b8a8ff/coverage-7.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1", size = 221982 }, + { url = "https://files.pythonhosted.org/packages/b7/30/a36050a6e83c2135ee0776f452ca3948224befc6d7f26acecc082d0c106a/coverage-7.15.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578", size = 252972 }, + { url = "https://files.pythonhosted.org/packages/31/d3/06b5f1daf95f0f15ab05bd75f26ba5f3c8b33d0bb72f3aaa3cf41d1bad3a/coverage-7.15.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1", size = 255569 }, + { url = "https://files.pythonhosted.org/packages/81/1c/9afb3f8de2b8d36960391c48559a2e3ff96594b58099f115921549ea8d0d/coverage-7.15.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6", size = 256806 }, + { url = "https://files.pythonhosted.org/packages/64/d8/b989f96061a5e32d82fddd1b1b9ff48a7c8f8ae7606f0e80fd9de54b1e33/coverage-7.15.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7", size = 258936 }, + { url = "https://files.pythonhosted.org/packages/b8/fa/f99771f5110457c7b511c1935ca49ddf288218eaa84322e028b9334146ae/coverage-7.15.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d", size = 253178 }, + { url = "https://files.pythonhosted.org/packages/f6/96/c098a6044d119c751ceede7be91035fa8310170ec24a6523aff72f0a5793/coverage-7.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026", size = 254934 }, + { url = "https://files.pythonhosted.org/packages/b2/a2/1457b3a7a50c8d77500103b97a046db863e2f59a1cf6d2f814595f349885/coverage-7.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa", size = 252898 }, + { url = "https://files.pythonhosted.org/packages/6c/0e/76958874c471ecfcdde0d2b2747bb2c61bdbf34a40636f4ce9db9923e643/coverage-7.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d", size = 257056 }, + { url = "https://files.pythonhosted.org/packages/7c/7c/3d7c4e3bf58baa40327dc7edc2272b17cf02299366d52763db1b0ca1556a/coverage-7.15.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b", size = 252718 }, + { url = "https://files.pythonhosted.org/packages/c8/b8/1cecffed9ce14fb25be9ba42d37b6bb61485c9a3ddd43cd3dde36b6087d8/coverage-7.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188", size = 254490 }, + { url = "https://files.pythonhosted.org/packages/6c/2c/42984561bc7f4c045dca67516a0c50ee5ef8d84352dbeb5559dc86c4823e/coverage-7.15.2-cp313-cp313-win32.whl", hash = "sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050", size = 223647 }, + { url = "https://files.pythonhosted.org/packages/41/9f/39c7c9245efc583beddf89a87683574e663ed93637f3afb6cd7b88405676/coverage-7.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c", size = 224190 }, + { url = "https://files.pythonhosted.org/packages/c7/de/3a2883cf8a213659280ef4b403059e17a9acaeb7fc7fd4105e1226ff2e6d/coverage-7.15.2-cp313-cp313-win_arm64.whl", hash = "sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b", size = 223583 }, + { url = "https://files.pythonhosted.org/packages/ec/82/32e3bd191d498e64f6f911ad55d14006a0861e54869d2d32452326399e65/coverage-7.15.2-py3-none-any.whl", hash = "sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c", size = 213375 }, ] [[package]] name = "croniter" version = "6.2.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/57/2e2a65aee2a70483cb28e2b7e15a072d00a523207593b44400d4717bb100/croniter-6.2.4.tar.gz", hash = "sha256:fc124f751b1b04805c2a04b061898b436b45ab2320b045e1e052ea895de65189", size = 166267, upload-time = "2026-07-10T09:52:59.955Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/57/2e2a65aee2a70483cb28e2b7e15a072d00a523207593b44400d4717bb100/croniter-6.2.4.tar.gz", hash = "sha256:fc124f751b1b04805c2a04b061898b436b45ab2320b045e1e052ea895de65189", size = 166267 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/ba/d678e5bd329646ca51d3c92addbc77804e86d21f4b6b6a027218e6abb010/croniter-6.2.4-py3-none-any.whl", hash = "sha256:8ef3d544107a5c05a150a2d78f8bf5a8eb9c5c4d93405a736b824109574e3f4d", size = 46677, upload-time = "2026-07-10T09:52:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ba/d678e5bd329646ca51d3c92addbc77804e86d21f4b6b6a027218e6abb010/croniter-6.2.4-py3-none-any.whl", hash = "sha256:8ef3d544107a5c05a150a2d78f8bf5a8eb9c5c4d93405a736b824109574e3f4d", size = 46677 }, ] [[package]] name = "cronsim" version = "2.7" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/1a/02f105147f7f2e06ed4f734ff5a6439590bb275a53dd91fc73df6312298a/cronsim-2.7-py3-none-any.whl", hash = "sha256:1e1431fa08c51dc7f72e67e571c7c7a09af26420169b607badd4ca9677ffad1e", size = 14213, upload-time = "2025-10-21T16:38:20.431Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1a/02f105147f7f2e06ed4f734ff5a6439590bb275a53dd91fc73df6312298a/cronsim-2.7-py3-none-any.whl", hash = "sha256:1e1431fa08c51dc7f72e67e571c7c7a09af26420169b607badd4ca9677ffad1e", size = 14213 }, ] [[package]] name = "cryptography" version = "49.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100 }, + { url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978 }, + { url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422 }, + { url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503 }, + { url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779 }, + { url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683 }, + { url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874 }, + { url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283 }, + { url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844 }, + { url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290 }, + { url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612 }, + { url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804 }, + { url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026 }, + { url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947 }, + { url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429 }, + { url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968 }, + { url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758 }, + { url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863 }, + { url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983 }, + { url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173 }, + { url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298 }, + { url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338 }, + { url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650 }, + { url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820 }, + { url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968 }, + { url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547 }, ] [[package]] name = "cyclopts" version = "4.22.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "docstring-parser" }, { name = "rich" }, { name = "rich-rst" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/47/32d992e829f63aedea5b93360db23c8882c9bbbde094bcf0fff899ea8a3b/cyclopts-4.22.1.tar.gz", hash = "sha256:49cd3779da7113a96ac5c23b151aa61ac9ae1b4b1fe813594d207ca843c97892", size = 193551, upload-time = "2026-07-20T17:38:59.38Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/47/32d992e829f63aedea5b93360db23c8882c9bbbde094bcf0fff899ea8a3b/cyclopts-4.22.1.tar.gz", hash = "sha256:49cd3779da7113a96ac5c23b151aa61ac9ae1b4b1fe813594d207ca843c97892", size = 193551 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/89/f2710638cd2f824cb976777e63ef6ed1e83fc56888cf5c9f5a053490b7be/cyclopts-4.22.1-py3-none-any.whl", hash = "sha256:9b614e231075aee9849c0bfd78f7611ab7adf417f16af5b9e42b9ed6e18c17d1", size = 232953, upload-time = "2026-07-20T17:38:58.078Z" }, + { url = "https://files.pythonhosted.org/packages/9a/89/f2710638cd2f824cb976777e63ef6ed1e83fc56888cf5c9f5a053490b7be/cyclopts-4.22.1-py3-none-any.whl", hash = "sha256:9b614e231075aee9849c0bfd78f7611ab7adf417f16af5b9e42b9ed6e18c17d1", size = 232953 }, ] [[package]] name = "dataclasses-json" version = "0.6.7" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "marshmallow" }, { name = "typing-inspect" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227, upload-time = "2024-06-09T16:20:19.103Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, + { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686 }, ] [[package]] name = "datamodel-code-generator" version = "0.70.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, { name = "black", marker = "sys_platform != 'emscripten'" }, @@ -1203,9 +973,9 @@ dependencies = [ { name = "pydantic" }, { name = "pyyaml" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/da/3f34411e9ef4d99fb774a506fabdf2c99f229612545fa213a9fab3d89661/datamodel_code_generator-0.70.0.tar.gz", hash = "sha256:eac611f052ede1dca4b37325d070d631a988a5936c69626a3de54e28472954c6", size = 1635415, upload-time = "2026-07-23T03:45:26.397Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/da/3f34411e9ef4d99fb774a506fabdf2c99f229612545fa213a9fab3d89661/datamodel_code_generator-0.70.0.tar.gz", hash = "sha256:eac611f052ede1dca4b37325d070d631a988a5936c69626a3de54e28472954c6", size = 1635415 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/ae/6a7da5c5b8e0e7667233e3ba0dd400131aa1cb688a1714a15fa989e36f59/datamodel_code_generator-0.70.0-py3-none-any.whl", hash = "sha256:4c3357328cf9998e9b1a6d4ad7ae4296f008563f9298aaa4e325b07418fafa40", size = 440877, upload-time = "2026-07-23T03:45:24.39Z" }, + { url = "https://files.pythonhosted.org/packages/01/ae/6a7da5c5b8e0e7667233e3ba0dd400131aa1cb688a1714a15fa989e36f59/datamodel_code_generator-0.70.0-py3-none-any.whl", hash = "sha256:4c3357328cf9998e9b1a6d4ad7ae4296f008563f9298aaa4e325b07418fafa40", size = 440877 }, ] [package.optional-dependencies] @@ -1216,87 +986,87 @@ ruff = [ [[package]] name = "deepmerge" version = "2.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/78/6e9e20106224083cfb817d2d3c26e80e72258d617b616721a169b87081e0/deepmerge-2.1.0.tar.gz", hash = "sha256:07ca7a7b8935df596c512fa8161877c0487ac61f691c07766e7d71d2b23bdd2f", size = 21449, upload-time = "2026-06-22T05:46:07.669Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/78/6e9e20106224083cfb817d2d3c26e80e72258d617b616721a169b87081e0/deepmerge-2.1.0.tar.gz", hash = "sha256:07ca7a7b8935df596c512fa8161877c0487ac61f691c07766e7d71d2b23bdd2f", size = 21449 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/25/2a75b47cb057b1e164c604fb81ab690a6cdb5e2260ce651194eae90f64a3/deepmerge-2.1.0-py3-none-any.whl", hash = "sha256:8f148339a91d680a75ecb74ade235d9e759a93df373a0b04e9d31c8666cfeb75", size = 14345, upload-time = "2026-06-22T05:46:06.742Z" }, + { url = "https://files.pythonhosted.org/packages/51/25/2a75b47cb057b1e164c604fb81ab690a6cdb5e2260ce651194eae90f64a3/deepmerge-2.1.0-py3-none-any.whl", hash = "sha256:8f148339a91d680a75ecb74ade235d9e759a93df373a0b04e9d31c8666cfeb75", size = 14345 }, ] [[package]] name = "defusedxml" version = "0.7.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" }, + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, ] [[package]] name = "deprecation" version = "2.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, + { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178 }, ] [[package]] name = "devtools" version = "0.12.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asttokens" }, { name = "executing" }, { name = "pygments" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/75/b78198620640d394bc435c17bb49db18419afdd6cfa3ed8bcfe14034ec80/devtools-0.12.2.tar.gz", hash = "sha256:efceab184cb35e3a11fa8e602cc4fadacaa2e859e920fc6f87bf130b69885507" } +sdist = { url = "https://files.pythonhosted.org/packages/84/75/b78198620640d394bc435c17bb49db18419afdd6cfa3ed8bcfe14034ec80/devtools-0.12.2.tar.gz", hash = "sha256:efceab184cb35e3a11fa8e602cc4fadacaa2e859e920fc6f87bf130b69885507", size = 75005 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/ae/afb1487556e2dc827a17097aac8158a25b433a345386f0e249f6d2694ccb/devtools-0.12.2-py3-none-any.whl", hash = "sha256:c366e3de1df4cdd635f1ad8cbcd3af01a384d7abda71900e68d43b04eb6aaca7" }, + { url = "https://files.pythonhosted.org/packages/d1/ae/afb1487556e2dc827a17097aac8158a25b433a345386f0e249f6d2694ccb/devtools-0.12.2-py3-none-any.whl", hash = "sha256:c366e3de1df4cdd635f1ad8cbcd3af01a384d7abda71900e68d43b04eb6aaca7", size = 19411 }, ] [[package]] name = "dirty-equals" version = "0.11" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/1d/c5913ac9d6615515a00f4bdc71356d302437cb74ff2e9aaccd3c14493b78/dirty_equals-0.11.tar.gz", hash = "sha256:f4ac74ee88f2d11e2fa0f65eb30ee4f07105c5f86f4dc92b09eb1138775027c3", size = 128067, upload-time = "2025-11-17T01:51:24.451Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/1d/c5913ac9d6615515a00f4bdc71356d302437cb74ff2e9aaccd3c14493b78/dirty_equals-0.11.tar.gz", hash = "sha256:f4ac74ee88f2d11e2fa0f65eb30ee4f07105c5f86f4dc92b09eb1138775027c3", size = 128067 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/8d/dbff05239043271dbeace563a7686212a3dd517864a35623fe4d4a64ca19/dirty_equals-0.11-py3-none-any.whl", hash = "sha256:b1d7093273fc2f9be12f443a8ead954ef6daaf6746fd42ef3a5616433ee85286", size = 28051, upload-time = "2025-11-17T01:51:22.849Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8d/dbff05239043271dbeace563a7686212a3dd517864a35623fe4d4a64ca19/dirty_equals-0.11-py3-none-any.whl", hash = "sha256:b1d7093273fc2f9be12f443a8ead954ef6daaf6746fd42ef3a5616433ee85286", size = 28051 }, ] [[package]] name = "diskcache" version = "5.6.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19" }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, ] [[package]] name = "distro" version = "1.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, ] [[package]] name = "dnspython" version = "2.8.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094 }, ] [[package]] name = "docler" version = "2.1.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv" }, { name = "mkdown" }, @@ -1307,78 +1077,78 @@ dependencies = [ { name = "schemez" }, { name = "upathtools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/fa/9d0da5814fae67fc890613f1dcf00fca0b1aea4fbf430502a1f76a20e2fa/docler-2.1.1.tar.gz", hash = "sha256:e231309e648924e8218475e37838ea1b917799d3cb5dfa7c10e7d31851fb7354", size = 1406248, upload-time = "2026-01-04T03:40:00.131Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/fa/9d0da5814fae67fc890613f1dcf00fca0b1aea4fbf430502a1f76a20e2fa/docler-2.1.1.tar.gz", hash = "sha256:e231309e648924e8218475e37838ea1b917799d3cb5dfa7c10e7d31851fb7354", size = 1406248 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/64/f5901aa7e2029944de35b243164c0f01a45c06355d004a3b17cb0484de4b/docler-2.1.1-py3-none-any.whl", hash = "sha256:446d2476530017465519cb4d2da7d836a7b2cf531032bd16a3867549aef737ae", size = 1444038, upload-time = "2026-01-04T03:40:04.19Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/f5901aa7e2029944de35b243164c0f01a45c06355d004a3b17cb0484de4b/docler-2.1.1-py3-none-any.whl", hash = "sha256:446d2476530017465519cb4d2da7d836a7b2cf531032bd16a3867549aef737ae", size = 1444038 }, ] [[package]] name = "docstring-parser" version = "0.18.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, + { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484 }, ] [[package]] name = "edge-tts" version = "7.2.8" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "certifi" }, { name = "tabulate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/60/afbf548b43c78355e03926c6b1fff7500303a2da4d84db9e1324119e21ae/edge_tts-7.2.8.tar.gz", hash = "sha256:fcf185a0d527a0d2d003f9d5841facc1d5e0e7b3b88d5df9c32990402c6b8cd0", size = 27875, upload-time = "2026-03-22T19:57:50.962Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/60/afbf548b43c78355e03926c6b1fff7500303a2da4d84db9e1324119e21ae/edge_tts-7.2.8.tar.gz", hash = "sha256:fcf185a0d527a0d2d003f9d5841facc1d5e0e7b3b88d5df9c32990402c6b8cd0", size = 27875 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/2b/a8cb687b92a2690d2ad171f0c2fd1c8f18690363cca7618bab2bbe4cdf2b/edge_tts-7.2.8-py3-none-any.whl", hash = "sha256:361fe48ce7ef613adbe30f664e3765dd71029c6cb57427279eff8ad6df2eb211", size = 31026, upload-time = "2026-03-22T19:57:49.672Z" }, + { url = "https://files.pythonhosted.org/packages/8c/2b/a8cb687b92a2690d2ad171f0c2fd1c8f18690363cca7618bab2bbe4cdf2b/edge_tts-7.2.8-py3-none-any.whl", hash = "sha256:361fe48ce7ef613adbe30f664e3765dd71029c6cb57427279eff8ad6df2eb211", size = 31026 }, ] [[package]] name = "email-validator" version = "2.3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dnspython" }, { name = "idna" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604 }, ] [[package]] name = "epregistry" version = "2.0.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/c2/fa050adee0743de7bc55d923d938398dcc22c76a13161b5c4fd350f71eda/epregistry-2.0.3.tar.gz", hash = "sha256:c0e54fd2dcb9eadb61837b5ad6729a7567dbc98b98bbd4e00628ad7caf49c216", size = 11829, upload-time = "2025-11-29T01:04:41.244Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/c2/fa050adee0743de7bc55d923d938398dcc22c76a13161b5c4fd350f71eda/epregistry-2.0.3.tar.gz", hash = "sha256:c0e54fd2dcb9eadb61837b5ad6729a7567dbc98b98bbd4e00628ad7caf49c216", size = 11829 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/2d/f8dcdfa47df719e0c5efba6f6212b09d0fe5fb247a6f26649e17311f9838/epregistry-2.0.3-py3-none-any.whl", hash = "sha256:9dfbc47787558c72d3f54290a7586b8b2202309eec2bedddad95c50170ef876b", size = 11773, upload-time = "2025-11-29T01:04:40.129Z" }, + { url = "https://files.pythonhosted.org/packages/29/2d/f8dcdfa47df719e0c5efba6f6212b09d0fe5fb247a6f26649e17311f9838/epregistry-2.0.3-py3-none-any.whl", hash = "sha256:9dfbc47787558c72d3f54290a7586b8b2202309eec2bedddad95c50170ef876b", size = 11773 }, ] [[package]] name = "eval-type-backport" version = "0.4.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/15/273a4baf8248d6d76220723c3caf039d283774b31a7c46ba686120145b76/eval_type_backport-0.4.0.tar.gz", hash = "sha256:8397d25e6524c2e67b9576bb0636be27dea2192017711220c534ec2de921e9b0", size = 10260, upload-time = "2026-06-02T13:22:06.059Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/15/273a4baf8248d6d76220723c3caf039d283774b31a7c46ba686120145b76/eval_type_backport-0.4.0.tar.gz", hash = "sha256:8397d25e6524c2e67b9576bb0636be27dea2192017711220c534ec2de921e9b0", size = 10260 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/a7/bb99bf5e6f78736ddb53480f2c3ff3702ffe2196a7c5e1661c03081d398e/eval_type_backport-0.4.0-py3-none-any.whl", hash = "sha256:ad5e2a8db71b6696a56eafb938b0f5a337d3217f256b8e158b469422b4772b20", size = 6432, upload-time = "2026-06-02T13:22:04.827Z" }, + { url = "https://files.pythonhosted.org/packages/50/a7/bb99bf5e6f78736ddb53480f2c3ff3702ffe2196a7c5e1661c03081d398e/eval_type_backport-0.4.0-py3-none-any.whl", hash = "sha256:ad5e2a8db71b6696a56eafb938b0f5a337d3217f256b8e158b469422b4772b20", size = 6432 }, ] [[package]] name = "evented" version = "1.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv" }, { name = "pydantic" }, { name = "schemez" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/bb/8eb332b5cff32f01e6d719186dec43f94cc3cf44bb9043ffe567febc731f/evented-1.1.0.tar.gz", hash = "sha256:7cab246065417fcac8a5531c4a96ac7467ebe9f6eecddc2047e986cfb265a1ca", size = 16248, upload-time = "2026-01-04T07:12:23.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/bb/8eb332b5cff32f01e6d719186dec43f94cc3cf44bb9043ffe567febc731f/evented-1.1.0.tar.gz", hash = "sha256:7cab246065417fcac8a5531c4a96ac7467ebe9f6eecddc2047e986cfb265a1ca", size = 16248 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/86/4246272170071672ddcb30b415edf32a71391b6eae93ba0365761eab0678/evented-1.1.0-py3-none-any.whl", hash = "sha256:ed8e0a30c54b24029851b2b8508cb7e4353b04bb919bf2db19dbaa57c5a06bed", size = 19801, upload-time = "2026-01-04T07:12:24.585Z" }, + { url = "https://files.pythonhosted.org/packages/a3/86/4246272170071672ddcb30b415edf32a71391b6eae93ba0365761eab0678/evented-1.1.0-py3-none-any.whl", hash = "sha256:ed8e0a30c54b24029851b2b8508cb7e4353b04bb919bf2db19dbaa57c5a06bed", size = 19801 }, ] [package.optional-dependencies] @@ -1392,7 +1162,7 @@ all = [ [[package]] name = "exa-py" version = "2.16.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpcore" }, { name = "httpx" }, @@ -1402,71 +1172,71 @@ dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/a3/dabad8d3dbdb50d2ad49f426cc3e1fb47a0d0fd6ba10fbb27219c9bef684/exa_py-2.16.0.tar.gz", hash = "sha256:dce0db698720b1b7b39b58a1a17ad65fcc5969ee47f810cff5bcc836aeb0f777", size = 63530, upload-time = "2026-07-02T01:04:38.797Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/a3/dabad8d3dbdb50d2ad49f426cc3e1fb47a0d0fd6ba10fbb27219c9bef684/exa_py-2.16.0.tar.gz", hash = "sha256:dce0db698720b1b7b39b58a1a17ad65fcc5969ee47f810cff5bcc836aeb0f777", size = 63530 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/c8/69d100ed12f8493ab8b9d03bf20cd0080a3a60dec4abadb1782cc4f91969/exa_py-2.16.0-py3-none-any.whl", hash = "sha256:cf726f801d6e4be25f38275a6318b536445a92077450a11cb3d7e493ea77ecc2", size = 86866, upload-time = "2026-07-02T01:04:37.451Z" }, + { url = "https://files.pythonhosted.org/packages/3c/c8/69d100ed12f8493ab8b9d03bf20cd0080a3a60dec4abadb1782cc4f91969/exa_py-2.16.0-py3-none-any.whl", hash = "sha256:cf726f801d6e4be25f38275a6318b536445a92077450a11cb3d7e493ea77ecc2", size = 86866 }, ] [[package]] name = "exceptiongroup" version = "1.3.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 }, ] [[package]] name = "execnet" version = "2.1.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, + { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708 }, ] [[package]] name = "executing" version = "2.2.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, ] [[package]] name = "extism" version = "1.1.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "extism-sys" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/c6/573488066680ef80ba6a46ab5e0df42aa4beafac51d2c3cc140a062531af/extism-1.1.1.tar.gz", hash = "sha256:067bf4ebd89ba85681508a77eb7f213ab13d2dbd6e71dabdac5d9622c61ee1d1", size = 11589, upload-time = "2026-03-26T22:03:42.955Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c6/573488066680ef80ba6a46ab5e0df42aa4beafac51d2c3cc140a062531af/extism-1.1.1.tar.gz", hash = "sha256:067bf4ebd89ba85681508a77eb7f213ab13d2dbd6e71dabdac5d9622c61ee1d1", size = 11589 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/ac/f730be96df64fd3c85b0c24f16993181f72b72dfba3099ad86d49a55e7c0/extism-1.1.1-py3-none-any.whl", hash = "sha256:157692d7dc79cde6b1c9759eaf765f04b34b28e1f3a4a84507294c06081c8e5b", size = 11210, upload-time = "2026-03-26T22:03:42.092Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ac/f730be96df64fd3c85b0c24f16993181f72b72dfba3099ad86d49a55e7c0/extism-1.1.1-py3-none-any.whl", hash = "sha256:157692d7dc79cde6b1c9759eaf765f04b34b28e1f3a4a84507294c06081c8e5b", size = 11210 }, ] [[package]] name = "extism-sys" version = "1.12.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/30/06f1cbe28c3eb486dffc87b9d4e3d5522a8656fac86c9c05cc102f74732b/extism_sys-1.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f0225b2b2969bda66b0acab5ad13489e80b1b03b2fa7ad7be06dab57a381968d", size = 7597910, upload-time = "2025-07-14T18:53:50.102Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/1e/37a0c9422105909dd3932eac955c15122b9797ae305d6da3046b7036a9e3/extism_sys-1.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:242f021d12feee10bf67cc9a08c078d9b5e1da5c7a74f1f2eef61c10da36fccd", size = 7120898, upload-time = "2025-07-14T18:53:51.489Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/1f/50d6a4d22e8be5e50d379aee27cde1564c726ca42f3194d1b970a079f239/extism_sys-1.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db850dc58081b48d0d50a8c7395806cbe764b82c50a971c0be00519bf336200b", size = 8355066, upload-time = "2025-07-14T18:53:52.825Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/7a/88fad64e8ae9bce4d2d26495e14600100145ecc3e3218fe9766b5e70d31f/extism_sys-1.12.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:687a1b1f1c009383aa466fe12b35ddda54d84c1775fc583e256a946e2bd17561", size = 7912809, upload-time = "2025-07-14T18:53:54.733Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/f6/50ff821a6ff825f89cc4ccccfcff46ac3755b474edfb1651fcd767196586/extism_sys-1.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:24f15d958db96bd17cd8a98dee9c172d0bd71094ff71cf9de193645a80d30269", size = 8087657, upload-time = "2025-07-14T18:53:56.065Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/81/d32e279cd464292f5a51ee68d47f643012d937664259f03d5639630867ca/extism_sys-1.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9be0c414e08414d4ce5356a185b4f05cff780352feb6755b75a75455e84c3d0f", size = 8523720, upload-time = "2025-07-14T18:53:57.356Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/4c/aeaeb544f667693690bf48101f6a11d1c87784c7622af6f7aa5427602fe5/extism_sys-1.12.0-py3-none-win_amd64.whl", hash = "sha256:be2ddf0120117ac495e8686f5bc80ab64a5ffe0b55b8fa2b77ee23b5ea0b3836", size = 6588019, upload-time = "2025-07-14T18:53:59.149Z" }, + { url = "https://files.pythonhosted.org/packages/01/30/06f1cbe28c3eb486dffc87b9d4e3d5522a8656fac86c9c05cc102f74732b/extism_sys-1.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f0225b2b2969bda66b0acab5ad13489e80b1b03b2fa7ad7be06dab57a381968d", size = 7597910 }, + { url = "https://files.pythonhosted.org/packages/04/1e/37a0c9422105909dd3932eac955c15122b9797ae305d6da3046b7036a9e3/extism_sys-1.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:242f021d12feee10bf67cc9a08c078d9b5e1da5c7a74f1f2eef61c10da36fccd", size = 7120898 }, + { url = "https://files.pythonhosted.org/packages/42/1f/50d6a4d22e8be5e50d379aee27cde1564c726ca42f3194d1b970a079f239/extism_sys-1.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db850dc58081b48d0d50a8c7395806cbe764b82c50a971c0be00519bf336200b", size = 8355066 }, + { url = "https://files.pythonhosted.org/packages/d9/7a/88fad64e8ae9bce4d2d26495e14600100145ecc3e3218fe9766b5e70d31f/extism_sys-1.12.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:687a1b1f1c009383aa466fe12b35ddda54d84c1775fc583e256a946e2bd17561", size = 7912809 }, + { url = "https://files.pythonhosted.org/packages/79/f6/50ff821a6ff825f89cc4ccccfcff46ac3755b474edfb1651fcd767196586/extism_sys-1.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:24f15d958db96bd17cd8a98dee9c172d0bd71094ff71cf9de193645a80d30269", size = 8087657 }, + { url = "https://files.pythonhosted.org/packages/bd/81/d32e279cd464292f5a51ee68d47f643012d937664259f03d5639630867ca/extism_sys-1.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9be0c414e08414d4ce5356a185b4f05cff780352feb6755b75a75455e84c3d0f", size = 8523720 }, + { url = "https://files.pythonhosted.org/packages/54/4c/aeaeb544f667693690bf48101f6a11d1c87784c7622af6f7aa5427602fe5/extism_sys-1.12.0-py3-none-win_amd64.whl", hash = "sha256:be2ddf0120117ac495e8686f5bc80ab64a5ffe0b55b8fa2b77ee23b5ea0b3836", size = 6588019 }, ] [[package]] name = "exxec" version = "0.4.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv" }, { name = "ptyprocess" }, @@ -1474,29 +1244,29 @@ dependencies = [ { name = "schemez" }, { name = "upathtools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/e3/4704f37bcfc30c07baf401254340ac48be6be80c439eff039813d6dc227e/exxec-0.4.1.tar.gz", hash = "sha256:12abc6324d421bdecf337193b324230b241eaab8a3d0e477e0e077315bb4264f", size = 94422, upload-time = "2026-04-25T02:28:55.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/e3/4704f37bcfc30c07baf401254340ac48be6be80c439eff039813d6dc227e/exxec-0.4.1.tar.gz", hash = "sha256:12abc6324d421bdecf337193b324230b241eaab8a3d0e477e0e077315bb4264f", size = 94422 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/34/c66b5a28616e17ded711fee9d596bda9f5cece41001973a0d1319a61c333/exxec-0.4.1-py3-none-any.whl", hash = "sha256:623215b41a63da4ef77e4b03c5a07626abae602ebd0657740002e79748598984", size = 133988, upload-time = "2026-04-25T02:29:01.106Z" }, + { url = "https://files.pythonhosted.org/packages/d3/34/c66b5a28616e17ded711fee9d596bda9f5cece41001973a0d1319a61c333/exxec-0.4.1-py3-none-any.whl", hash = "sha256:623215b41a63da4ef77e4b03c5a07626abae602ebd0657740002e79748598984", size = 133988 }, ] [[package]] name = "fasta2a" version = "0.6.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "pydantic" }, { name = "starlette" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/28/35949997804129537becf17d0635b8b2b67036589a4da851e44a053857fc/fasta2a-0.6.1.tar.gz", hash = "sha256:e97b92771ad338166b1516dcbb687c9f3f8392a5cd5820542ec4b75ff5219a48", size = 1453473, upload-time = "2026-05-15T20:51:22.059Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/28/35949997804129537becf17d0635b8b2b67036589a4da851e44a053857fc/fasta2a-0.6.1.tar.gz", hash = "sha256:e97b92771ad338166b1516dcbb687c9f3f8392a5cd5820542ec4b75ff5219a48", size = 1453473 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/21/4d7e00c10bb4ec33a196efc55190ae086e97f5f487945bdfceb94df3e8d8/fasta2a-0.6.1-py3-none-any.whl", hash = "sha256:1b5369ee1b62405eebafa6d71c57c9e40a1a3f0dcbae5ec3aa6ee450361028dd", size = 28888, upload-time = "2026-05-15T20:51:20.222Z" }, + { url = "https://files.pythonhosted.org/packages/21/21/4d7e00c10bb4ec33a196efc55190ae086e97f5f487945bdfceb94df3e8d8/fasta2a-0.6.1-py3-none-any.whl", hash = "sha256:1b5369ee1b62405eebafa6d71c57c9e40a1a3f0dcbae5ec3aa6ee450361028dd", size = 28888 }, ] [[package]] name = "fastapi" version = "0.139.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, { name = "pydantic" }, @@ -1504,15 +1274,15 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/95/d3f0ae10836324a2eab98a52b61210ac609f08200bf4bb0dc8132d32f78a/fastapi-0.139.2.tar.gz", hash = "sha256:333145a6891e9b5b3cfceb69baf817e8240cde4d4588ae5a10bf56ffacb6255e", size = 423428, upload-time = "2026-07-16T15:06:17.912Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/95/d3f0ae10836324a2eab98a52b61210ac609f08200bf4bb0dc8132d32f78a/fastapi-0.139.2.tar.gz", hash = "sha256:333145a6891e9b5b3cfceb69baf817e8240cde4d4588ae5a10bf56ffacb6255e", size = 423428 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/c7/cb03251d9dfb177246a9809a76f189d21df32dbd4a845951881d11323b7f/fastapi-0.139.2-py3-none-any.whl", hash = "sha256:b9ad015a835173d59865e2f5d8296fbc2b317bf56a2ba1a5bfbdd03de2fd4b1c", size = 130234, upload-time = "2026-07-16T15:06:19.557Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c7/cb03251d9dfb177246a9809a76f189d21df32dbd4a845951881d11323b7f/fastapi-0.139.2-py3-none-any.whl", hash = "sha256:b9ad015a835173d59865e2f5d8296fbc2b317bf56a2ba1a5bfbdd03de2fd4b1c", size = 130234 }, ] [[package]] name = "fastembed" version = "0.8.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, { name = "loguru" }, @@ -1525,27 +1295,27 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/25/58865e36b6e8a9a0d0ff905b5601aa30db97956327c0df42ec4ed6accc21/fastembed-0.8.0.tar.gz", hash = "sha256:75966edfa8b006ee78514c726bd7f6a50721dadc89305279052be9db72fd53e8", size = 75115, upload-time = "2026-03-23T16:34:41.699Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/25/58865e36b6e8a9a0d0ff905b5601aa30db97956327c0df42ec4ed6accc21/fastembed-0.8.0.tar.gz", hash = "sha256:75966edfa8b006ee78514c726bd7f6a50721dadc89305279052be9db72fd53e8", size = 75115 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/e8/26b7d78bb8972498c467ca34cb12ee2e60d26ba5eae6d8443189a1af37a5/fastembed-0.8.0-py3-none-any.whl", hash = "sha256:40bee672657574a1009e35ec50030a55f2b426842cb011845379817641bbbbd0", size = 116572, upload-time = "2026-03-23T16:34:40.69Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e8/26b7d78bb8972498c467ca34cb12ee2e60d26ba5eae6d8443189a1af37a5/fastembed-0.8.0-py3-none-any.whl", hash = "sha256:40bee672657574a1009e35ec50030a55f2b426842cb011845379817641bbbbd0", size = 116572 }, ] [[package]] name = "fastmcp" version = "3.4.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastmcp-slim", extra = ["client", "server"] }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/f7/5188565d1b93ad611cbd80bf473e7ad669d1f3b689c4bedcd304e1ec3472/fastmcp-3.4.4.tar.gz", hash = "sha256:378202e26ec15b23819d9a1c0d1b0ebda096bc712720532010a0b82a45c2b1df", size = 28796458, upload-time = "2026-07-09T00:32:41.352Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/f7/5188565d1b93ad611cbd80bf473e7ad669d1f3b689c4bedcd304e1ec3472/fastmcp-3.4.4.tar.gz", hash = "sha256:378202e26ec15b23819d9a1c0d1b0ebda096bc712720532010a0b82a45c2b1df", size = 28796458 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/67/3cef84ba38a23dca1e1e776bfda8a35ab3c7a6c94a8ca81d0715de6dd3c5/fastmcp-3.4.4-py3-none-any.whl", hash = "sha256:f86f208713212260068cf55c32936839eee856fefc7808e18a032f31eb0f718e", size = 8019, upload-time = "2026-07-09T00:32:39.411Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/3cef84ba38a23dca1e1e776bfda8a35ab3c7a6c94a8ca81d0715de6dd3c5/fastmcp-3.4.4-py3-none-any.whl", hash = "sha256:f86f208713212260068cf55c32936839eee856fefc7808e18a032f31eb0f718e", size = 8019 }, ] [[package]] name = "fastmcp-slim" version = "3.4.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "platformdirs" }, { name = "pydantic", extra = ["email"] }, @@ -1554,9 +1324,9 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/79/f35661c6a1d76dfbe17a079f912d96fffcfdd40fad5a9144bb9e7dfb1fdf/fastmcp_slim-3.4.4.tar.gz", hash = "sha256:dcaa3e0be2127d7eacdce592c2ef0039204923dc0ec396454615cb4a3275b078", size = 590203, upload-time = "2026-07-09T00:32:20.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/79/f35661c6a1d76dfbe17a079f912d96fffcfdd40fad5a9144bb9e7dfb1fdf/fastmcp_slim-3.4.4.tar.gz", hash = "sha256:dcaa3e0be2127d7eacdce592c2ef0039204923dc0ec396454615cb4a3275b078", size = 590203 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/91/321e0b2e9ed70d0628b17ddaec76fc7b09f3e1d5d290f70bf101a2890142/fastmcp_slim-3.4.4-py3-none-any.whl", hash = "sha256:9d3a6327b9ee835188eb7323fc3b5d4cd061631b48da8ece56794bb538972505", size = 765158, upload-time = "2026-07-09T00:32:19.11Z" }, + { url = "https://files.pythonhosted.org/packages/16/91/321e0b2e9ed70d0628b17ddaec76fc7b09f3e1d5d290f70bf101a2890142/fastmcp_slim-3.4.4-py3-none-any.whl", hash = "sha256:9d3a6327b9ee835188eb7323fc3b5d4cd061631b48da8ece56794bb538972505", size = 765158 }, ] [package.optional-dependencies] @@ -1596,179 +1366,179 @@ server = [ [[package]] name = "fieldz" version = "0.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/46/7a8d1db959eabd5d3e52bd3c000a8b132184b7651cbd5481c4cbf31ff65d/fieldz-0.2.0.tar.gz", hash = "sha256:e11215188cad5c5371113d2b7707155960efd0d48500b6bf675648bc3f6fc8d6", size = 18222, upload-time = "2026-02-24T19:26:02.251Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/46/7a8d1db959eabd5d3e52bd3c000a8b132184b7651cbd5481c4cbf31ff65d/fieldz-0.2.0.tar.gz", hash = "sha256:e11215188cad5c5371113d2b7707155960efd0d48500b6bf675648bc3f6fc8d6", size = 18222 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/9d/9034acaf3d80e85deb3d49876cb734479327b9cf89a918815ef67cb6b36c/fieldz-0.2.0-py3-none-any.whl", hash = "sha256:43b5be702816df39f55d08ae392eaabe58d3c69d2e4b61a853252cb2da41931f", size = 17946, upload-time = "2026-02-24T19:26:00.931Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9d/9034acaf3d80e85deb3d49876cb734479327b9cf89a918815ef67cb6b36c/fieldz-0.2.0-py3-none-any.whl", hash = "sha256:43b5be702816df39f55d08ae392eaabe58d3c69d2e4b61a853252cb2da41931f", size = 17946 }, ] [[package]] name = "filelock" version = "3.32.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/80/8232b582c4b318b817cf1274ba74976b07b34d35ef439b3eb948f98645a1/filelock-3.32.0.tar.gz", hash = "sha256:7be2ad23a14607ccc71808e68fe30848aeace7058ace17852f68e2a68e310402", size = 213757, upload-time = "2026-07-21T13:17:42.898Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/80/8232b582c4b318b817cf1274ba74976b07b34d35ef439b3eb948f98645a1/filelock-3.32.0.tar.gz", hash = "sha256:7be2ad23a14607ccc71808e68fe30848aeace7058ace17852f68e2a68e310402", size = 213757 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/79/b4c714bef36bc4ec2beeae1e0c124f0223888cd8c6feb1cdc56038116920/filelock-3.32.0-py3-none-any.whl", hash = "sha256:d396bea984af47333ef05e50eae7eff88c84256de6112aea0ec48a233c064fe3", size = 97732, upload-time = "2026-07-21T13:17:41.55Z" }, + { url = "https://files.pythonhosted.org/packages/06/79/b4c714bef36bc4ec2beeae1e0c124f0223888cd8c6feb1cdc56038116920/filelock-3.32.0-py3-none-any.whl", hash = "sha256:d396bea984af47333ef05e50eae7eff88c84256de6112aea0ec48a233c064fe3", size = 97732 }, ] [[package]] name = "flatbuffers" version = "25.12.19" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661 }, ] [[package]] name = "frozenlist" version = "1.8.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717 }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651 }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417 }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391 }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048 }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549 }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833 }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363 }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314 }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365 }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763 }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110 }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717 }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628 }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882 }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676 }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235 }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742 }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725 }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506 }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161 }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676 }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638 }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067 }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101 }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901 }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395 }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659 }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492 }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034 }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, ] [[package]] name = "fsspec" version = "2026.6.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646, upload-time = "2026-06-16T01:57:28.105Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949, upload-time = "2026-06-16T01:57:26.358Z" }, + { url = "https://files.pythonhosted.org/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949 }, ] [[package]] name = "genai-prices" version = "0.0.72" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx2" }, { name = "pydantic" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/c8/2549fa8ceaaf0bd61114cf392250a107be657233723bc4e65cb91dba934f/genai_prices-0.0.72.tar.gz", hash = "sha256:a7e481d0ea85922fcf48df6864f2491fc81a4f03dcf0ecbd9aa8c2c6d9fcdbe8", size = 82753, upload-time = "2026-07-22T21:11:03.997Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/c8/2549fa8ceaaf0bd61114cf392250a107be657233723bc4e65cb91dba934f/genai_prices-0.0.72.tar.gz", hash = "sha256:a7e481d0ea85922fcf48df6864f2491fc81a4f03dcf0ecbd9aa8c2c6d9fcdbe8", size = 82753 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/2e/1e2c31666f7e4e2d0327a80f1c920fd8764ccd27672c4bc389d79219ece6/genai_prices-0.0.72-py3-none-any.whl", hash = "sha256:21281d8df34d9bfbb736e0763a60ceda02226938cd47379ae437a44fcf8ba23d", size = 85238, upload-time = "2026-07-22T21:11:02.911Z" }, + { url = "https://files.pythonhosted.org/packages/22/2e/1e2c31666f7e4e2d0327a80f1c920fd8764ccd27672c4bc389d79219ece6/genai_prices-0.0.72-py3-none-any.whl", hash = "sha256:21281d8df34d9bfbb736e0763a60ceda02226938cd47379ae437a44fcf8ba23d", size = 85238 }, ] [[package]] name = "genson" version = "1.4.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/53/de162dc8e03fccd9ebe59d17c7812378fe8bd2b604f6b1b94d00165140ac/genson-1.4.0.tar.gz", hash = "sha256:bc7f1c1bae87a21ca44d81149aec95a3f4468d676de9b8b08caa064f3c50b3da", size = 47908, upload-time = "2026-07-06T08:21:50.331Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/53/de162dc8e03fccd9ebe59d17c7812378fe8bd2b604f6b1b94d00165140ac/genson-1.4.0.tar.gz", hash = "sha256:bc7f1c1bae87a21ca44d81149aec95a3f4468d676de9b8b08caa064f3c50b3da", size = 47908 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/02/767f744ab6d4cb7761e5008acc3d534b7a0481af62563d52e391fbcb2140/genson-1.4.0-py3-none-any.whl", hash = "sha256:03bc71bbe52defde70660cc4dcd1ea1097997da5a1cbb90a9dbd3acc7c9e1b65", size = 24484, upload-time = "2026-07-06T08:21:49.046Z" }, + { url = "https://files.pythonhosted.org/packages/16/02/767f744ab6d4cb7761e5008acc3d534b7a0481af62563d52e391fbcb2140/genson-1.4.0-py3-none-any.whl", hash = "sha256:03bc71bbe52defde70660cc4dcd1ea1097997da5a1cbb90a9dbd3acc7c9e1b65", size = 24484 }, ] [[package]] name = "ghp-import" version = "2.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, ] [[package]] name = "git-changelog" version = "2.9.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jinja2" }, { name = "packaging" }, { name = "platformdirs" }, { name = "semver" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/7f/17e58ec656fd79d4a3b02517923e24a93b28fe2e4518568aedb4ce038fb9/git_changelog-2.9.4.tar.gz", hash = "sha256:06a38a0d174a069b760430ec41493bf53307be13f16b0e48ce379be1646ea9a0", size = 92601, upload-time = "2026-04-29T14:01:39.575Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/7f/17e58ec656fd79d4a3b02517923e24a93b28fe2e4518568aedb4ce038fb9/git_changelog-2.9.4.tar.gz", hash = "sha256:06a38a0d174a069b760430ec41493bf53307be13f16b0e48ce379be1646ea9a0", size = 92601 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/a6/fb6713ce5c128078492b5ce691bd27b8a7c6f2ad84aca99d3b847ab1721d/git_changelog-2.9.4-py3-none-any.whl", hash = "sha256:c19a099640518fbbc545cb240250cf8129e55e668d8374ba8061ea5456da204b", size = 40604, upload-time = "2026-04-29T14:01:38.195Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/fb6713ce5c128078492b5ce691bd27b8a7c6f2ad84aca99d3b847ab1721d/git_changelog-2.9.4-py3-none-any.whl", hash = "sha256:c19a099640518fbbc545cb240250cf8129e55e668d8374ba8061ea5456da204b", size = 40604 }, ] [[package]] name = "gitdb" version = "4.0.12" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smmap" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, ] [[package]] name = "githarbor" version = "1.0.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "universal-pathlib" }, { name = "upathtools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/00/fffef32d9dd820616d346964c9cabb91fa3fc404483e3575c3a62f3137b8/githarbor-1.0.2.tar.gz", hash = "sha256:6df681fa04d8e951f8b1af508a44362d48cf88440037714b07be7fa589a6018e", size = 66500, upload-time = "2025-12-10T11:47:05.767Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/00/fffef32d9dd820616d346964c9cabb91fa3fc404483e3575c3a62f3137b8/githarbor-1.0.2.tar.gz", hash = "sha256:6df681fa04d8e951f8b1af508a44362d48cf88440037714b07be7fa589a6018e", size = 66500 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/70/d546991d92c686dd3ee594497b52721f6750f5233abad5133b021fbab35e/githarbor-1.0.2-py3-none-any.whl", hash = "sha256:5c1ec1eb78d028a3f68188cec1e32ca5f6ffae4a90749a81513172afcfb3394e", size = 91646, upload-time = "2025-12-10T11:47:07.481Z" }, + { url = "https://files.pythonhosted.org/packages/bb/70/d546991d92c686dd3ee594497b52721f6750f5233abad5133b021fbab35e/githarbor-1.0.2-py3-none-any.whl", hash = "sha256:5c1ec1eb78d028a3f68188cec1e32ca5f6ffae4a90749a81513172afcfb3394e", size = 91646 }, ] [[package]] name = "gitpython" version = "3.1.55" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/ab/ba0d29f2fa2277ed6256b2ac09003494045355f3a10bf32f351761287870/gitpython-3.1.55.tar.gz", hash = "sha256:781e3b1624dad81b24e9524bf0297b69786a0706db2cbceec1e2b05c38e5152f", size = 225071, upload-time = "2026-07-23T02:52:43.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/ab/ba0d29f2fa2277ed6256b2ac09003494045355f3a10bf32f351761287870/gitpython-3.1.55.tar.gz", hash = "sha256:781e3b1624dad81b24e9524bf0297b69786a0706db2cbceec1e2b05c38e5152f", size = 225071 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/6a/d3b8208d2f8aac66abe8ccc1c23fa2c89464ec42cc71a601e95d05902428/gitpython-3.1.55-py3-none-any.whl", hash = "sha256:7c9ec1e69c158c081632ab35c41471e302c96db2ae42165036a5d2403378812e", size = 216590, upload-time = "2026-07-23T02:52:41.932Z" }, + { url = "https://files.pythonhosted.org/packages/20/6a/d3b8208d2f8aac66abe8ccc1c23fa2c89464ec42cc71a601e95d05902428/gitpython-3.1.55-py3-none-any.whl", hash = "sha256:7c9ec1e69c158c081632ab35c41471e302c96db2ae42165036a5d2403378812e", size = 216590 }, ] [[package]] name = "google-auth" version = "2.56.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/33/dbc946a407401b975f0719658f18e664ece2109f79ffd1ff3bf226c205f4/google_auth-2.56.2.tar.gz", hash = "sha256:e28f103ca8091fb7012b99c44243d7366c29863713b8e34a220c3322b7a07051", size = 365820, upload-time = "2026-07-21T21:53:28.188Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/33/dbc946a407401b975f0719658f18e664ece2109f79ffd1ff3bf226c205f4/google_auth-2.56.2.tar.gz", hash = "sha256:e28f103ca8091fb7012b99c44243d7366c29863713b8e34a220c3322b7a07051", size = 365820 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/63/50636aae68c9bf17c891c7eb18b49baa9bd6b31d2a97b8de4813a9fc8d1c/google_auth-2.56.2-py3-none-any.whl", hash = "sha256:c8270ea95b2697b74e3d8438ae9c5b898e38b623b915c7b5c5635921e7de68a6", size = 258588, upload-time = "2026-07-21T21:53:26.399Z" }, + { url = "https://files.pythonhosted.org/packages/88/63/50636aae68c9bf17c891c7eb18b49baa9bd6b31d2a97b8de4813a9fc8d1c/google_auth-2.56.2-py3-none-any.whl", hash = "sha256:c8270ea95b2697b74e3d8438ae9c5b898e38b623b915c7b5c5635921e7de68a6", size = 258588 }, ] [package.optional-dependencies] @@ -1779,7 +1549,7 @@ requests = [ [[package]] name = "google-genai" version = "2.14.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -1792,121 +1562,121 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/df/4f820054c99f29f2fe3de4a8a7c9534dd795302e4a07483a0cb07c3a29b6/google_genai-2.14.0.tar.gz", hash = "sha256:a9d1f4f362d76280f1be1340fcb3c86e63dbca128f6a4ae09d86ab47ff7148e8", size = 641055, upload-time = "2026-07-22T21:35:44.717Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/df/4f820054c99f29f2fe3de4a8a7c9534dd795302e4a07483a0cb07c3a29b6/google_genai-2.14.0.tar.gz", hash = "sha256:a9d1f4f362d76280f1be1340fcb3c86e63dbca128f6a4ae09d86ab47ff7148e8", size = 641055 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/86/5ac5fb53e44cca4a6607fb917eb331fa237c65a103b9ec2e8e8acc8a42db/google_genai-2.14.0-py3-none-any.whl", hash = "sha256:ae7172cdd35695189b516b33a878e4132e5daa2dbc03a5b44cddfa8a82fad664", size = 1030738, upload-time = "2026-07-22T21:35:42.785Z" }, + { url = "https://files.pythonhosted.org/packages/ba/86/5ac5fb53e44cca4a6607fb917eb331fa237c65a103b9ec2e8e8acc8a42db/google_genai-2.14.0-py3-none-any.whl", hash = "sha256:ae7172cdd35695189b516b33a878e4132e5daa2dbc03a5b44cddfa8a82fad664", size = 1030738 }, ] [[package]] name = "googleapis-common-protos" version = "1.75.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035, upload-time = "2026-05-07T08:04:49.423Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631 }, ] [[package]] name = "greenlet" version = "3.5.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/74/b13368064b09053253555d3f2839cc2684d22d5aed0d2ccffbf7a6736558/greenlet-3.5.4.tar.gz", hash = "sha256:0232ae1de90a8e07867bb127d7a6ba2301e859145489f25cda8a6096dabe1d20", size = 206538, upload-time = "2026-07-22T12:47:14.468Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/74/b13368064b09053253555d3f2839cc2684d22d5aed0d2ccffbf7a6736558/greenlet-3.5.4.tar.gz", hash = "sha256:0232ae1de90a8e07867bb127d7a6ba2301e859145489f25cda8a6096dabe1d20", size = 206538 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/9a/e51225dcd58713f16ccbdcc501a8da21098ea14515b7870f1f94459e5ff5/greenlet-3.5.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:24e61b88cb7e1b1d794b32a10cc346ac779681d6d74ff137a3e0a444d2bf1f02", size = 294831, upload-time = "2026-07-22T11:38:53.389Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/ea/de50a50fadf979713ab18b46f22ad5ff5f2dcfc637a3ebdecf669801e1a5/greenlet-3.5.4-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:870d730fec833f5a06906a32596cc099b9161594642a92a520b7a88911c95356", size = 614619, upload-time = "2026-07-22T12:26:42.282Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/c7/2aae27fea41205b8650294c301f042a2a4bb6155eea48c995b890a92f2c1/greenlet-3.5.4-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec5ff0d1878df6af3bf9b638a5a92a7d5693291de77c91bff10fa48519c604ef", size = 627021, upload-time = "2026-07-22T12:29:03.445Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/80/fb4d4788bbc8e54761f1fc88533af9523a6e86299fa113d6e8a8503ed9fc/greenlet-3.5.4-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07bd44616608d873d06735b63ef1a88191d6ca57c8d291d6559c71bc14c0893c", size = 632845, upload-time = "2026-07-22T12:43:45.19Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/56/79fd826f9ccaae0b84e1b4ef68dabba5e105bb044ffcd448a0b782fcba9a/greenlet-3.5.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d84d993f6e575c950d91a23c1345d18fe1a4310d447bf630849d7809196b52f0", size = 624002, upload-time = "2026-07-22T11:51:11.391Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/e3/6086fa578ebb72772722cdc4bcd628459814b42e0c2db1e3cbd6552b3271/greenlet-3.5.4-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:3529a8a933582ad19e224792cac7372489526576b75b4c124e8e4f29948f4861", size = 435053, upload-time = "2026-07-22T12:39:52.715Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/1a/27319f97e731298513dcba1a2e91b63e9d8811d9de22130f960b129b1bf1/greenlet-3.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:58023945f421093de5e6fa108c0985a8659d43f49e0216da25099369a121bcbd", size = 1581533, upload-time = "2026-07-22T12:25:05.322Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/6d/24240bf562e9786dd2799ee0a4a4dadb4ded22510f41b20245099159ac8c/greenlet-3.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bae2728e1897aa8df8cb1af38cd48b3a743aefe29372de7b8b7a9f532501e69f", size = 1645781, upload-time = "2026-07-22T11:51:14.805Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/5a/442ab1a9ef7ca6bf7210e5397a95972206a91a31033a03c8900866a10039/greenlet-3.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:ca5726c0b08ca35ae873557266a78b2c3f3b2b7d7401aa5ff886c2045dd0111c", size = 247133, upload-time = "2026-07-22T11:39:20.661Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/e6/9160210222386b1a378ff94db846b9508ca24a121cf684991561fdb69280/greenlet-3.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:7c1303791d603080cac6fc3b34df51c3b75b723739c282c8029e48a0d241672f", size = 245500, upload-time = "2026-07-22T11:40:22.185Z" }, + { url = "https://files.pythonhosted.org/packages/c0/9a/e51225dcd58713f16ccbdcc501a8da21098ea14515b7870f1f94459e5ff5/greenlet-3.5.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:24e61b88cb7e1b1d794b32a10cc346ac779681d6d74ff137a3e0a444d2bf1f02", size = 294831 }, + { url = "https://files.pythonhosted.org/packages/9f/ea/de50a50fadf979713ab18b46f22ad5ff5f2dcfc637a3ebdecf669801e1a5/greenlet-3.5.4-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:870d730fec833f5a06906a32596cc099b9161594642a92a520b7a88911c95356", size = 614619 }, + { url = "https://files.pythonhosted.org/packages/db/c7/2aae27fea41205b8650294c301f042a2a4bb6155eea48c995b890a92f2c1/greenlet-3.5.4-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec5ff0d1878df6af3bf9b638a5a92a7d5693291de77c91bff10fa48519c604ef", size = 627021 }, + { url = "https://files.pythonhosted.org/packages/1b/80/fb4d4788bbc8e54761f1fc88533af9523a6e86299fa113d6e8a8503ed9fc/greenlet-3.5.4-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07bd44616608d873d06735b63ef1a88191d6ca57c8d291d6559c71bc14c0893c", size = 632845 }, + { url = "https://files.pythonhosted.org/packages/eb/56/79fd826f9ccaae0b84e1b4ef68dabba5e105bb044ffcd448a0b782fcba9a/greenlet-3.5.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d84d993f6e575c950d91a23c1345d18fe1a4310d447bf630849d7809196b52f0", size = 624002 }, + { url = "https://files.pythonhosted.org/packages/42/e3/6086fa578ebb72772722cdc4bcd628459814b42e0c2db1e3cbd6552b3271/greenlet-3.5.4-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:3529a8a933582ad19e224792cac7372489526576b75b4c124e8e4f29948f4861", size = 435053 }, + { url = "https://files.pythonhosted.org/packages/0a/1a/27319f97e731298513dcba1a2e91b63e9d8811d9de22130f960b129b1bf1/greenlet-3.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:58023945f421093de5e6fa108c0985a8659d43f49e0216da25099369a121bcbd", size = 1581533 }, + { url = "https://files.pythonhosted.org/packages/b1/6d/24240bf562e9786dd2799ee0a4a4dadb4ded22510f41b20245099159ac8c/greenlet-3.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bae2728e1897aa8df8cb1af38cd48b3a743aefe29372de7b8b7a9f532501e69f", size = 1645781 }, + { url = "https://files.pythonhosted.org/packages/c1/5a/442ab1a9ef7ca6bf7210e5397a95972206a91a31033a03c8900866a10039/greenlet-3.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:ca5726c0b08ca35ae873557266a78b2c3f3b2b7d7401aa5ff886c2045dd0111c", size = 247133 }, + { url = "https://files.pythonhosted.org/packages/3e/e6/9160210222386b1a378ff94db846b9508ca24a121cf684991561fdb69280/greenlet-3.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:7c1303791d603080cac6fc3b34df51c3b75b723739c282c8029e48a0d241672f", size = 245500 }, ] [[package]] name = "grep-ast" version = "0.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pathspec" }, { name = "tree-sitter-language-pack" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/82/a87079945a7c15d242cb586ae22e17952132439eaa9c878ec5fbdc61c54d/grep_ast-0.9.0.tar.gz", hash = "sha256:620a242a4493e6721338d1c9a6c234ae651f8774f4924a6dcf90f6865d4b2ee3", size = 14125, upload-time = "2025-05-08T01:08:28.371Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/82/a87079945a7c15d242cb586ae22e17952132439eaa9c878ec5fbdc61c54d/grep_ast-0.9.0.tar.gz", hash = "sha256:620a242a4493e6721338d1c9a6c234ae651f8774f4924a6dcf90f6865d4b2ee3", size = 14125 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/79/29f1373b2ce1eec37c03aefbc17194c2470d8b61ede288e5043231825999/grep_ast-0.9.0-py3-none-any.whl", hash = "sha256:a3973dca99f1abc026a01bbbc70e00a63860c8ff94a56182ff18b089836826d7", size = 13918, upload-time = "2025-05-08T01:08:27.481Z" }, + { url = "https://files.pythonhosted.org/packages/40/79/29f1373b2ce1eec37c03aefbc17194c2470d8b61ede288e5043231825999/grep_ast-0.9.0-py3-none-any.whl", hash = "sha256:a3973dca99f1abc026a01bbbc70e00a63860c8ff94a56182ff18b089836826d7", size = 13918 }, ] [[package]] name = "griffelib" version = "2.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/e4/8d187ea29c2e30b3a09505c567513077d6117861bde1fbd997a167f262ec/griffelib-2.1.0.tar.gz", hash = "sha256:762a186d2c6fd6794d4ea20d428d597ffb857cb56b66421651cbba15bdd5e813", size = 216234, upload-time = "2026-06-19T12:05:42.278Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/e4/8d187ea29c2e30b3a09505c567513077d6117861bde1fbd997a167f262ec/griffelib-2.1.0.tar.gz", hash = "sha256:762a186d2c6fd6794d4ea20d428d597ffb857cb56b66421651cbba15bdd5e813", size = 216234 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/d3/5268aeabf2ad82658c4e2ff3a060648d0f02f3926cb53247c0e4d0dab49e/griffelib-2.1.0-py3-none-any.whl", hash = "sha256:cc7b3d2d2865ad0b909fcc38086e3f554b5ea7acbaa7bbb7ecaa3f5dfb7d9f00", size = 142560, upload-time = "2026-06-19T12:05:38.742Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d3/5268aeabf2ad82658c4e2ff3a060648d0f02f3926cb53247c0e4d0dab49e/griffelib-2.1.0-py3-none-any.whl", hash = "sha256:cc7b3d2d2865ad0b909fcc38086e3f554b5ea7acbaa7bbb7ecaa3f5dfb7d9f00", size = 142560 }, ] [[package]] name = "grimp" version = "3.15" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/73/ce58881177b003def779c87b5e10f396deef068933c97d6d206bd46d4cb7/grimp-3.15.tar.gz", hash = "sha256:91b57d4d801dc107ebfb5a7040d4777a152c579b5dc202426e1185e50931fe1e", size = 831734, upload-time = "2026-07-03T12:09:36.244Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/6a/3e0a0760cc509cd09764d128de78a1f329740306c74e42dba82c58a99118/grimp-3.15-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f19b957053d1c736aaa0015eed2d4855bb2511637c5360d32b3c5ea045904e7a", size = 2143197, upload-time = "2026-07-03T12:08:51.685Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/2e/127cbce04a5603d382c6a2bbc1a19b6889be49d60b88864bcdb174c8926d/grimp-3.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6480472c2d1f7f6a903906c92e9897d4e3dee5d69ce3881f04368d4ec6d2dde", size = 2088342, upload-time = "2026-07-03T12:08:44.227Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/36/af9df683bf6c8711e0e9136876ca130f9971102d945ff3a36d0c45dae2ec/grimp-3.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c14b64dbf2e4397df2e35fa8aa7028533621ecf1f8ea7ec24bc296a9c695ea4", size = 2254509, upload-time = "2026-07-03T12:07:35.809Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/f5/e712633b68ea14d04e7de84ede4f8ddbba763d5f2d29ae8d6af721f84870/grimp-3.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26364c2c9f7db88243365299b4d1c4d948b74304ff03c8a6e4f028147fed3b22", size = 2193831, upload-time = "2026-07-03T12:07:46.309Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/74/151bdd73d6a60bd77d4b956f36d03dd558dd46a9b5ec8f5ad15921b503d7/grimp-3.15-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69331e1be693415596c6084342059b9ba3ecf1cfe2e3b1c761598dd2fe14d522", size = 2345289, upload-time = "2026-07-03T12:08:19.458Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/b8/3fc950fa73b757cbc35f77542bd662f431b9a8f360e63196ded640771a33/grimp-3.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe397991c269868c2fb08114099b2aa4f1bc803d03fadaaf97e006019f9e5da2", size = 2604407, upload-time = "2026-07-03T12:07:57.217Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/d8/98916b9dc0b89a3e89e0d714ce0872be859fff40ceee3e2cb6886b106eb4/grimp-3.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d556bd62664ee044c47cff64d737be132408064d4ba68ca9f756cb29b41cc2d", size = 2326769, upload-time = "2026-07-03T12:08:08.773Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/51/39595c5857f609e976e0bba1f19c1f45182ee4d8d2ea5cdfab72841eafbc/grimp-3.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9061c5b6f01130ff8639c49c80176d29d921acc36741b2ae0b763a7668106082", size = 2272498, upload-time = "2026-07-03T12:08:32.03Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/d8/a44cc9db500ae80c45425238ee44d9556796aa88b8c0649cfa613fb88d14/grimp-3.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a9d04c195f0c6da4476361560d3d206a6a784b9eb0da7156fc6974511337e2e3", size = 2431075, upload-time = "2026-07-03T12:08:58.935Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/41/544f197ddb44990789683c25740ffa72474a57f0b16bc6b4a544edf9a2a9/grimp-3.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:01fd68c74cfd08b110bfd338d77efaa470670530f7179e6977764f44cd74d5cc", size = 2467361, upload-time = "2026-07-03T12:09:09.275Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/b1/8261018b9f1ab47fffbb7739d7af3f04c8b529555b7fb2ae8b742d42d3db/grimp-3.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bd1ddd428a124d6730bed49ea60fb2ca6c8e0640c8f5abe1d0f6fc27a92fd8bc", size = 2503500, upload-time = "2026-07-03T12:09:19.585Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/d6/99a2421c4c0de9f203a7e246030b13b676766e62e48504883863942646fb/grimp-3.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07e645e9d45ed43bb8ea8da5982c19eacf13ee685d8440e3dc280064c005599c", size = 2513834, upload-time = "2026-07-03T12:09:29.758Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/a5/263729e23d64541cd99d36dbb592e29c1ecea803deb3bb5c0c463b43c2ec/grimp-3.15-cp313-cp313-win32.whl", hash = "sha256:473646e0a74a554b4ab071d7fcbf5f442eb8cf87561770dae269818636b8edf2", size = 1855743, upload-time = "2026-07-03T12:09:49.789Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/8c/a072bbea2e2e94da38f90bd8794037c90fb4eba389b49d01cdd2bb85e13c/grimp-3.15-cp313-cp313-win_amd64.whl", hash = "sha256:dbc2c15a1fbca2ff358f86cc90067176096dd73bec27d002515521b3125ba507", size = 1984546, upload-time = "2026-07-03T12:09:42.543Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/73/ce58881177b003def779c87b5e10f396deef068933c97d6d206bd46d4cb7/grimp-3.15.tar.gz", hash = "sha256:91b57d4d801dc107ebfb5a7040d4777a152c579b5dc202426e1185e50931fe1e", size = 831734 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/6a/3e0a0760cc509cd09764d128de78a1f329740306c74e42dba82c58a99118/grimp-3.15-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f19b957053d1c736aaa0015eed2d4855bb2511637c5360d32b3c5ea045904e7a", size = 2143197 }, + { url = "https://files.pythonhosted.org/packages/c8/2e/127cbce04a5603d382c6a2bbc1a19b6889be49d60b88864bcdb174c8926d/grimp-3.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6480472c2d1f7f6a903906c92e9897d4e3dee5d69ce3881f04368d4ec6d2dde", size = 2088342 }, + { url = "https://files.pythonhosted.org/packages/b0/36/af9df683bf6c8711e0e9136876ca130f9971102d945ff3a36d0c45dae2ec/grimp-3.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c14b64dbf2e4397df2e35fa8aa7028533621ecf1f8ea7ec24bc296a9c695ea4", size = 2254509 }, + { url = "https://files.pythonhosted.org/packages/02/f5/e712633b68ea14d04e7de84ede4f8ddbba763d5f2d29ae8d6af721f84870/grimp-3.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26364c2c9f7db88243365299b4d1c4d948b74304ff03c8a6e4f028147fed3b22", size = 2193831 }, + { url = "https://files.pythonhosted.org/packages/13/74/151bdd73d6a60bd77d4b956f36d03dd558dd46a9b5ec8f5ad15921b503d7/grimp-3.15-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69331e1be693415596c6084342059b9ba3ecf1cfe2e3b1c761598dd2fe14d522", size = 2345289 }, + { url = "https://files.pythonhosted.org/packages/a1/b8/3fc950fa73b757cbc35f77542bd662f431b9a8f360e63196ded640771a33/grimp-3.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe397991c269868c2fb08114099b2aa4f1bc803d03fadaaf97e006019f9e5da2", size = 2604407 }, + { url = "https://files.pythonhosted.org/packages/0a/d8/98916b9dc0b89a3e89e0d714ce0872be859fff40ceee3e2cb6886b106eb4/grimp-3.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d556bd62664ee044c47cff64d737be132408064d4ba68ca9f756cb29b41cc2d", size = 2326769 }, + { url = "https://files.pythonhosted.org/packages/63/51/39595c5857f609e976e0bba1f19c1f45182ee4d8d2ea5cdfab72841eafbc/grimp-3.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9061c5b6f01130ff8639c49c80176d29d921acc36741b2ae0b763a7668106082", size = 2272498 }, + { url = "https://files.pythonhosted.org/packages/7d/d8/a44cc9db500ae80c45425238ee44d9556796aa88b8c0649cfa613fb88d14/grimp-3.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a9d04c195f0c6da4476361560d3d206a6a784b9eb0da7156fc6974511337e2e3", size = 2431075 }, + { url = "https://files.pythonhosted.org/packages/a9/41/544f197ddb44990789683c25740ffa72474a57f0b16bc6b4a544edf9a2a9/grimp-3.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:01fd68c74cfd08b110bfd338d77efaa470670530f7179e6977764f44cd74d5cc", size = 2467361 }, + { url = "https://files.pythonhosted.org/packages/3b/b1/8261018b9f1ab47fffbb7739d7af3f04c8b529555b7fb2ae8b742d42d3db/grimp-3.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bd1ddd428a124d6730bed49ea60fb2ca6c8e0640c8f5abe1d0f6fc27a92fd8bc", size = 2503500 }, + { url = "https://files.pythonhosted.org/packages/db/d6/99a2421c4c0de9f203a7e246030b13b676766e62e48504883863942646fb/grimp-3.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07e645e9d45ed43bb8ea8da5982c19eacf13ee685d8440e3dc280064c005599c", size = 2513834 }, + { url = "https://files.pythonhosted.org/packages/62/a5/263729e23d64541cd99d36dbb592e29c1ecea803deb3bb5c0c463b43c2ec/grimp-3.15-cp313-cp313-win32.whl", hash = "sha256:473646e0a74a554b4ab071d7fcbf5f442eb8cf87561770dae269818636b8edf2", size = 1855743 }, + { url = "https://files.pythonhosted.org/packages/1c/8c/a072bbea2e2e94da38f90bd8794037c90fb4eba389b49d01cdd2bb85e13c/grimp-3.15-cp313-cp313-win_amd64.whl", hash = "sha256:dbc2c15a1fbca2ff358f86cc90067176096dd73bec27d002515521b3125ba507", size = 1984546 }, ] [[package]] name = "h11" version = "0.16.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] [[package]] name = "hf-xet" version = "1.5.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/39/67be8d71f900d9a55761b6022821d6679fb56c64f1b6063d5af2c2606727/hf_xet-1.5.2.tar.gz", hash = "sha256:73044bd31bae33c984af832d19c752a0dffb67518fee9ddbd91d616e1101cf47", size = 903674, upload-time = "2026-07-16T17:29:56.833Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/39/67be8d71f900d9a55761b6022821d6679fb56c64f1b6063d5af2c2606727/hf_xet-1.5.2.tar.gz", hash = "sha256:73044bd31bae33c984af832d19c752a0dffb67518fee9ddbd91d616e1101cf47", size = 903674 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/ba/2b70603c7552db82baeb2623e2336898304a17328845151be4fe1f48d420/hf_xet-1.5.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f922b8f5fb84f1dd3d7ab7a1316354a1bca9b1c73ecfc19c76e51a2a49d29799", size = 4033760, upload-time = "2026-07-16T17:29:43.884Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/ac/b097a86a1e4a6098f3a79382643ab09d5733d87ccc864877ad1e12b49b70/hf_xet-1.5.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:045f84440c55cdeb659cf1a1dd48c77bcd0d2e93632e2fea8f2c3bdee79f38ed", size = 3841438, upload-time = "2026-07-16T17:29:45.539Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/35/db860aa3a0780660324a506ad4b3d322ddc6ecbba4b9340aed0942cbf21c/hf_xet-1.5.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db78c39c83d6279daddc98e2238f373ab8980685556d42472b4ec51abcf03e8c", size = 4428006, upload-time = "2026-07-16T17:29:46.996Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/6b/832dd980af4b0c3ae0660e309285f2ffcdff2faa38129390dbb47aa4a3f9/hf_xet-1.5.2-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7db73c810500c54c6760be8c39d4b2e476974de85424c50063efc22fdda13025", size = 4221099, upload-time = "2026-07-16T17:29:48.525Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/05/ae50f0d34e3254e6c3e208beb2519f6b8673016fc4b3643badaf6450d186/hf_xet-1.5.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6395cfe3c9cbead4f16b31808b0e67eac428b66c656f856e99636adaddea878f", size = 4420766, upload-time = "2026-07-16T17:29:50.092Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/a9/c050bc2743a2bcd68928bfee157b08681667a164a24ec95fbfcfcd717e08/hf_xet-1.5.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cde8cd167126bb6109b2ceb19b844433a4988643e8f3e01dd9dd0e4a34535097", size = 4636716, upload-time = "2026-07-16T17:29:51.62Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/f8/68b01c5c2edb56ac9a67b3d076ffddcb90867abaee923923eb34e7a14e76/hf_xet-1.5.2-cp38-abi3-win_amd64.whl", hash = "sha256:ecf63d1cb69a9a7319910f8f83fcf9b46e7a32dfcf4b8f8eeddb55f647306e65", size = 3988373, upload-time = "2026-07-16T17:29:53.395Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/c6/988383e9dc17294d536fcbcd6fd16eed882e411ad16c954984a53e47b09c/hf_xet-1.5.2-cp38-abi3-win_arm64.whl", hash = "sha256:1da28519496eb7c8094c11e4d25509b4a468457a0302d58136099db2fd9a671d", size = 3816957, upload-time = "2026-07-16T17:29:54.991Z" }, + { url = "https://files.pythonhosted.org/packages/de/ba/2b70603c7552db82baeb2623e2336898304a17328845151be4fe1f48d420/hf_xet-1.5.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f922b8f5fb84f1dd3d7ab7a1316354a1bca9b1c73ecfc19c76e51a2a49d29799", size = 4033760 }, + { url = "https://files.pythonhosted.org/packages/60/ac/b097a86a1e4a6098f3a79382643ab09d5733d87ccc864877ad1e12b49b70/hf_xet-1.5.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:045f84440c55cdeb659cf1a1dd48c77bcd0d2e93632e2fea8f2c3bdee79f38ed", size = 3841438 }, + { url = "https://files.pythonhosted.org/packages/d3/35/db860aa3a0780660324a506ad4b3d322ddc6ecbba4b9340aed0942cbf21c/hf_xet-1.5.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db78c39c83d6279daddc98e2238f373ab8980685556d42472b4ec51abcf03e8c", size = 4428006 }, + { url = "https://files.pythonhosted.org/packages/af/6b/832dd980af4b0c3ae0660e309285f2ffcdff2faa38129390dbb47aa4a3f9/hf_xet-1.5.2-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7db73c810500c54c6760be8c39d4b2e476974de85424c50063efc22fdda13025", size = 4221099 }, + { url = "https://files.pythonhosted.org/packages/9e/05/ae50f0d34e3254e6c3e208beb2519f6b8673016fc4b3643badaf6450d186/hf_xet-1.5.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6395cfe3c9cbead4f16b31808b0e67eac428b66c656f856e99636adaddea878f", size = 4420766 }, + { url = "https://files.pythonhosted.org/packages/07/a9/c050bc2743a2bcd68928bfee157b08681667a164a24ec95fbfcfcd717e08/hf_xet-1.5.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cde8cd167126bb6109b2ceb19b844433a4988643e8f3e01dd9dd0e4a34535097", size = 4636716 }, + { url = "https://files.pythonhosted.org/packages/e9/f8/68b01c5c2edb56ac9a67b3d076ffddcb90867abaee923923eb34e7a14e76/hf_xet-1.5.2-cp38-abi3-win_amd64.whl", hash = "sha256:ecf63d1cb69a9a7319910f8f83fcf9b46e7a32dfcf4b8f8eeddb55f647306e65", size = 3988373 }, + { url = "https://files.pythonhosted.org/packages/39/c6/988383e9dc17294d536fcbcd6fd16eed882e411ad16c954984a53e47b09c/hf_xet-1.5.2-cp38-abi3-win_arm64.whl", hash = "sha256:1da28519496eb7c8094c11e4d25509b4a468457a0302d58136099db2fd9a671d", size = 3816957 }, ] [[package]] name = "hishel" version = "1.3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "msgpack" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/6c/6fda06f82ec7adc959ec49d80a87e26d31ddce2a9a4c8582a8721c9ac8ec/hishel-1.3.0.tar.gz", hash = "sha256:5be585dca8c035866cffc745a50dbce6b277a3d1f4b07ef24f77c07f6b31ff92", size = 813532, upload-time = "2026-06-11T08:41:29.699Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/6c/6fda06f82ec7adc959ec49d80a87e26d31ddce2a9a4c8582a8721c9ac8ec/hishel-1.3.0.tar.gz", hash = "sha256:5be585dca8c035866cffc745a50dbce6b277a3d1f4b07ef24f77c07f6b31ff92", size = 813532 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/fd/8d2ed31f94fbf6fd15923afdb40d0e4ed26286ecaa70c256033b240ca5b1/hishel-1.3.0-py3-none-any.whl", hash = "sha256:b803ad9f1d410085b61459a5dbbd273806215021e672198ba911c7d3db14adaa", size = 73626, upload-time = "2026-06-11T08:41:28.079Z" }, + { url = "https://files.pythonhosted.org/packages/f5/fd/8d2ed31f94fbf6fd15923afdb40d0e4ed26286ecaa70c256033b240ca5b1/hishel-1.3.0-py3-none-any.whl", hash = "sha256:b803ad9f1d410085b61459a5dbbd273806215021e672198ba911c7d3db14adaa", size = 73626 }, ] [package.optional-dependencies] @@ -1918,57 +1688,57 @@ async = [ [[package]] name = "httpcore" version = "1.0.9" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, ] [[package]] name = "httpcore2" version = "2.9.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "h11" }, { name = "truststore" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/a8/20ed1ed79cbc2ecdf5301c0968ab7c85547212e2a7bd126ddd2d986e206e/httpcore2-2.9.1.tar.gz", hash = "sha256:4d8acbf8b306f48c9d6046591fd5ba4037d1b1b1000d140fc2c3eab1e9a0c0e2", size = 67089, upload-time = "2026-07-24T09:21:03.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/a8/20ed1ed79cbc2ecdf5301c0968ab7c85547212e2a7bd126ddd2d986e206e/httpcore2-2.9.1.tar.gz", hash = "sha256:4d8acbf8b306f48c9d6046591fd5ba4037d1b1b1000d140fc2c3eab1e9a0c0e2", size = 67089 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/fb/46c52b781975c335a2bcf1072c7bbc007cbdc8d674217f5ee1daba2c848b/httpcore2-2.9.1-py3-none-any.whl", hash = "sha256:6182472379e855fe4221246a2bb7ecede403bc61c6798062ae1787d051ccde26", size = 82809, upload-time = "2026-07-24T09:21:01.178Z" }, + { url = "https://files.pythonhosted.org/packages/9f/fb/46c52b781975c335a2bcf1072c7bbc007cbdc8d674217f5ee1daba2c848b/httpcore2-2.9.1-py3-none-any.whl", hash = "sha256:6182472379e855fe4221246a2bb7ecede403bc61c6798062ae1787d051ccde26", size = 82809 }, ] [[package]] name = "httptools" version = "0.8.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148, upload-time = "2026-05-25T22:17:16.333Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368, upload-time = "2026-05-25T22:17:17.586Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447, upload-time = "2026-05-25T22:17:18.564Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448, upload-time = "2026-05-25T22:17:19.912Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460, upload-time = "2026-05-25T22:17:20.882Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312, upload-time = "2026-05-25T22:17:22.085Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117, upload-time = "2026-05-25T22:17:23.074Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148 }, + { url = "https://files.pythonhosted.org/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368 }, + { url = "https://files.pythonhosted.org/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447 }, + { url = "https://files.pythonhosted.org/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448 }, + { url = "https://files.pythonhosted.org/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460 }, + { url = "https://files.pythonhosted.org/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312 }, + { url = "https://files.pythonhosted.org/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117 }, ] [[package]] name = "httpx" version = "0.28.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "certifi" }, { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] [package.optional-dependencies] @@ -1979,31 +1749,31 @@ socks = [ [[package]] name = "httpx-sse" version = "0.4.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960 }, ] [[package]] name = "httpx2" version = "2.9.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "httpcore2" }, { name = "idna" }, { name = "truststore" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/14/38128fbafd7e0ed41d874df6c9a653d47c2d111cfe59e2b4ac95161b4abd/httpx2-2.9.1.tar.gz", hash = "sha256:1932a768737e3666291582833da748cc4e563c337cf96706fccc04fa6e58764a", size = 95458, upload-time = "2026-07-24T09:21:04.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/14/38128fbafd7e0ed41d874df6c9a653d47c2d111cfe59e2b4ac95161b4abd/httpx2-2.9.1.tar.gz", hash = "sha256:1932a768737e3666291582833da748cc4e563c337cf96706fccc04fa6e58764a", size = 95458 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/b8/cfd91c4ab9134d386d48f0b6ac662ff3d4be6efdee59ee1c67ebc3c0487c/httpx2-2.9.1-py3-none-any.whl", hash = "sha256:1820fe14a9ab1107bfeff39259987429450b070ec0ff38cc87eb0d8c97fdc71a", size = 91191, upload-time = "2026-07-24T09:21:02.6Z" }, + { url = "https://files.pythonhosted.org/packages/13/b8/cfd91c4ab9134d386d48f0b6ac662ff3d4be6efdee59ee1c67ebc3c0487c/httpx2-2.9.1-py3-none-any.whl", hash = "sha256:1820fe14a9ab1107bfeff39259987429450b070ec0ff38cc87eb0d8c97fdc71a", size = 91191 }, ] [[package]] name = "huggingface-hub" version = "1.24.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "filelock" }, @@ -2015,73 +1785,73 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/9b/d3bb4e7d792835daf34dd7091bbc7d7b4e0437d9388f1ea7239cce49f478/huggingface_hub-1.24.0.tar.gz", hash = "sha256:18431ff4daae0749aa9ba102fc952e314c98e1d30ebdec5319d85ca0a83e1ae5", size = 921848, upload-time = "2026-07-17T09:54:01.022Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/9b/d3bb4e7d792835daf34dd7091bbc7d7b4e0437d9388f1ea7239cce49f478/huggingface_hub-1.24.0.tar.gz", hash = "sha256:18431ff4daae0749aa9ba102fc952e314c98e1d30ebdec5319d85ca0a83e1ae5", size = 921848 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/c3/aeaaf3911d2529614be18d1c8b5496afc185560e76568063d517283318af/huggingface_hub-1.24.0-py3-none-any.whl", hash = "sha256:6ed4120a84a6beec900640aa7e346bd766a6b7341e41526fef5dc8bd81fb7d59", size = 771904, upload-time = "2026-07-17T09:53:59.106Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c3/aeaaf3911d2529614be18d1c8b5496afc185560e76568063d517283318af/huggingface_hub-1.24.0-py3-none-any.whl", hash = "sha256:6ed4120a84a6beec900640aa7e346bd766a6b7341e41526fef5dc8bd81fb7d59", size = 771904 }, ] [[package]] name = "idna" version = "3.18" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455 }, ] [[package]] name = "import-linter" version = "2.13" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "grimp" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/c6/42962eb043df4d6984c1540220735b20442572fe37dab5e65ac807c939b9/import_linter-2.13.tar.gz", hash = "sha256:13af4a1d6b06044c58ea784e8732fd7fe48eec821a75feb4d6a1a2de36dd5c27", size = 1279761, upload-time = "2026-07-03T14:00:31.285Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/c6/42962eb043df4d6984c1540220735b20442572fe37dab5e65ac807c939b9/import_linter-2.13.tar.gz", hash = "sha256:13af4a1d6b06044c58ea784e8732fd7fe48eec821a75feb4d6a1a2de36dd5c27", size = 1279761 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/13/e7725e6eb32607fd4af51ccf3edfe835826e5cdf783b4d7fdc8f459196ae/import_linter-2.13-py3-none-any.whl", hash = "sha256:c0372e7ee5e15657bc06a8e841445e13237afd738a672d26863dc927af9f0bf5", size = 638185, upload-time = "2026-07-03T14:00:29.676Z" }, + { url = "https://files.pythonhosted.org/packages/88/13/e7725e6eb32607fd4af51ccf3edfe835826e5cdf783b4d7fdc8f459196ae/import_linter-2.13-py3-none-any.whl", hash = "sha256:c0372e7ee5e15657bc06a8e841445e13237afd738a672d26863dc927af9f0bf5", size = 638185 }, ] [[package]] name = "importlib-metadata" version = "8.7.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, ] [[package]] name = "inflect" version = "7.5.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, { name = "typeguard" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751, upload-time = "2024-12-28T17:11:18.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197, upload-time = "2024-12-28T17:11:15.931Z" }, + { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197 }, ] [[package]] name = "iniconfig" version = "2.3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, ] [[package]] name = "inline-snapshot" version = "0.35.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asttokens" }, { name = "executing" }, @@ -2089,87 +1859,87 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/b3/38a1b9c6323c5dff0ab6c74aa839f1c7d8b7057a861d80ec5bfe834be24e/inline_snapshot-0.35.2.tar.gz", hash = "sha256:6cfd2ea0b52d9cb9beb13c1e73d740ff86f630ba48d9ea6947e54e3ff8494876", size = 2534713, upload-time = "2026-07-16T10:51:58.826Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/b3/38a1b9c6323c5dff0ab6c74aa839f1c7d8b7057a861d80ec5bfe834be24e/inline_snapshot-0.35.2.tar.gz", hash = "sha256:6cfd2ea0b52d9cb9beb13c1e73d740ff86f630ba48d9ea6947e54e3ff8494876", size = 2534713 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/ea/de85fc264e4c76bda3552eee42904e8b7079fd826902aa5fa044bc5bbc3c/inline_snapshot-0.35.2-py3-none-any.whl", hash = "sha256:91b230b310c95a8c3b91cdbc637e064c7c2f98c13935b64875a7ba9ef174e161", size = 94669, upload-time = "2026-07-16T10:51:57.457Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ea/de85fc264e4c76bda3552eee42904e8b7079fd826902aa5fa044bc5bbc3c/inline_snapshot-0.35.2-py3-none-any.whl", hash = "sha256:91b230b310c95a8c3b91cdbc637e064c7c2f98c13935b64875a7ba9ef174e161", size = 94669 }, ] [[package]] name = "installer" version = "1.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/fe/b9f481cf0cc867958a21338baa900357b7b7d86cac9b025948049d77923c/installer-1.0.1.tar.gz", hash = "sha256:052c7fc3721d54c696e2dea019be67539d7b144e924f559f54beb3121831c364", size = 481132, upload-time = "2026-05-11T18:13:28.158Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/fe/b9f481cf0cc867958a21338baa900357b7b7d86cac9b025948049d77923c/installer-1.0.1.tar.gz", hash = "sha256:052c7fc3721d54c696e2dea019be67539d7b144e924f559f54beb3121831c364", size = 481132 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/48/bedf3ba4163e7db392c9d4cbdfc8217423196c8fccbe5a404a0f094fde63/installer-1.0.1-py3-none-any.whl", hash = "sha256:011d045df8b954ced7dde3a7e42ae4418da40ecda7990f2d11d5ed7c146fd98b", size = 464455, upload-time = "2026-05-11T18:13:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/26/48/bedf3ba4163e7db392c9d4cbdfc8217423196c8fccbe5a404a0f094fde63/installer-1.0.1-py3-none-any.whl", hash = "sha256:011d045df8b954ced7dde3a7e42ae4418da40ecda7990f2d11d5ed7c146fd98b", size = 464455 }, ] [[package]] name = "isort" version = "8.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733 }, ] [[package]] name = "jaraco-classes" version = "3.4.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777 }, ] [[package]] name = "jaraco-context" version = "6.1.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" }, + { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871 }, ] [[package]] name = "jaraco-functools" version = "4.6.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/1f/c23395957d41ccf27c4e535c3d334c4051e5395b3752057ba4cbaec35c56/jaraco_functools-4.6.0.tar.gz", hash = "sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280", size = 20837, upload-time = "2026-07-14T01:28:02.544Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/1f/c23395957d41ccf27c4e535c3d334c4051e5395b3752057ba4cbaec35c56/jaraco_functools-4.6.0.tar.gz", hash = "sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280", size = 20837 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl", hash = "sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30", size = 11677, upload-time = "2026-07-14T01:28:01.59Z" }, + { url = "https://files.pythonhosted.org/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl", hash = "sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30", size = 11677 }, ] [[package]] name = "jeepney" version = "0.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010 }, ] [[package]] name = "jinja2" version = "3.1.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] name = "jinjarope" version = "1.0.7" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv" }, { name = "epregistry" }, @@ -2178,9 +1948,9 @@ dependencies = [ { name = "universal-pathlib" }, { name = "upathtools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/14/6caaf04cefcf97fff8db5de10e5fc1f6f7af7f20acdb441f089a5dbe130c/jinjarope-1.0.7.tar.gz", hash = "sha256:876db5c9b087f8275f9a6ae2686ae12d57999d320500b8ddcbc9ecbf26e3c42b", size = 2686298, upload-time = "2026-01-08T20:36:05.787Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/14/6caaf04cefcf97fff8db5de10e5fc1f6f7af7f20acdb441f089a5dbe130c/jinjarope-1.0.7.tar.gz", hash = "sha256:876db5c9b087f8275f9a6ae2686ae12d57999d320500b8ddcbc9ecbf26e3c42b", size = 2686298 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/da/065560563ffa16799e488f1fddfca96834a905b47a3201a0071b33fac43f/jinjarope-1.0.7-py3-none-any.whl", hash = "sha256:c6a46558281f74fa2bde31bce368663100962ec0fa2bf68c9faf430030dea298", size = 2699207, upload-time = "2026-01-08T20:36:04.156Z" }, + { url = "https://files.pythonhosted.org/packages/17/da/065560563ffa16799e488f1fddfca96834a905b47a3201a0071b33fac43f/jinjarope-1.0.7-py3-none-any.whl", hash = "sha256:c6a46558281f74fa2bde31bce368663100962ec0fa2bf68c9faf430030dea298", size = 2699207 }, ] [package.optional-dependencies] @@ -2191,41 +1961,41 @@ icons = [ [[package]] name = "jiter" version = "0.16.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431, upload-time = "2026-06-29T13:05:13.657Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/c0/555fc60473d30d66894ba825e63615e3be7524fac23858356afa7a38906c/jiter-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:41977aa5654023948c2dae2a81cbf9c43343954bef1cd59a154dd15a4d84c195", size = 306203, upload-time = "2026-06-29T13:03:36.243Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/2b/c3eaf16f5d7c9bad66ea32f40a95bd169b29a91217fcc7f081375157e99c/jiter-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d28bb3c26762358dadf3e5bf0bccd29ae987d65e6988d2e6f49829c76b003c09", size = 306489, upload-time = "2026-06-29T13:03:37.846Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/3f/02fdfc6705cad96127d883af5c34e4867f554f29ec7705ec1a46156400a9/jiter-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0542a7189c26920778658fc8fcf2af8bae05bae9924577f71804acef37996536", size = 335453, upload-time = "2026-06-29T13:03:39.221Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/a6/e4bda5920d4b0d7c5dfb7174ce4a6b2e4d3e11c9162c452ef0eab4cdbdbd/jiter-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fb8de1e23a0cb2a7f53c335049c7b72b6db41aa6227cdcc0972a1de5cb39450", size = 361625, upload-time = "2026-06-29T13:03:40.597Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/97/4e6b59b2c6e55cbb3e183595f81ad65dcfb21c915fee5e19e335df21bc55/jiter-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72d0b2990ca754a9102779ac98d8597b7cb31678958562214a007f909eab78e", size = 456958, upload-time = "2026-06-29T13:03:42.074Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/e0/97e9557686d2f94f4b93786eccb7eed28e9228ad132ea8237f44727314a7/jiter-0.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f91b1c27fc22a57993d5a5cb8a627cb8ed4b10502716fac1ffbfe1d19d84e8", size = 372017, upload-time = "2026-06-29T13:03:43.658Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/94/db768b6938e0df35c86beeba3dfbbb025c9ee5c19e1aa271f2396e50864d/jiter-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c682bea068a90b764577bdb78a60a4c1d1606daf9cd4c893832a37c7cc9d9026", size = 343320, upload-time = "2026-06-29T13:03:45.226Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/d6/5a59d938244a30735fe62d9433fd325f9021ea29d89780ea4596ea93bc89/jiter-0.16.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8d031aabecc4f1b6276adfb42e3aabb77c89d468bf616600e8d3a11328929053", size = 350520, upload-time = "2026-06-29T13:03:46.671Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/f8/c4a857f49c9af125f6bbcac7e3eee7f7978ed89682833062e2dbf62576b1/jiter-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eab2cd170150e70153de16896a1774e3a1dca80154c56b54d7a812c479a7165e", size = 387550, upload-time = "2026-06-29T13:03:48.361Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/d6/5fbc2f7d6b67b754caa61a993a2e626e815dec47ffc2f9e35f01adfebec7/jiter-0.16.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6edb63a46e65a82c26800a868e49b2cac30dd5a4218b88d74bc2c848c8ad60bb", size = 515424, upload-time = "2026-06-29T13:03:49.881Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/54/284f0164b64a5fed915fea6ba7e9ba9b3d8d37c67d59cf2e3bb99d45cdfe/jiter-0.16.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:659039cc50b5addcc35fcc87ae2c1833b7c0a8e5326ef631a75e4478447bcf84", size = 546981, upload-time = "2026-06-29T13:03:51.363Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/c5/2a467585a576594384e1d2c43e1224deaafc085f24e243529cf98beef8e1/jiter-0.16.0-cp313-cp313-win32.whl", hash = "sha256:c9c53be232c2e206ef9cdbad81a48bfa74c3d3f08bcf8124630a8a748aad993e", size = 202853, upload-time = "2026-06-29T13:03:53.015Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/6a/de61d04b9eec69c71719968d2f716532a3bc121170c44a39e14979c6be81/jiter-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:baad945ed47f163ad833314f8e3288c396118934f94e7bbb9e243ce4b341a4fd", size = 196160, upload-time = "2026-06-29T13:03:54.447Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/4b/b390ed59bafb3f31d008d1218578f10327714484b334439947f7e5b11e7f/jiter-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:3c1fd2dbe1b0af19e987f03fe66c5f5bd105a2229c1aff4ab14890b24f41d21a", size = 189862, upload-time = "2026-06-29T13:03:55.754Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/c0/555fc60473d30d66894ba825e63615e3be7524fac23858356afa7a38906c/jiter-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:41977aa5654023948c2dae2a81cbf9c43343954bef1cd59a154dd15a4d84c195", size = 306203 }, + { url = "https://files.pythonhosted.org/packages/d0/2b/c3eaf16f5d7c9bad66ea32f40a95bd169b29a91217fcc7f081375157e99c/jiter-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d28bb3c26762358dadf3e5bf0bccd29ae987d65e6988d2e6f49829c76b003c09", size = 306489 }, + { url = "https://files.pythonhosted.org/packages/96/3f/02fdfc6705cad96127d883af5c34e4867f554f29ec7705ec1a46156400a9/jiter-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0542a7189c26920778658fc8fcf2af8bae05bae9924577f71804acef37996536", size = 335453 }, + { url = "https://files.pythonhosted.org/packages/b2/a6/e4bda5920d4b0d7c5dfb7174ce4a6b2e4d3e11c9162c452ef0eab4cdbdbd/jiter-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fb8de1e23a0cb2a7f53c335049c7b72b6db41aa6227cdcc0972a1de5cb39450", size = 361625 }, + { url = "https://files.pythonhosted.org/packages/b7/97/4e6b59b2c6e55cbb3e183595f81ad65dcfb21c915fee5e19e335df21bc55/jiter-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72d0b2990ca754a9102779ac98d8597b7cb31678958562214a007f909eab78e", size = 456958 }, + { url = "https://files.pythonhosted.org/packages/15/e0/97e9557686d2f94f4b93786eccb7eed28e9228ad132ea8237f44727314a7/jiter-0.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f91b1c27fc22a57993d5a5cb8a627cb8ed4b10502716fac1ffbfe1d19d84e8", size = 372017 }, + { url = "https://files.pythonhosted.org/packages/0f/94/db768b6938e0df35c86beeba3dfbbb025c9ee5c19e1aa271f2396e50864d/jiter-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c682bea068a90b764577bdb78a60a4c1d1606daf9cd4c893832a37c7cc9d9026", size = 343320 }, + { url = "https://files.pythonhosted.org/packages/c1/d6/5a59d938244a30735fe62d9433fd325f9021ea29d89780ea4596ea93bc89/jiter-0.16.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8d031aabecc4f1b6276adfb42e3aabb77c89d468bf616600e8d3a11328929053", size = 350520 }, + { url = "https://files.pythonhosted.org/packages/67/f8/c4a857f49c9af125f6bbcac7e3eee7f7978ed89682833062e2dbf62576b1/jiter-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eab2cd170150e70153de16896a1774e3a1dca80154c56b54d7a812c479a7165e", size = 387550 }, + { url = "https://files.pythonhosted.org/packages/8b/d6/5fbc2f7d6b67b754caa61a993a2e626e815dec47ffc2f9e35f01adfebec7/jiter-0.16.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6edb63a46e65a82c26800a868e49b2cac30dd5a4218b88d74bc2c848c8ad60bb", size = 515424 }, + { url = "https://files.pythonhosted.org/packages/ed/54/284f0164b64a5fed915fea6ba7e9ba9b3d8d37c67d59cf2e3bb99d45cdfe/jiter-0.16.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:659039cc50b5addcc35fcc87ae2c1833b7c0a8e5326ef631a75e4478447bcf84", size = 546981 }, + { url = "https://files.pythonhosted.org/packages/13/c5/2a467585a576594384e1d2c43e1224deaafc085f24e243529cf98beef8e1/jiter-0.16.0-cp313-cp313-win32.whl", hash = "sha256:c9c53be232c2e206ef9cdbad81a48bfa74c3d3f08bcf8124630a8a748aad993e", size = 202853 }, + { url = "https://files.pythonhosted.org/packages/88/6a/de61d04b9eec69c71719968d2f716532a3bc121170c44a39e14979c6be81/jiter-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:baad945ed47f163ad833314f8e3288c396118934f94e7bbb9e243ce4b341a4fd", size = 196160 }, + { url = "https://files.pythonhosted.org/packages/19/4b/b390ed59bafb3f31d008d1218578f10327714484b334439947f7e5b11e7f/jiter-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:3c1fd2dbe1b0af19e987f03fe66c5f5bd105a2229c1aff4ab14890b24f41d21a", size = 189862 }, ] [[package]] name = "joserfc" version = "1.7.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/e0/27a6a081ae25420eda6768ceae05d7022a7f2447f420588843f2a44e4298/joserfc-1.7.4.tar.gz", hash = "sha256:b3bc561672ae541b17a9237053b48a03dacddd92d68047b3ecdfb4b5714a88ed", size = 234027, upload-time = "2026-07-19T15:43:02.739Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/e0/27a6a081ae25420eda6768ceae05d7022a7f2447f420588843f2a44e4298/joserfc-1.7.4.tar.gz", hash = "sha256:b3bc561672ae541b17a9237053b48a03dacddd92d68047b3ecdfb4b5714a88ed", size = 234027 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/bf/249dcd99b3376375910b7fa922383b57792975c8758f50d44612e749226c/joserfc-1.7.4-py3-none-any.whl", hash = "sha256:32d46c2cd5e3203c13e87a6c61333cab310b1ba80cd54b4c4f386a848a122463", size = 71000, upload-time = "2026-07-19T15:43:01.299Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/249dcd99b3376375910b7fa922383b57792975c8758f50d44612e749226c/joserfc-1.7.4-py3-none-any.whl", hash = "sha256:32d46c2cd5e3203c13e87a6c61333cab310b1ba80cd54b4c4f386a848a122463", size = 71000 }, ] [[package]] name = "json-schema-for-humans" version = "1.5.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "dataclasses-json" }, @@ -2236,87 +2006,87 @@ dependencies = [ { name = "pyyaml" }, { name = "requests" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/4c/83fc5c339cabfc0d14717c843f506ad2e70a197b6f2c139bb7b2a27e368b/json_schema_for_humans-1.5.1.tar.gz", hash = "sha256:a43023f6c1b99f8e75126cc824c22992d8f55e941060aa3fddfc7c38de3cb973", size = 256753, upload-time = "2025-11-21T14:55:54.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/4c/83fc5c339cabfc0d14717c843f506ad2e70a197b6f2c139bb7b2a27e368b/json_schema_for_humans-1.5.1.tar.gz", hash = "sha256:a43023f6c1b99f8e75126cc824c22992d8f55e941060aa3fddfc7c38de3cb973", size = 256753 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/97/1e9a730a7d7674d6022e9eaabff9218ba096b310cef712c004854248a64b/json_schema_for_humans-1.5.1-py3-none-any.whl", hash = "sha256:6b5bb65c8ccfb46219352f3da32312c9d904c08790bb96f43f109338c2141478", size = 285017, upload-time = "2025-11-21T14:55:53.26Z" }, + { url = "https://files.pythonhosted.org/packages/a1/97/1e9a730a7d7674d6022e9eaabff9218ba096b310cef712c004854248a64b/json_schema_for_humans-1.5.1-py3-none-any.whl", hash = "sha256:6b5bb65c8ccfb46219352f3da32312c9d904c08790bb96f43f109338c2141478", size = 285017 }, ] [[package]] name = "json-schema-to-pydantic" version = "0.4.11" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/d8/423895b918706c80db1cee679c13fbe810200b9a9d9a9442c7a58d35c3f2/json_schema_to_pydantic-0.4.11.tar.gz", hash = "sha256:35448ed711a28dd33396b095c8492939b4925aa30eb31942e9b8e08d04279465", size = 56597, upload-time = "2026-03-09T20:53:55.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/d8/423895b918706c80db1cee679c13fbe810200b9a9d9a9442c7a58d35c3f2/json_schema_to_pydantic-0.4.11.tar.gz", hash = "sha256:35448ed711a28dd33396b095c8492939b4925aa30eb31942e9b8e08d04279465", size = 56597 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/64/7cfeb8c6d2a5e73e0f8d732032aa62be9a7724c04beb461d677de0b4beb3/json_schema_to_pydantic-0.4.11-py3-none-any.whl", hash = "sha256:da2ccc39d070ee03dbcf0517d16720e3e33f7aa8d61257ace09af8c51bd46348", size = 17842, upload-time = "2026-03-09T20:53:54.576Z" }, + { url = "https://files.pythonhosted.org/packages/f3/64/7cfeb8c6d2a5e73e0f8d732032aa62be9a7724c04beb461d677de0b4beb3/json_schema_to_pydantic-0.4.11-py3-none-any.whl", hash = "sha256:da2ccc39d070ee03dbcf0517d16720e3e33f7aa8d61257ace09af8c51bd46348", size = 17842 }, ] [[package]] name = "jsonpath-python" version = "1.1.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/18/4ca8742534a5993ff383f7602e325ce2d5d7cc93d72ac5e1cdedbea8a458/jsonpath_python-1.1.6.tar.gz", hash = "sha256:dded9932b4ec41fb8726e09c83afa4e6be618f938c2db287cc2a81723c639671", size = 88178, upload-time = "2026-05-07T01:26:34.482Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/18/4ca8742534a5993ff383f7602e325ce2d5d7cc93d72ac5e1cdedbea8a458/jsonpath_python-1.1.6.tar.gz", hash = "sha256:dded9932b4ec41fb8726e09c83afa4e6be618f938c2db287cc2a81723c639671", size = 88178 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/8a/1270a6803bd821cbfcdda387eaa13cb41a7b1f7b9bd145979b3bfb9d6cb7/jsonpath_python-1.1.6-py3-none-any.whl", hash = "sha256:a1c50afd8d3fbbaf47a4873bc890dcb3c15da96f5c020327977d844d8731a2d4", size = 14453, upload-time = "2026-05-07T01:26:33.306Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/1270a6803bd821cbfcdda387eaa13cb41a7b1f7b9bd145979b3bfb9d6cb7/jsonpath_python-1.1.6-py3-none-any.whl", hash = "sha256:a1c50afd8d3fbbaf47a4873bc890dcb3c15da96f5c020327977d844d8731a2d4", size = 14453 }, ] [[package]] name = "jsonref" version = "1.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425 }, ] [[package]] name = "jsonschema" version = "4.26.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "jsonschema-specifications" }, { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, ] [[package]] name = "jsonschema-path" version = "0.5.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "pathable" }, { name = "pyyaml" }, { name = "referencing" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/79/cd02a4df6d9270efdc7d3feefe6edd730b0820c39eeaa107a2faee8322d5/jsonschema_path-0.5.0.tar.gz", hash = "sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c", size = 19597, upload-time = "2026-05-19T20:45:00.971Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/79/cd02a4df6d9270efdc7d3feefe6edd730b0820c39eeaa107a2faee8322d5/jsonschema_path-0.5.0.tar.gz", hash = "sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c", size = 19597 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/2c/9e69d73c4297508be9e3b64a970ea3971b3eb8db64ffc5802d40bd25981f/jsonschema_path-0.5.0-py3-none-any.whl", hash = "sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2", size = 24077, upload-time = "2026-05-19T20:44:59.225Z" }, + { url = "https://files.pythonhosted.org/packages/04/2c/9e69d73c4297508be9e3b64a970ea3971b3eb8db64ffc5802d40bd25981f/jsonschema_path-0.5.0-py3-none-any.whl", hash = "sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2", size = 24077 }, ] [[package]] name = "jsonschema-specifications" version = "2025.9.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, ] [[package]] name = "keyring" version = "25.7.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jaraco-classes" }, { name = "jaraco-context" }, @@ -2325,42 +2095,42 @@ dependencies = [ { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, { name = "secretstorage", marker = "sys_platform == 'linux'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160 }, ] [[package]] name = "lance-namespace" version = "0.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lance-namespace-urllib3-client" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/81/4cf8d0412e1f37b2bfa70d0aeb9c7ae4ab73607534e44d60b55efb485306/lance_namespace-0.9.0.tar.gz", hash = "sha256:f738b641cc615b17323baa4eb47900f184688739ee3d2ea9fe39396b9588e53d", size = 11637, upload-time = "2026-07-01T07:42:41.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/81/4cf8d0412e1f37b2bfa70d0aeb9c7ae4ab73607534e44d60b55efb485306/lance_namespace-0.9.0.tar.gz", hash = "sha256:f738b641cc615b17323baa4eb47900f184688739ee3d2ea9fe39396b9588e53d", size = 11637 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/fe/f38747c9610ade83dd9a99a0470b9432b6f21ce4e2bb5524edbe66f626fd/lance_namespace-0.9.0-py3-none-any.whl", hash = "sha256:f785ff10927e4ce0db69986576670fedd37f8a33521e8a4630c6be22db8061b2", size = 13501, upload-time = "2026-07-01T07:42:39.372Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/f38747c9610ade83dd9a99a0470b9432b6f21ce4e2bb5524edbe66f626fd/lance_namespace-0.9.0-py3-none-any.whl", hash = "sha256:f785ff10927e4ce0db69986576670fedd37f8a33521e8a4630c6be22db8061b2", size = 13501 }, ] [[package]] name = "lance-namespace-urllib3-client" version = "0.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dateutil" }, { name = "typing-extensions" }, { name = "urllib3" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/c3/32d0e2618549ace857c80a457e5915ef3e1145661baff876c8a5ec27be5b/lance_namespace_urllib3_client-0.9.0.tar.gz", hash = "sha256:cf796fa5307fa4dde91fe4bec2af28b90ba79191852d4394e8fe44276538e40f", size = 235805, upload-time = "2026-07-01T07:42:42.563Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/c3/32d0e2618549ace857c80a457e5915ef3e1145661baff876c8a5ec27be5b/lance_namespace_urllib3_client-0.9.0.tar.gz", hash = "sha256:cf796fa5307fa4dde91fe4bec2af28b90ba79191852d4394e8fe44276538e40f", size = 235805 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/ab/c8754da0a1efc817f8480100cfe12b7e04034df834759de8ecf02beff3cc/lance_namespace_urllib3_client-0.9.0-py3-none-any.whl", hash = "sha256:be819c8cffb1e460a3a504dbf52d1ca009560a48e7202b8c4279998e4adf9fe4", size = 405586, upload-time = "2026-07-01T07:42:40.503Z" }, + { url = "https://files.pythonhosted.org/packages/cc/ab/c8754da0a1efc817f8480100cfe12b7e04034df834759de8ecf02beff3cc/lance_namespace_urllib3_client-0.9.0-py3-none-any.whl", hash = "sha256:be819c8cffb1e460a3a504dbf52d1ca009560a48e7202b8c4279998e4adf9fe4", size = 405586 }, ] [[package]] name = "lancedb" version = "0.34.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecation" }, { name = "lance-namespace" }, @@ -2371,16 +2141,16 @@ dependencies = [ { name = "tqdm" }, ] wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/f7/5262b9aa593f790757163c0165ab0da1dda054758901bea7e4f02c9cb633/lancedb-0.34.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c462f2e6f933cad659fd0179394eaab578acbc9151fe2ef41bc29b36ecca5058", size = 52654213, upload-time = "2026-07-02T17:13:31.102Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/99/05ea0d32229ebea695193ff20c15d6ecae25785ad82a9d4723d98832a284/lancedb-0.34.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:48829e88e708947d0520454ab9e4f8efa35f3e3626469eadd3a6e061b89cb223", size = 55434501, upload-time = "2026-07-02T17:13:34.81Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/4e/4325c13d5afa93c466428a5a0f168ad4d96f5eb4a77bbe7c5100d39c9897/lancedb-0.34.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:05ba8a5b58e064edfbe5be71b1abf2e411b4eaf295d1a173dcb1a55c5bfb5285", size = 58659359, upload-time = "2026-07-02T17:13:38.424Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/5d/8ca165f1386caf6c4d1c515afd52f345b66432264eecfdfb7fd33eefd9af/lancedb-0.34.0-cp39-abi3-win_amd64.whl", hash = "sha256:51cbc11808f9e3332819b9367c975b3a888541447a8e7bea09c57c852a279153", size = 63530726, upload-time = "2026-07-02T17:13:41.612Z" }, + { url = "https://files.pythonhosted.org/packages/df/f7/5262b9aa593f790757163c0165ab0da1dda054758901bea7e4f02c9cb633/lancedb-0.34.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c462f2e6f933cad659fd0179394eaab578acbc9151fe2ef41bc29b36ecca5058", size = 52654213 }, + { url = "https://files.pythonhosted.org/packages/69/99/05ea0d32229ebea695193ff20c15d6ecae25785ad82a9d4723d98832a284/lancedb-0.34.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:48829e88e708947d0520454ab9e4f8efa35f3e3626469eadd3a6e061b89cb223", size = 55434501 }, + { url = "https://files.pythonhosted.org/packages/cd/4e/4325c13d5afa93c466428a5a0f168ad4d96f5eb4a77bbe7c5100d39c9897/lancedb-0.34.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:05ba8a5b58e064edfbe5be71b1abf2e411b4eaf295d1a173dcb1a55c5bfb5285", size = 58659359 }, + { url = "https://files.pythonhosted.org/packages/d9/5d/8ca165f1386caf6c4d1c515afd52f345b66432264eecfdfb7fd33eefd9af/lancedb-0.34.0-cp39-abi3-win_amd64.whl", hash = "sha256:51cbc11808f9e3332819b9367c975b3a888541447a8e7bea09c57c852a279153", size = 63530726 }, ] [[package]] name = "langfuse" version = "4.14.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backoff" }, { name = "httpx" }, @@ -2391,57 +2161,73 @@ dependencies = [ { name = "pydantic" }, { name = "wrapt" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/61/bb7fa00f5334a670c9aae1f053bc725654a0ca5435fa17d0cb103df8c978/langfuse-4.14.1.tar.gz", hash = "sha256:576641820ae79aeca71453c8eff3c8c35d53f7e41729c918f59eb81f7499faf9", size = 381977, upload-time = "2026-07-20T08:44:04.669Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/61/bb7fa00f5334a670c9aae1f053bc725654a0ca5435fa17d0cb103df8c978/langfuse-4.14.1.tar.gz", hash = "sha256:576641820ae79aeca71453c8eff3c8c35d53f7e41729c918f59eb81f7499faf9", size = 381977 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/e4/6c305af16f1ce344271dd829383b7af586530cf60b55af1aaf274ca37ba9/langfuse-4.14.1-py3-none-any.whl", hash = "sha256:07d19f16338b8e21f8e5996b7e6c3ed150ee582fbaa6275ac9eeea297093f4be", size = 669448, upload-time = "2026-07-20T08:44:05.682Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e4/6c305af16f1ce344271dd829383b7af586530cf60b55af1aaf274ca37ba9/langfuse-4.14.1-py3-none-any.whl", hash = "sha256:07d19f16338b8e21f8e5996b7e6c3ed150ee582fbaa6275ac9eeea297093f4be", size = 669448 }, ] [[package]] name = "lazy-object-proxy" version = "1.12.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/a2/69df9c6ba6d316cfd81fe2381e464db3e6de5db45f8c43c6a23504abf8cb/lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61", size = 43681, upload-time = "2025-08-22T13:50:06.783Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/a2/69df9c6ba6d316cfd81fe2381e464db3e6de5db45f8c43c6a23504abf8cb/lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61", size = 43681 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/26/b74c791008841f8ad896c7f293415136c66cc27e7c7577de4ee68040c110/lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e", size = 26745, upload-time = "2025-08-22T13:42:44.982Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/52/641870d309e5d1fb1ea7d462a818ca727e43bfa431d8c34b173eb090348c/lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e", size = 71537, upload-time = "2025-08-22T13:42:46.141Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/b6/919118e99d51c5e76e8bf5a27df406884921c0acf2c7b8a3b38d847ab3e9/lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655", size = 71141, upload-time = "2025-08-22T13:42:47.375Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/47/1d20e626567b41de085cf4d4fb3661a56c159feaa73c825917b3b4d4f806/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff", size = 69449, upload-time = "2025-08-22T13:42:48.49Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/8d/25c20ff1a1a8426d9af2d0b6f29f6388005fc8cd10d6ee71f48bff86fdd0/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be", size = 70744, upload-time = "2025-08-22T13:42:49.608Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/67/8ec9abe15c4f8a4bcc6e65160a2c667240d025cbb6591b879bea55625263/lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1", size = 26568, upload-time = "2025-08-22T13:42:57.719Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/12/cd2235463f3469fd6c62d41d92b7f120e8134f76e52421413a0ad16d493e/lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65", size = 27391, upload-time = "2025-08-22T13:42:50.62Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/9e/f1c53e39bbebad2e8609c67d0830cc275f694d0ea23d78e8f6db526c12d3/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9", size = 80552, upload-time = "2025-08-22T13:42:51.731Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/b6/6c513693448dcb317d9d8c91d91f47addc09553613379e504435b4cc8b3e/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66", size = 82857, upload-time = "2025-08-22T13:42:53.225Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/1c/d9c4aaa4c75da11eb7c22c43d7c90a53b4fca0e27784a5ab207768debea7/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847", size = 80833, upload-time = "2025-08-22T13:42:54.391Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/ae/29117275aac7d7d78ae4f5a4787f36ff33262499d486ac0bf3e0b97889f6/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac", size = 79516, upload-time = "2025-08-22T13:42:55.812Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/40/b4e48b2c38c69392ae702ae7afa7b6551e0ca5d38263198b7c79de8b3bdf/lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f", size = 27656, upload-time = "2025-08-22T13:42:56.793Z" }, + { url = "https://files.pythonhosted.org/packages/f4/26/b74c791008841f8ad896c7f293415136c66cc27e7c7577de4ee68040c110/lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e", size = 26745 }, + { url = "https://files.pythonhosted.org/packages/9b/52/641870d309e5d1fb1ea7d462a818ca727e43bfa431d8c34b173eb090348c/lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e", size = 71537 }, + { url = "https://files.pythonhosted.org/packages/47/b6/919118e99d51c5e76e8bf5a27df406884921c0acf2c7b8a3b38d847ab3e9/lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655", size = 71141 }, + { url = "https://files.pythonhosted.org/packages/e5/47/1d20e626567b41de085cf4d4fb3661a56c159feaa73c825917b3b4d4f806/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff", size = 69449 }, + { url = "https://files.pythonhosted.org/packages/58/8d/25c20ff1a1a8426d9af2d0b6f29f6388005fc8cd10d6ee71f48bff86fdd0/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be", size = 70744 }, + { url = "https://files.pythonhosted.org/packages/c0/67/8ec9abe15c4f8a4bcc6e65160a2c667240d025cbb6591b879bea55625263/lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1", size = 26568 }, + { url = "https://files.pythonhosted.org/packages/23/12/cd2235463f3469fd6c62d41d92b7f120e8134f76e52421413a0ad16d493e/lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65", size = 27391 }, + { url = "https://files.pythonhosted.org/packages/60/9e/f1c53e39bbebad2e8609c67d0830cc275f694d0ea23d78e8f6db526c12d3/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9", size = 80552 }, + { url = "https://files.pythonhosted.org/packages/4c/b6/6c513693448dcb317d9d8c91d91f47addc09553613379e504435b4cc8b3e/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66", size = 82857 }, + { url = "https://files.pythonhosted.org/packages/12/1c/d9c4aaa4c75da11eb7c22c43d7c90a53b4fca0e27784a5ab207768debea7/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847", size = 80833 }, + { url = "https://files.pythonhosted.org/packages/0b/ae/29117275aac7d7d78ae4f5a4787f36ff33262499d486ac0bf3e0b97889f6/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac", size = 79516 }, + { url = "https://files.pythonhosted.org/packages/19/40/b4e48b2c38c69392ae702ae7afa7b6551e0ca5d38263198b7c79de8b3bdf/lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f", size = 27656 }, ] [[package]] name = "librt" version = "0.13.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/2f/3908645ddddab7120b46295e541ead308109fa48dbec7d67d7a778870d60/librt-0.13.0.tar.gz", hash = "sha256:1d2a610c14ac0d0750ee0a3ab8548e83155258387891caaca04def4bf7289781", size = 211402, upload-time = "2026-07-08T12:26:29.834Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/3b/18e7b63255297a2bdc9c25c8d6d4ca8eca9f63aceb1252c0f7427ac7099e/librt-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a468951af16155824e88bdd8326ebe5bdb371f3ec0ac04642994b98201d914f3", size = 151027, upload-time = "2026-07-08T12:25:19.638Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/68/e2248452c00d1a03b45fee1752cdc8f790a476efd2402b75181da88a9e61/librt-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae01d8512cc17079e53425635327dbf3f7ff57a42c00dec348bf79791c56444c", size = 155152, upload-time = "2026-07-08T12:25:20.851Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/16/52b1c99bf19057a062aac39c900cbb81499f6f75d6c537c14463d247ba78/librt-0.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32c26893cd085c1efe83219e78d866da23fb20a066101b8f68210004361d224c", size = 502499, upload-time = "2026-07-08T12:25:22.055Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/54/b811151805c795f55e0dedee6ec687b75f9982a8105d240ea3910737a77b/librt-0.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5929da1981a46bcf4b28b1b9499905f0ff58e2419da402a048234e9783acbc4b", size = 496108, upload-time = "2026-07-08T12:25:23.296Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/f8/094d6b2bd93f3fdaa54db54cc788c4a365333bddad65ab02e04da0b1d004/librt-0.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b85d664d777bab6c0d709416cb42938251fda9e221b79e3a2215d85df5f4f9", size = 531576, upload-time = "2026-07-08T12:25:24.648Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/40/541733d5755824f968f7ec39d78ffbd75d145964157ae5e69a09ec6d7326/librt-0.13.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:531b2df3e9fe96b1fcf73a6d165921e4656be5f58d631d384ebce344298368db", size = 524390, upload-time = "2026-07-08T12:25:25.898Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/b5/255673cfdbf5ba663339d36cd863c897289ab4337577e19f9405ce059f36/librt-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:109b84a9edf69ad89dc1f66358659e14a031baca95e3e5b0060bd903ede8efd6", size = 543053, upload-time = "2026-07-08T12:25:27.436Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/11/ab5005e9c9850710f21e354201bf090646349d3fabf5f951eaf70235729e/librt-0.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1304368a3e7ffc3e9db986796cc5326fdb5943a3567ecc137cff318e4240c0e7", size = 546387, upload-time = "2026-07-08T12:25:28.65Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/04/a5d7ce1d1df1afd15ca283dcdf7530ac073e12d69ae8c40879dda96f7868/librt-0.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e4f9b472e7d308d94b62c801982065661158c6ed02790d6c7ddb4337cea0f9c1", size = 535970, upload-time = "2026-07-08T12:25:30.171Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/76/927e267a6daa290174ac281b23c9804c8829b042ade9c6f24a065f540958/librt-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f836c37478f167a81200d8c8b2c920a22224564bed2c23d7aeec760965c367a", size = 573582, upload-time = "2026-07-08T12:25:31.507Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/24/b6c5213efe39c19f9e13605644d0cf063b4ddaa33ac2e45b088e23a70e2e/librt-0.13.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:4000d961ff9598ac6ea603c6c836a5ed49bc205ade5fc378b998dfe1e2c36628", size = 82189, upload-time = "2026-07-08T12:25:32.675Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/00/d29736be177a906ac0b84a5b04b4fbfa22c776dc2f366de4172b0f968c08/librt-0.13.0-cp313-cp313-win32.whl", hash = "sha256:79e44cff71750d299d61a678e49995b0d5935a9cda238c2574daeca3ba536927", size = 106193, upload-time = "2026-07-08T12:25:33.692Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/ac/aff6fb45393cb8912f39dfb156ef6b2d1cadb207ff465fc8f66141054be8/librt-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:54dab44a847d5ad1acd05c8a83fe518ae685516ecf4d3f7cc6e3df2a66767650", size = 126962, upload-time = "2026-07-08T12:25:34.769Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/3a/d68cb2b334d53fd30fac81d3a489ce4ba0d9506f4df43fcf676b68352b19/librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566", size = 112127, upload-time = "2026-07-08T12:25:35.981Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/2f/3908645ddddab7120b46295e541ead308109fa48dbec7d67d7a778870d60/librt-0.13.0.tar.gz", hash = "sha256:1d2a610c14ac0d0750ee0a3ab8548e83155258387891caaca04def4bf7289781", size = 211402 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/3b/18e7b63255297a2bdc9c25c8d6d4ca8eca9f63aceb1252c0f7427ac7099e/librt-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a468951af16155824e88bdd8326ebe5bdb371f3ec0ac04642994b98201d914f3", size = 151027 }, + { url = "https://files.pythonhosted.org/packages/4d/68/e2248452c00d1a03b45fee1752cdc8f790a476efd2402b75181da88a9e61/librt-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae01d8512cc17079e53425635327dbf3f7ff57a42c00dec348bf79791c56444c", size = 155152 }, + { url = "https://files.pythonhosted.org/packages/0e/16/52b1c99bf19057a062aac39c900cbb81499f6f75d6c537c14463d247ba78/librt-0.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32c26893cd085c1efe83219e78d866da23fb20a066101b8f68210004361d224c", size = 502499 }, + { url = "https://files.pythonhosted.org/packages/9f/54/b811151805c795f55e0dedee6ec687b75f9982a8105d240ea3910737a77b/librt-0.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5929da1981a46bcf4b28b1b9499905f0ff58e2419da402a048234e9783acbc4b", size = 496108 }, + { url = "https://files.pythonhosted.org/packages/8f/f8/094d6b2bd93f3fdaa54db54cc788c4a365333bddad65ab02e04da0b1d004/librt-0.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b85d664d777bab6c0d709416cb42938251fda9e221b79e3a2215d85df5f4f9", size = 531576 }, + { url = "https://files.pythonhosted.org/packages/2e/40/541733d5755824f968f7ec39d78ffbd75d145964157ae5e69a09ec6d7326/librt-0.13.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:531b2df3e9fe96b1fcf73a6d165921e4656be5f58d631d384ebce344298368db", size = 524390 }, + { url = "https://files.pythonhosted.org/packages/c6/b5/255673cfdbf5ba663339d36cd863c897289ab4337577e19f9405ce059f36/librt-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:109b84a9edf69ad89dc1f66358659e14a031baca95e3e5b0060bd903ede8efd6", size = 543053 }, + { url = "https://files.pythonhosted.org/packages/9e/11/ab5005e9c9850710f21e354201bf090646349d3fabf5f951eaf70235729e/librt-0.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1304368a3e7ffc3e9db986796cc5326fdb5943a3567ecc137cff318e4240c0e7", size = 546387 }, + { url = "https://files.pythonhosted.org/packages/a2/04/a5d7ce1d1df1afd15ca283dcdf7530ac073e12d69ae8c40879dda96f7868/librt-0.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e4f9b472e7d308d94b62c801982065661158c6ed02790d6c7ddb4337cea0f9c1", size = 535970 }, + { url = "https://files.pythonhosted.org/packages/5a/76/927e267a6daa290174ac281b23c9804c8829b042ade9c6f24a065f540958/librt-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f836c37478f167a81200d8c8b2c920a22224564bed2c23d7aeec760965c367a", size = 573582 }, + { url = "https://files.pythonhosted.org/packages/10/24/b6c5213efe39c19f9e13605644d0cf063b4ddaa33ac2e45b088e23a70e2e/librt-0.13.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:4000d961ff9598ac6ea603c6c836a5ed49bc205ade5fc378b998dfe1e2c36628", size = 82189 }, + { url = "https://files.pythonhosted.org/packages/4c/00/d29736be177a906ac0b84a5b04b4fbfa22c776dc2f366de4172b0f968c08/librt-0.13.0-cp313-cp313-win32.whl", hash = "sha256:79e44cff71750d299d61a678e49995b0d5935a9cda238c2574daeca3ba536927", size = 106193 }, + { url = "https://files.pythonhosted.org/packages/c8/ac/aff6fb45393cb8912f39dfb156ef6b2d1cadb207ff465fc8f66141054be8/librt-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:54dab44a847d5ad1acd05c8a83fe518ae685516ecf4d3f7cc6e3df2a66767650", size = 126962 }, + { url = "https://files.pythonhosted.org/packages/d9/3a/d68cb2b334d53fd30fac81d3a489ce4ba0d9506f4df43fcf676b68352b19/librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566", size = 112127 }, +] + +[[package]] +name = "llmling-models" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyenv", extra = ["httpx"] }, + { name = "pydantic" }, + { name = "pydantic-ai-slim" }, + { name = "schemez" }, + { name = "tokonomics" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/eb/0730ef45f5442da4ac1866e8969678ce2d96551474f8dc66fe85f47d703c/llmling_models-1.6.1.tar.gz", hash = "sha256:6f73ead1d1bdc518531c0af0756cd7e4862f176e6a15d1514a1ad299211d2908", size = 90738 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/54/90dc8ef416f24b138ebbe579548dd2940f6cdae9f93db0ce0f1c9e920524/llmling_models-1.6.1-py3-none-any.whl", hash = "sha256:94bfd7608b39b324ca2d40a1a07c1a623990ea6c81213deef327012c48dde147", size = 130926 }, ] [[package]] name = "logfire" version = "4.38.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "executing" }, { name = "opentelemetry-exporter-otlp-proto-http" }, @@ -2451,9 +2237,9 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/b7/2b9670e893b5c662c23252bc52cafa2664b7c582b8c36b280693b2beaaa0/logfire-4.38.0.tar.gz", hash = "sha256:da62f7616f3411098cf5fb087f9ff24f54886a9087a238fe0e5fae8bbd609717", size = 1237040, upload-time = "2026-07-20T12:15:33.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/b7/2b9670e893b5c662c23252bc52cafa2664b7c582b8c36b280693b2beaaa0/logfire-4.38.0.tar.gz", hash = "sha256:da62f7616f3411098cf5fb087f9ff24f54886a9087a238fe0e5fae8bbd609717", size = 1237040 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/84/7593993a474fdea837577303840177864aaa2883859874d5fa6d037af03f/logfire-4.38.0-py3-none-any.whl", hash = "sha256:da4bffd43190930d0b889e06c596c0a7fce1c485a51de862ad9ff0d5033ecfb9", size = 403289, upload-time = "2026-07-20T12:15:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/5c/84/7593993a474fdea837577303840177864aaa2883859874d5fa6d037af03f/logfire-4.38.0-py3-none-any.whl", hash = "sha256:da4bffd43190930d0b889e06c596c0a7fce1c485a51de862ad9ff0d5033ecfb9", size = 403289 }, ] [package.optional-dependencies] @@ -2467,114 +2253,114 @@ httpx = [ [[package]] name = "logfire-api" version = "4.38.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/1a/17af258260cf8378fabd10bacef0c585e19697eab1deea25f8e4f4fb6462/logfire_api-4.38.0.tar.gz", hash = "sha256:e773cd4a26edb48ef4ec6f88661d1c670bb68c303a71c7043a236d64a9699cd2", size = 90364, upload-time = "2026-07-20T12:15:34.913Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/1a/17af258260cf8378fabd10bacef0c585e19697eab1deea25f8e4f4fb6462/logfire_api-4.38.0.tar.gz", hash = "sha256:e773cd4a26edb48ef4ec6f88661d1c670bb68c303a71c7043a236d64a9699cd2", size = 90364 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/22/598f988f293ce391e88baddeeaa1acaea625822653193935301e7149b5c6/logfire_api-4.38.0-py3-none-any.whl", hash = "sha256:6c0612d9200727b64b42eba6068d0d6afd45308f4b844ffd6290c2d33aa6a6f6", size = 140109, upload-time = "2026-07-20T12:15:31.973Z" }, + { url = "https://files.pythonhosted.org/packages/26/22/598f988f293ce391e88baddeeaa1acaea625822653193935301e7149b5c6/logfire_api-4.38.0-py3-none-any.whl", hash = "sha256:6c0612d9200727b64b42eba6068d0d6afd45308f4b844ffd6290c2d33aa6a6f6", size = 140109 }, ] [[package]] name = "loguru" version = "0.7.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "win32-setctime", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 }, ] [[package]] name = "macholib" version = "1.16.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "altgraph" }, + { name = "altgraph", marker = "sys_platform != 'win32'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362", size = 59427, upload-time = "2025-11-22T08:28:38.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362", size = 59427 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/d1/a9f36f8ecdf0fb7c9b1e78c8d7af12b8c8754e74851ac7b94a8305540fc7/macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea", size = 38117, upload-time = "2025-11-22T08:28:36.939Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/a9f36f8ecdf0fb7c9b1e78c8d7af12b8c8754e74851ac7b94a8305540fc7/macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea", size = 38117 }, ] [[package]] name = "magika" version = "0.6.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "numpy" }, { name = "onnxruntime" }, { name = "python-dotenv" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/b6/8fdd991142ad3e037179a494b153f463024e5a211ef3ad948b955c26b4de/magika-0.6.2.tar.gz", hash = "sha256:37eb6ae8020f6e68f231bc06052c0a0cbe8e6fa27492db345e8dc867dbceb067", size = 3036634, upload-time = "2025-05-02T14:54:18.88Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/8fdd991142ad3e037179a494b153f463024e5a211ef3ad948b955c26b4de/magika-0.6.2.tar.gz", hash = "sha256:37eb6ae8020f6e68f231bc06052c0a0cbe8e6fa27492db345e8dc867dbceb067", size = 3036634 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/07/4f7748f34279f2852068256992377474f9700b6fbad6735d6be58605178f/magika-0.6.2-py3-none-any.whl", hash = "sha256:5ef72fbc07723029b3684ef81454bc224ac5f60986aa0fc5a28f4456eebcb5b2", size = 2967609, upload-time = "2025-05-02T14:54:09.696Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/6d/0783af677e601d8a42258f0fbc47663abf435f927e58a8d2928296743099/magika-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9109309328a1553886c8ff36c2ee9a5e9cfd36893ad81b65bf61a57debdd9d0e", size = 12404787, upload-time = "2025-05-02T14:54:16.963Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/ad/42e39748ddc4bbe55c2dc1093ce29079c04d096ac0d844f8ae66178bc3ed/magika-0.6.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:57cd1d64897634d15de552bd6b3ae9c6ff6ead9c60d384dc46497c08288e4559", size = 15091089, upload-time = "2025-05-02T14:54:11.59Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/1f/28e412d0ccedc068fbccdae6a6233faaa97ec3e5e2ffd242e49655b10064/magika-0.6.2-py3-none-win_amd64.whl", hash = "sha256:711f427a633e0182737dcc2074748004842f870643585813503ff2553b973b9f", size = 12385740, upload-time = "2025-05-02T14:54:14.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/07/4f7748f34279f2852068256992377474f9700b6fbad6735d6be58605178f/magika-0.6.2-py3-none-any.whl", hash = "sha256:5ef72fbc07723029b3684ef81454bc224ac5f60986aa0fc5a28f4456eebcb5b2", size = 2967609 }, + { url = "https://files.pythonhosted.org/packages/64/6d/0783af677e601d8a42258f0fbc47663abf435f927e58a8d2928296743099/magika-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9109309328a1553886c8ff36c2ee9a5e9cfd36893ad81b65bf61a57debdd9d0e", size = 12404787 }, + { url = "https://files.pythonhosted.org/packages/8a/ad/42e39748ddc4bbe55c2dc1093ce29079c04d096ac0d844f8ae66178bc3ed/magika-0.6.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:57cd1d64897634d15de552bd6b3ae9c6ff6ead9c60d384dc46497c08288e4559", size = 15091089 }, + { url = "https://files.pythonhosted.org/packages/b0/1f/28e412d0ccedc068fbccdae6a6233faaa97ec3e5e2ffd242e49655b10064/magika-0.6.2-py3-none-win_amd64.whl", hash = "sha256:711f427a633e0182737dcc2074748004842f870643585813503ff2553b973b9f", size = 12385740 }, ] [[package]] name = "mako" version = "1.3.12" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/62/791b31e69ae182791ec67f04850f2f062716bbd205483d63a215f3e062d3/mako-1.3.12.tar.gz", hash = "sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a", size = 400219, upload-time = "2026-04-28T19:01:08.512Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/62/791b31e69ae182791ec67f04850f2f062716bbd205483d63a215f3e062d3/mako-1.3.12.tar.gz", hash = "sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a", size = 400219 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521 }, ] [[package]] name = "markdown" version = "3.10.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180 }, ] [[package]] name = "markdown-it-py" version = "4.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687 }, ] [[package]] name = "markdown2" version = "2.5.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/ae/07d4a5fcaa5509221287d289323d75ac8eda5a5a4ac9de2accf7bbcc2b88/markdown2-2.5.5.tar.gz", hash = "sha256:001547e68f6e7fcf0f1cb83f7e82f48aa7d48b2c6a321f0cd20a853a8a2d1664", size = 157249, upload-time = "2026-03-02T20:46:53.411Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/ae/07d4a5fcaa5509221287d289323d75ac8eda5a5a4ac9de2accf7bbcc2b88/markdown2-2.5.5.tar.gz", hash = "sha256:001547e68f6e7fcf0f1cb83f7e82f48aa7d48b2c6a321f0cd20a853a8a2d1664", size = 157249 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/af/4b3891eb0a49d6cfd5cbf3e9bf514c943afc2b0f13e2c57cc57cd88ecc21/markdown2-2.5.5-py3-none-any.whl", hash = "sha256:be798587e09d1f52d2e4d96a649c4b82a778c75f9929aad52a2c95747fa26941", size = 56250, upload-time = "2026-03-02T20:46:52.032Z" }, + { url = "https://files.pythonhosted.org/packages/43/af/4b3891eb0a49d6cfd5cbf3e9bf514c943afc2b0f13e2c57cc57cd88ecc21/markdown2-2.5.5-py3-none-any.whl", hash = "sha256:be798587e09d1f52d2e4d96a649c4b82a778c75f9929aad52a2c95747fa26941", size = 56250 }, ] [[package]] name = "markdownify" version = "1.2.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "six" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/ab/d1297139c0e2ceb151ae564c8c4f57ac0155d8f1f8b4cbd5d6523c82ea36/markdownify-1.2.3.tar.gz", hash = "sha256:1a176f05522c8a2cb1dd3ab9d307dcdadbed5c26ae717855bfc42b3b6d38d937", size = 18852, upload-time = "2026-06-30T20:27:39.06Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ab/d1297139c0e2ceb151ae564c8c4f57ac0155d8f1f8b4cbd5d6523c82ea36/markdownify-1.2.3.tar.gz", hash = "sha256:1a176f05522c8a2cb1dd3ab9d307dcdadbed5c26ae717855bfc42b3b6d38d937", size = 18852 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/10/fa543d484e8b1199243fe20eedd02cc5af050edebce98a7293a5773df592/markdownify-1.2.3-py3-none-any.whl", hash = "sha256:a189a0bedfd14009030fde5f85bb6f77c56897cb839b5c25315dd7d4e3e290ba", size = 15732, upload-time = "2026-06-30T20:27:38.094Z" }, + { url = "https://files.pythonhosted.org/packages/04/10/fa543d484e8b1199243fe20eedd02cc5af050edebce98a7293a5773df592/markdownify-1.2.3-py3-none-any.whl", hash = "sha256:a189a0bedfd14009030fde5f85bb6f77c56897cb839b5c25315dd7d4e3e290ba", size = 15732 }, ] [[package]] name = "markitdown" version = "0.1.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "charset-normalizer" }, @@ -2583,57 +2369,57 @@ dependencies = [ { name = "markdownify" }, { name = "requests" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/b7/91fe0e2df07107ab701a15c8ad3213135707e4d6206ae9bd8f457a7ad86a/markitdown-0.1.6.tar.gz", hash = "sha256:e5bdbaffd971b29598c7c39ef0e9afce2f08c0751fbfa4e4257678ebaf8cfc7e", size = 50795, upload-time = "2026-05-26T22:43:59.318Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/b7/91fe0e2df07107ab701a15c8ad3213135707e4d6206ae9bd8f457a7ad86a/markitdown-0.1.6.tar.gz", hash = "sha256:e5bdbaffd971b29598c7c39ef0e9afce2f08c0751fbfa4e4257678ebaf8cfc7e", size = 50795 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/30/8031f183ee86ea8ac4e7ea1296bab4cca1bee2fd036a26df69764eb7ca74/markitdown-0.1.6-py3-none-any.whl", hash = "sha256:07b2d5bf87b5c53e13a9f2fdc440df8ccc85e26f40c1e557781727b700049775", size = 70032, upload-time = "2026-05-26T22:44:03.209Z" }, + { url = "https://files.pythonhosted.org/packages/9e/30/8031f183ee86ea8ac4e7ea1296bab4cca1bee2fd036a26df69764eb7ca74/markitdown-0.1.6-py3-none-any.whl", hash = "sha256:07b2d5bf87b5c53e13a9f2fdc440df8ccc85e26f40c1e557781727b700049775", size = 70032 }, ] [[package]] name = "markupsafe" version = "3.0.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, ] [[package]] name = "marshmallow" version = "3.26.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/79/de6c16cc902f4fc372236926b0ce2ab7845268dcc30fb2fbb7f71b418631/marshmallow-3.26.2.tar.gz", hash = "sha256:bbe2adb5a03e6e3571b573f42527c6fe926e17467833660bebd11593ab8dfd57", size = 222095, upload-time = "2025-12-22T06:53:53.309Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/79/de6c16cc902f4fc372236926b0ce2ab7845268dcc30fb2fbb7f71b418631/marshmallow-3.26.2.tar.gz", hash = "sha256:bbe2adb5a03e6e3571b573f42527c6fe926e17467833660bebd11593ab8dfd57", size = 222095 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/2f/5108cb3ee4ba6501748c4908b908e55f42a5b66245b4cfe0c99326e1ef6e/marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73", size = 50964, upload-time = "2025-12-22T06:53:51.801Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/5108cb3ee4ba6501748c4908b908e55f42a5b66245b4cfe0c99326e1ef6e/marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73", size = 50964 }, ] [[package]] name = "mcp" version = "1.28.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "httpx" }, @@ -2650,92 +2436,92 @@ dependencies = [ { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620 }, ] [[package]] name = "mcp-run" version = "0.5.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "extism" }, { name = "mcp" }, { name = "requests" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/5e/99422329beb04d4a4b7d150e469e7ae1d85e68d09888e077dee57ff5a509/mcp_run-0.5.0.tar.gz", hash = "sha256:f6270283dcb51328dfd97b984a4d1264d5d04853340d94dd0abaf7b53ad25f75", size = 13366, upload-time = "2025-05-08T22:12:33.562Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/5e/99422329beb04d4a4b7d150e469e7ae1d85e68d09888e077dee57ff5a509/mcp_run-0.5.0.tar.gz", hash = "sha256:f6270283dcb51328dfd97b984a4d1264d5d04853340d94dd0abaf7b53ad25f75", size = 13366 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/d4/b4f925a6f57c680e10fc4766af607de62cca8cfd28664a5cd67e3f0351a1/mcp_run-0.5.0-py3-none-any.whl", hash = "sha256:ba30756d7e6feb872e82d08fd0267638b9bd88c2c326bcd6ac5cb51cf59a3969", size = 14038, upload-time = "2025-05-08T22:12:32.365Z" }, + { url = "https://files.pythonhosted.org/packages/02/d4/b4f925a6f57c680e10fc4766af607de62cca8cfd28664a5cd67e3f0351a1/mcp_run-0.5.0-py3-none-any.whl", hash = "sha256:ba30756d7e6feb872e82d08fd0267638b9bd88c2c326bcd6ac5cb51cf59a3969", size = 14038 }, ] [[package]] name = "mcpx-py" version = "0.7.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mcpx-pydantic-ai" }, { name = "psutil" }, { name = "python-dotenv" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/86/05715a4edf13f591e6b8f9925e691abbadcaadabb9c1db176355995e510b/mcpx_py-0.7.0.tar.gz", hash = "sha256:f6726b6606debacbc86a6c2d76083179ebc530264d41e4c451db96000f7b3347", size = 8120, upload-time = "2025-05-08T22:17:43.698Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/86/05715a4edf13f591e6b8f9925e691abbadcaadabb9c1db176355995e510b/mcpx_py-0.7.0.tar.gz", hash = "sha256:f6726b6606debacbc86a6c2d76083179ebc530264d41e4c451db96000f7b3347", size = 8120 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/1d/92b95bb02f8e39f0e31d6786edbf60ecfad4acb833a9804861bbef61de39/mcpx_py-0.7.0-py3-none-any.whl", hash = "sha256:72501095019401cffe6821466a8ebf585aa3f82a8e4f6130b31cedc9c61844dc", size = 7175, upload-time = "2025-05-08T22:17:42.471Z" }, + { url = "https://files.pythonhosted.org/packages/79/1d/92b95bb02f8e39f0e31d6786edbf60ecfad4acb833a9804861bbef61de39/mcpx_py-0.7.0-py3-none-any.whl", hash = "sha256:72501095019401cffe6821466a8ebf585aa3f82a8e4f6130b31cedc9c61844dc", size = 7175 }, ] [[package]] name = "mcpx-pydantic-ai" version = "0.7.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mcp-run" }, { name = "pydantic" }, { name = "pydantic-ai" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/ce/a4587c7f41960eb243aa7298f0ba4eda357cdd20f843640dae2949f053dd/mcpx_pydantic_ai-0.7.0.tar.gz", hash = "sha256:c7d951c96fd8bc1ea2731562d6e1b76c5d274d3b84afc3fc8524be669ce3c81d", size = 2720, upload-time = "2025-05-08T22:15:45.303Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/ce/a4587c7f41960eb243aa7298f0ba4eda357cdd20f843640dae2949f053dd/mcpx_pydantic_ai-0.7.0.tar.gz", hash = "sha256:c7d951c96fd8bc1ea2731562d6e1b76c5d274d3b84afc3fc8524be669ce3c81d", size = 2720 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/d9/cda159c1d06cb4a31bf38027492fcae91d44f026a3a813c9772ba0169106/mcpx_pydantic_ai-0.7.0-py3-none-any.whl", hash = "sha256:e50cc29a4219e33c4eba3737cf0106ded18c748590a2fa79264d97decd6a80bd", size = 2128, upload-time = "2025-05-08T22:15:44.31Z" }, + { url = "https://files.pythonhosted.org/packages/46/d9/cda159c1d06cb4a31bf38027492fcae91d44f026a3a813c9772ba0169106/mcpx_pydantic_ai-0.7.0-py3-none-any.whl", hash = "sha256:e50cc29a4219e33c4eba3737cf0106ded18c748590a2fa79264d97decd6a80bd", size = 2128 }, ] [[package]] name = "mdurl" version = "0.1.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] [[package]] name = "mergedeep" version = "1.3.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, ] [[package]] name = "miniaudio" version = "1.71" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/d5/e5439dc08561f73656bfeb3340fc64ab63163e101426593d8fb9a025ff1e/miniaudio-1.71.tar.gz", hash = "sha256:ff51e2887bb673e2e757752b586b3dc924d59aa5fbcae9bbc45f4a111bd3262b", size = 1116480, upload-time = "2026-04-29T21:20:38.182Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/d5/e5439dc08561f73656bfeb3340fc64ab63163e101426593d8fb9a025ff1e/miniaudio-1.71.tar.gz", hash = "sha256:ff51e2887bb673e2e757752b586b3dc924d59aa5fbcae9bbc45f4a111bd3262b", size = 1116480 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/85/44545f767ec21142ffed5f9108406d11dc8a19aafed9bd57621a0892bb60/miniaudio-1.71-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:61b86f26d653040db32d9d15b05446321dd10e45beba25b44f841e26935213d5", size = 377184, upload-time = "2026-04-29T21:20:24.109Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/d1/071a560000c8ce903dc919968ecce40fbe7a73213ac399051b887184f8a3/miniaudio-1.71-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d9dc15eff711bcfc62a9d05e0c78e4bc34821a455595e049629f2fea7491a523", size = 351488, upload-time = "2026-04-29T21:20:25.183Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/24/5873a569451cae5686fb656ebd78ffe0b5eebe48ca21ef61e227d237d20a/miniaudio-1.71-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12bc33e7e61072b4b541c14e10ef76119d5643e6bbb98e2dec0c0738889438fb", size = 643552, upload-time = "2026-04-29T21:20:26.211Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/9b/25785525e6b5ff9afd7f4c4279215dc09c3317ea4d837275b1ae17912b36/miniaudio-1.71-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70fa2ea5353e6919aca59b8c5768144af009d18c3bca251749d66fb497424563", size = 645171, upload-time = "2026-04-29T21:20:27.494Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/6d/cbfd55fdc40256231f7b0c861e2bf79cc289bfbbe5e15869317944e6d673/miniaudio-1.71-cp313-cp313-win32.whl", hash = "sha256:1bf93aeede652926f27f430f0fd69ef0cf8a949c07b537d6a2f295602c747037", size = 235088, upload-time = "2026-04-29T21:20:29.031Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/8d/d5059c04b247b1079c0e48914a9ec20352910a3b9373060fb258dfd194ab/miniaudio-1.71-cp313-cp313-win_amd64.whl", hash = "sha256:4c849ccb1349f7b3553a77a66fe7e972315185f5c4c44a0bbda7ebcdd224db37", size = 274247, upload-time = "2026-04-29T21:20:29.96Z" }, + { url = "https://files.pythonhosted.org/packages/3a/85/44545f767ec21142ffed5f9108406d11dc8a19aafed9bd57621a0892bb60/miniaudio-1.71-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:61b86f26d653040db32d9d15b05446321dd10e45beba25b44f841e26935213d5", size = 377184 }, + { url = "https://files.pythonhosted.org/packages/bd/d1/071a560000c8ce903dc919968ecce40fbe7a73213ac399051b887184f8a3/miniaudio-1.71-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d9dc15eff711bcfc62a9d05e0c78e4bc34821a455595e049629f2fea7491a523", size = 351488 }, + { url = "https://files.pythonhosted.org/packages/46/24/5873a569451cae5686fb656ebd78ffe0b5eebe48ca21ef61e227d237d20a/miniaudio-1.71-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12bc33e7e61072b4b541c14e10ef76119d5643e6bbb98e2dec0c0738889438fb", size = 643552 }, + { url = "https://files.pythonhosted.org/packages/90/9b/25785525e6b5ff9afd7f4c4279215dc09c3317ea4d837275b1ae17912b36/miniaudio-1.71-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70fa2ea5353e6919aca59b8c5768144af009d18c3bca251749d66fb497424563", size = 645171 }, + { url = "https://files.pythonhosted.org/packages/b1/6d/cbfd55fdc40256231f7b0c861e2bf79cc289bfbbe5e15869317944e6d673/miniaudio-1.71-cp313-cp313-win32.whl", hash = "sha256:1bf93aeede652926f27f430f0fd69ef0cf8a949c07b537d6a2f295602c747037", size = 235088 }, + { url = "https://files.pythonhosted.org/packages/8d/8d/d5059c04b247b1079c0e48914a9ec20352910a3b9373060fb258dfd194ab/miniaudio-1.71-cp313-cp313-win_amd64.whl", hash = "sha256:4c849ccb1349f7b3553a77a66fe7e972315185f5c4c44a0bbda7ebcdd224db37", size = 274247 }, ] [[package]] name = "mistralai" version = "2.7.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "eval-type-backport" }, { name = "httpx" }, @@ -2746,15 +2532,15 @@ dependencies = [ { name = "python-dateutil" }, { name = "typing-inspection" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/f2/5a2fdcbee80e83674fab398cedbfa97e0b35f316fadd6ff85c37c8054e84/mistralai-2.7.2.tar.gz", hash = "sha256:184b7a502e69cc7cdeb0b095020d7e9407a9544c4496f0317984263dac3d7939", size = 535414, upload-time = "2026-07-24T13:06:55.577Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/f2/5a2fdcbee80e83674fab398cedbfa97e0b35f316fadd6ff85c37c8054e84/mistralai-2.7.2.tar.gz", hash = "sha256:184b7a502e69cc7cdeb0b095020d7e9407a9544c4496f0317984263dac3d7939", size = 535414 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/40/e2c0cca6da63716a384776a9e348ebcdb693e04a06e3c9db189439d0077c/mistralai-2.7.2-py3-none-any.whl", hash = "sha256:b2b26fa43670d6247ea352c23fe821605ecbbcf305755b629e804dbd3f6fd439", size = 1273387, upload-time = "2026-07-24T13:06:53.649Z" }, + { url = "https://files.pythonhosted.org/packages/82/40/e2c0cca6da63716a384776a9e348ebcdb693e04a06e3c9db189439d0077c/mistralai-2.7.2-py3-none-any.whl", hash = "sha256:b2b26fa43670d6247ea352c23fe821605ecbbcf305755b629e804dbd3f6fd439", size = 1273387 }, ] [[package]] name = "mkdocs" version = "1.6.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -2770,43 +2556,43 @@ dependencies = [ { name = "pyyaml-env-tag" }, { name = "watchdog" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, ] [[package]] name = "mkdocs-autorefs" version = "1.4.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "markupsafe" }, { name = "mkdocs" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588, upload-time = "2026-02-10T15:23:55.105Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530, upload-time = "2026-02-10T15:23:53.817Z" }, + { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530 }, ] [[package]] name = "mkdocs-get-deps" version = "0.2.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mergedeep" }, { name = "platformdirs" }, { name = "pyyaml" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, + { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555 }, ] [[package]] name = "mkdocs-material" version = "9.7.7" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "babel" }, { name = "backrefs" }, @@ -2820,24 +2606,24 @@ dependencies = [ { name = "pymdown-extensions" }, { name = "requests" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/cd/c05d3a530ba7934f144fb45f7203cd236adc25c7bdcc34673d202f4b0278/mkdocs_material-9.7.7.tar.gz", hash = "sha256:c0649c065b1b0512d60aad8c10f947f8e455284475239b364b610f2deb4d0855", size = 4097923, upload-time = "2026-07-17T16:21:33.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/cd/c05d3a530ba7934f144fb45f7203cd236adc25c7bdcc34673d202f4b0278/mkdocs_material-9.7.7.tar.gz", hash = "sha256:c0649c065b1b0512d60aad8c10f947f8e455284475239b364b610f2deb4d0855", size = 4097923 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/21/17c1bc9e6f47c972ad66fb2ac2568f99f90f1207eeb6fc3b34d094dba7b5/mkdocs_material-9.7.7-py3-none-any.whl", hash = "sha256:8ea9bb1737a5b524a5f9dcf2e1b4ebda8274ae3008aa7845720a97083bef708f", size = 9305438, upload-time = "2026-07-17T16:21:30.017Z" }, + { url = "https://files.pythonhosted.org/packages/ad/21/17c1bc9e6f47c972ad66fb2ac2568f99f90f1207eeb6fc3b34d094dba7b5/mkdocs_material-9.7.7-py3-none-any.whl", hash = "sha256:8ea9bb1737a5b524a5f9dcf2e1b4ebda8274ae3008aa7845720a97083bef708f", size = 9305438 }, ] [[package]] name = "mkdocs-material-extensions" version = "1.3.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31" }, + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, ] [[package]] name = "mkdocstrings" version = "1.0.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jinja2" }, { name = "markdown" }, @@ -2846,9 +2632,9 @@ dependencies = [ { name = "mkdocs-autorefs" }, { name = "pymdown-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/71/f85bdf13355073ae15a7375f09879375a830553552e58c1c4b7e0bbc5c8b/mkdocstrings-1.0.6.tar.gz", hash = "sha256:a0b8c2bdd29a6416c80d717aa369bbf7831946bd9f23c2a66db1b1dbe7693dbd", size = 100649, upload-time = "2026-07-11T19:38:05.732Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/71/f85bdf13355073ae15a7375f09879375a830553552e58c1c4b7e0bbc5c8b/mkdocstrings-1.0.6.tar.gz", hash = "sha256:a0b8c2bdd29a6416c80d717aa369bbf7831946bd9f23c2a66db1b1dbe7693dbd", size = 100649 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/5b/4c1902e8bdd5c4db63284e9d101dece4038d4025d6d88850ffe0a1578980/mkdocstrings-1.0.6-py3-none-any.whl", hash = "sha256:2703708697487d1b6d6d7b412e176fa436edf120c1bf81dc9e126b12d00893c7", size = 35787, upload-time = "2026-07-11T19:38:04.417Z" }, + { url = "https://files.pythonhosted.org/packages/5d/5b/4c1902e8bdd5c4db63284e9d101dece4038d4025d6d88850ffe0a1578980/mkdocstrings-1.0.6-py3-none-any.whl", hash = "sha256:2703708697487d1b6d6d7b412e176fa436edf120c1bf81dc9e126b12d00893c7", size = 35787 }, ] [package.optional-dependencies] @@ -2859,36 +2645,36 @@ python = [ [[package]] name = "mkdocstrings-python" version = "2.0.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "griffelib" }, { name = "mkdocs-autorefs" }, { name = "mkdocstrings" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/b6/e858701499d57eee8b3fd8e78168083956c6683ddbe727b46758b19e1119/mkdocstrings_python-2.0.5.tar.gz", hash = "sha256:3a4d92556ad39637e88af94a5374213af9a8e3040c3824ceaed04b486c017594", size = 199578, upload-time = "2026-06-19T10:41:08.868Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/b6/e858701499d57eee8b3fd8e78168083956c6683ddbe727b46758b19e1119/mkdocstrings_python-2.0.5.tar.gz", hash = "sha256:3a4d92556ad39637e88af94a5374213af9a8e3040c3824ceaed04b486c017594", size = 199578 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/fc/10ab7e80650a9c9e8f4f1105f8c8e73567f88ed0c06ada589ab81d38687c/mkdocstrings_python-2.0.5-py3-none-any.whl", hash = "sha256:30c837bbff016549f659fcba6539ac351303f0fd7e713c89a040611072236e9d", size = 104951, upload-time = "2026-06-19T10:41:07.378Z" }, + { url = "https://files.pythonhosted.org/packages/d1/fc/10ab7e80650a9c9e8f4f1105f8c8e73567f88ed0c06ada589ab81d38687c/mkdocstrings_python-2.0.5-py3-none-any.whl", hash = "sha256:30c837bbff016549f659fcba6539ac351303f0fd7e713c89a040611072236e9d", size = 104951 }, ] [[package]] name = "mkdown" version = "1.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv" }, { name = "pydantic" }, { name = "schemez" }, { name = "upathtools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/2f/7c01c56d2ece11838505baeb7169b1d37593be8dbc9958a665660e401ce1/mkdown-1.0.1.tar.gz", hash = "sha256:3d0591f1ff16d513eefa36e3f9de4e52121e2c5dccc35b583a803e994d9f7125", size = 16373, upload-time = "2025-10-07T20:15:17.771Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/2f/7c01c56d2ece11838505baeb7169b1d37593be8dbc9958a665660e401ce1/mkdown-1.0.1.tar.gz", hash = "sha256:3d0591f1ff16d513eefa36e3f9de4e52121e2c5dccc35b583a803e994d9f7125", size = 16373 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/34/29cac5028652780cb9709944c5244660c30011812ade255cd4d7104402c8/mkdown-1.0.1-py3-none-any.whl", hash = "sha256:5d027f59f333670617e7f500c79377f4c022ec28be4500732f2a5750113da7e9", size = 18086, upload-time = "2025-10-07T20:15:16.512Z" }, + { url = "https://files.pythonhosted.org/packages/0d/34/29cac5028652780cb9709944c5244660c30011812ade255cd4d7104402c8/mkdown-1.0.1-py3-none-any.whl", hash = "sha256:5d027f59f333670617e7f500c79377f4c022ec28be4500732f2a5750113da7e9", size = 18086 }, ] [[package]] name = "mknodes" version = "2.2.14" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agentpool" }, { name = "anybadge" }, @@ -2918,117 +2704,117 @@ dependencies = [ { name = "yamling" }, { name = "zensical" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/4d/0fc0269801c55d3d434b391cd99b518a7e5a59739376b3db23ff6037cd55/mknodes-2.2.14.tar.gz", hash = "sha256:ab572a394121b309c05f572ad96f14e9c6defab5a3f7dfe7e8d95ea8168be4c6", size = 342702, upload-time = "2026-03-06T00:53:15.047Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/4d/0fc0269801c55d3d434b391cd99b518a7e5a59739376b3db23ff6037cd55/mknodes-2.2.14.tar.gz", hash = "sha256:ab572a394121b309c05f572ad96f14e9c6defab5a3f7dfe7e8d95ea8168be4c6", size = 342702 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/65/8e0828b326c2b50e95ba5d2dd5e9fe807acf45bdf96482e0d6d09deee8be/mknodes-2.2.14-py3-none-any.whl", hash = "sha256:ce544198ff5fcd29ad6b258877e8cbe76ea86cd9e4fde4fbb3f6afb843681baa", size = 472582, upload-time = "2026-03-06T00:53:11.905Z" }, + { url = "https://files.pythonhosted.org/packages/83/65/8e0828b326c2b50e95ba5d2dd5e9fe807acf45bdf96482e0d6d09deee8be/mknodes-2.2.14-py3-none-any.whl", hash = "sha256:ce544198ff5fcd29ad6b258877e8cbe76ea86cd9e4fde4fbb3f6afb843681baa", size = 472582 }, ] [[package]] name = "mmh3" version = "5.2.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/1a/edb23803a168f070ded7a3014c6d706f63b90c84ccc024f89d794a3b7a6d/mmh3-5.2.1.tar.gz", hash = "sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad", size = 33775, upload-time = "2026-03-05T15:55:57.716Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/a5/9daa0508a1569a54130f6198d5462a92deda870043624aa3ea72721aa765/mmh3-5.2.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e", size = 40832, upload-time = "2026-03-05T15:54:43.212Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/6b/3230c6d80c1f4b766dedf280a92c2241e99f87c1504ff74205ec8cebe451/mmh3-5.2.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d", size = 41964, upload-time = "2026-03-05T15:54:44.204Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/fb/648bfddb74a872004b6ee751551bfdda783fe6d70d2e9723bad84dbe5311/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6", size = 39114, upload-time = "2026-03-05T15:54:45.205Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/c2/ab7901f87af438468b496728d11264cb397b3574d41506e71b92128e0373/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f", size = 39819, upload-time = "2026-03-05T15:54:46.509Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/ed/6f88dda0df67de1612f2e130ffea34cf84aaee5bff5b0aff4dbff2babe34/mmh3-5.2.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8", size = 40330, upload-time = "2026-03-05T15:54:47.864Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/66/7516d23f53cdf90f43fce24ab80c28f45e6851d78b46bef8c02084edf583/mmh3-5.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6", size = 56078, upload-time = "2026-03-05T15:54:48.9Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/34/4d152fdf4a91a132cb226b671f11c6b796eada9ab78080fb5ce1e95adaab/mmh3-5.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9", size = 40498, upload-time = "2026-03-05T15:54:49.942Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/4c/8e3af1b6d85a299767ec97bd923f12b06267089c1472c27c1696870d1175/mmh3-5.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03", size = 40033, upload-time = "2026-03-05T15:54:50.994Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/f2/966ea560e32578d453c9e9db53d602cbb1d0da27317e232afa7c38ceba11/mmh3-5.2.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b", size = 97320, upload-time = "2026-03-05T15:54:52.072Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/0d/2c5f9893b38aeb6b034d1a44ecd55a010148054f6a516abe53b5e4057297/mmh3-5.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5", size = 103299, upload-time = "2026-03-05T15:54:53.569Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/fc/2ebaef4a4d4376f89761274dc274035ffd96006ab496b4ee5af9b08f21a9/mmh3-5.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593", size = 106222, upload-time = "2026-03-05T15:54:55.092Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/09/ea7ffe126d0ba0406622602a2d05e1e1a6841cc92fc322eb576c95b27fad/mmh3-5.2.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4", size = 113048, upload-time = "2026-03-05T15:54:56.305Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/57/9447032edf93a64aa9bef4d9aa596400b1756f40411890f77a284f6293ca/mmh3-5.2.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1", size = 120742, upload-time = "2026-03-05T15:54:57.453Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/82/a86cc87cc88c92e9e1a598fee509f0409435b57879a6129bf3b3e40513c7/mmh3-5.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104", size = 99132, upload-time = "2026-03-05T15:54:58.583Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/f7/6b16eb1b40ee89bb740698735574536bc20d6cdafc65ae702ea235578e05/mmh3-5.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d", size = 98686, upload-time = "2026-03-05T15:55:00.078Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/88/a601e9f32ad1410f438a6d0544298ea621f989bd34a0731a7190f7dec799/mmh3-5.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f", size = 106479, upload-time = "2026-03-05T15:55:01.532Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/5c/ce29ae3dfc4feec4007a437a1b7435fb9507532a25147602cd5b52be86db/mmh3-5.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2", size = 110030, upload-time = "2026-03-05T15:55:02.934Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/30/ae444ef2ff87c805d525da4fa63d27cda4fe8a48e77003a036b8461cfd5c/mmh3-5.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a", size = 97536, upload-time = "2026-03-05T15:55:04.135Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/f9/dc3787ee5c813cc27fe79f45ad4500d9b5437f23a7402435cc34e07c7718/mmh3-5.2.1-cp313-cp313-win32.whl", hash = "sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b", size = 40769, upload-time = "2026-03-05T15:55:05.277Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/67/850e0b5a1e97799822ebfc4ca0e8c6ece3ed8baf7dcdf64de817dfdda2ca/mmh3-5.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229", size = 41563, upload-time = "2026-03-05T15:55:06.283Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/cc/98c90b28e1da5458e19fbfaf4adb5289208d3bfccd45dd14eab216a2f0bb/mmh3-5.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d", size = 39310, upload-time = "2026-03-05T15:55:07.323Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/1a/edb23803a168f070ded7a3014c6d706f63b90c84ccc024f89d794a3b7a6d/mmh3-5.2.1.tar.gz", hash = "sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad", size = 33775 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/a5/9daa0508a1569a54130f6198d5462a92deda870043624aa3ea72721aa765/mmh3-5.2.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e", size = 40832 }, + { url = "https://files.pythonhosted.org/packages/0a/6b/3230c6d80c1f4b766dedf280a92c2241e99f87c1504ff74205ec8cebe451/mmh3-5.2.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d", size = 41964 }, + { url = "https://files.pythonhosted.org/packages/62/fb/648bfddb74a872004b6ee751551bfdda783fe6d70d2e9723bad84dbe5311/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6", size = 39114 }, + { url = "https://files.pythonhosted.org/packages/95/c2/ab7901f87af438468b496728d11264cb397b3574d41506e71b92128e0373/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f", size = 39819 }, + { url = "https://files.pythonhosted.org/packages/2f/ed/6f88dda0df67de1612f2e130ffea34cf84aaee5bff5b0aff4dbff2babe34/mmh3-5.2.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8", size = 40330 }, + { url = "https://files.pythonhosted.org/packages/3d/66/7516d23f53cdf90f43fce24ab80c28f45e6851d78b46bef8c02084edf583/mmh3-5.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6", size = 56078 }, + { url = "https://files.pythonhosted.org/packages/bc/34/4d152fdf4a91a132cb226b671f11c6b796eada9ab78080fb5ce1e95adaab/mmh3-5.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9", size = 40498 }, + { url = "https://files.pythonhosted.org/packages/d4/4c/8e3af1b6d85a299767ec97bd923f12b06267089c1472c27c1696870d1175/mmh3-5.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03", size = 40033 }, + { url = "https://files.pythonhosted.org/packages/8b/f2/966ea560e32578d453c9e9db53d602cbb1d0da27317e232afa7c38ceba11/mmh3-5.2.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b", size = 97320 }, + { url = "https://files.pythonhosted.org/packages/bb/0d/2c5f9893b38aeb6b034d1a44ecd55a010148054f6a516abe53b5e4057297/mmh3-5.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5", size = 103299 }, + { url = "https://files.pythonhosted.org/packages/1c/fc/2ebaef4a4d4376f89761274dc274035ffd96006ab496b4ee5af9b08f21a9/mmh3-5.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593", size = 106222 }, + { url = "https://files.pythonhosted.org/packages/57/09/ea7ffe126d0ba0406622602a2d05e1e1a6841cc92fc322eb576c95b27fad/mmh3-5.2.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4", size = 113048 }, + { url = "https://files.pythonhosted.org/packages/85/57/9447032edf93a64aa9bef4d9aa596400b1756f40411890f77a284f6293ca/mmh3-5.2.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1", size = 120742 }, + { url = "https://files.pythonhosted.org/packages/53/82/a86cc87cc88c92e9e1a598fee509f0409435b57879a6129bf3b3e40513c7/mmh3-5.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104", size = 99132 }, + { url = "https://files.pythonhosted.org/packages/54/f7/6b16eb1b40ee89bb740698735574536bc20d6cdafc65ae702ea235578e05/mmh3-5.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d", size = 98686 }, + { url = "https://files.pythonhosted.org/packages/e8/88/a601e9f32ad1410f438a6d0544298ea621f989bd34a0731a7190f7dec799/mmh3-5.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f", size = 106479 }, + { url = "https://files.pythonhosted.org/packages/d6/5c/ce29ae3dfc4feec4007a437a1b7435fb9507532a25147602cd5b52be86db/mmh3-5.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2", size = 110030 }, + { url = "https://files.pythonhosted.org/packages/13/30/ae444ef2ff87c805d525da4fa63d27cda4fe8a48e77003a036b8461cfd5c/mmh3-5.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a", size = 97536 }, + { url = "https://files.pythonhosted.org/packages/4b/f9/dc3787ee5c813cc27fe79f45ad4500d9b5437f23a7402435cc34e07c7718/mmh3-5.2.1-cp313-cp313-win32.whl", hash = "sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b", size = 40769 }, + { url = "https://files.pythonhosted.org/packages/43/67/850e0b5a1e97799822ebfc4ca0e8c6ece3ed8baf7dcdf64de817dfdda2ca/mmh3-5.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229", size = 41563 }, + { url = "https://files.pythonhosted.org/packages/c0/cc/98c90b28e1da5458e19fbfaf4adb5289208d3bfccd45dd14eab216a2f0bb/mmh3-5.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d", size = 39310 }, ] [[package]] name = "more-itertools" version = "11.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772, upload-time = "2026-05-22T14:14:29.909Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226 }, ] [[package]] name = "msgpack" version = "1.2.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/f9/c0a1c127f9049db9155afc316952ea571720dd01833ff5e4d7e8e6352dbb/msgpack-1.2.1.tar.gz", hash = "sha256:04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647", size = 183960, upload-time = "2026-06-18T16:13:52.594Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/f9/c0a1c127f9049db9155afc316952ea571720dd01833ff5e4d7e8e6352dbb/msgpack-1.2.1.tar.gz", hash = "sha256:04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647", size = 183960 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/ac/dcddcab6f6c20ecb387ca5e980371cdb3f87ff69aeca388be97eebc4c074/msgpack-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a70e3cf2804a300d921bb0940426e35f4e489a23adfb77a808892241db0a064", size = 83151, upload-time = "2026-06-18T16:13:12.173Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/71/fbcfa83a1d6a9c6091942d1cfd070962244664b87427a9a49a6897b1b219/msgpack-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:491cc39455ca765fad51fb451bf2915eb2cf41192ab5801ce8d67c1d614fe056", size = 82351, upload-time = "2026-06-18T16:13:13.194Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/10/ddf7b06db879e8792d13934ddda09ff20bd2a583fd84c9b59aae9b0e650b/msgpack-1.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f310233ef7fb9c14e201c93639fe5f5260b005f56f0b29048e999c30935596cc", size = 407518, upload-time = "2026-06-18T16:13:14.233Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/d3/36a46a8ed992b781acbc05928bd5bee3c810cb0c3563bf81a7b0c04a1a76/msgpack-1.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:787c9bebb5833e8f6fc8abca3c0597683d8d87f56a8842b6b89c75a5f3176e2d", size = 416405, upload-time = "2026-06-18T16:13:15.435Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/84/e8e9598b557c0ba6ddae901a73780a4c75ac667dddf59414b1e56a42fb34/msgpack-1.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc871b997a9370d855b7394465f2f350e847a5b806dd38dcc9c989e7d87da155", size = 376257, upload-time = "2026-06-18T16:13:17.022Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/16/738fe6d875ad7e2a9429c165322a4ec088f4f273cdfae63d96a89c467961/msgpack-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85f57e960d877f2977f6430896191b04a21f8901b3b4baf2e4604329f4db5402", size = 397469, upload-time = "2026-06-18T16:13:18.287Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/be/6d5952df75a7f24f35833af764c3a6860780364cb3a0030beb8099e1b2b4/msgpack-1.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1233ee2dd0cefba127583de50ea654677277047d238303521db35def3d7b2e7c", size = 372802, upload-time = "2026-06-18T16:13:19.685Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/39/e2ef7dbf0473bcb8dc7c50bf782a892d67414877b63e47fc88eb189ef5e6/msgpack-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3dc2feb0876209d9c38aa56cb1de169bd6c4348f1aa48271f241226590993e6", size = 411273, upload-time = "2026-06-18T16:13:21.028Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/c5/133f4512a56e983a93445c836c9d94d88f3bc2e0980ff4b9e577bd8416ce/msgpack-1.2.1-cp313-cp313-win32.whl", hash = "sha256:6d09badf350af2be9d189184e04e64cf54ad93569ab3d96fca58bd3e84aad707", size = 64471, upload-time = "2026-06-18T16:13:22.293Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/98/577e10b055096a7dd40732358cabaf7180a20c79ed1dcdbb618e4b9deac7/msgpack-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:33f14fba63278b714efe6ad07e50ea5f03d91537aa6a1c5f1ceca4cf44013ca9", size = 71274, upload-time = "2026-06-18T16:13:23.455Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/ee/0c0048e7cfbef23c6a94791b8959ab28155232e7956de8a305b5ff588f05/msgpack-1.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc5febcd4c99effbc02b528e49d6fd0760b2b7d48c05239e345a5fa6e743d9a", size = 64795, upload-time = "2026-06-18T16:13:24.687Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ac/dcddcab6f6c20ecb387ca5e980371cdb3f87ff69aeca388be97eebc4c074/msgpack-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a70e3cf2804a300d921bb0940426e35f4e489a23adfb77a808892241db0a064", size = 83151 }, + { url = "https://files.pythonhosted.org/packages/64/71/fbcfa83a1d6a9c6091942d1cfd070962244664b87427a9a49a6897b1b219/msgpack-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:491cc39455ca765fad51fb451bf2915eb2cf41192ab5801ce8d67c1d614fe056", size = 82351 }, + { url = "https://files.pythonhosted.org/packages/e3/10/ddf7b06db879e8792d13934ddda09ff20bd2a583fd84c9b59aae9b0e650b/msgpack-1.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f310233ef7fb9c14e201c93639fe5f5260b005f56f0b29048e999c30935596cc", size = 407518 }, + { url = "https://files.pythonhosted.org/packages/79/d3/36a46a8ed992b781acbc05928bd5bee3c810cb0c3563bf81a7b0c04a1a76/msgpack-1.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:787c9bebb5833e8f6fc8abca3c0597683d8d87f56a8842b6b89c75a5f3176e2d", size = 416405 }, + { url = "https://files.pythonhosted.org/packages/f9/84/e8e9598b557c0ba6ddae901a73780a4c75ac667dddf59414b1e56a42fb34/msgpack-1.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc871b997a9370d855b7394465f2f350e847a5b806dd38dcc9c989e7d87da155", size = 376257 }, + { url = "https://files.pythonhosted.org/packages/40/16/738fe6d875ad7e2a9429c165322a4ec088f4f273cdfae63d96a89c467961/msgpack-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85f57e960d877f2977f6430896191b04a21f8901b3b4baf2e4604329f4db5402", size = 397469 }, + { url = "https://files.pythonhosted.org/packages/ca/be/6d5952df75a7f24f35833af764c3a6860780364cb3a0030beb8099e1b2b4/msgpack-1.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1233ee2dd0cefba127583de50ea654677277047d238303521db35def3d7b2e7c", size = 372802 }, + { url = "https://files.pythonhosted.org/packages/e1/39/e2ef7dbf0473bcb8dc7c50bf782a892d67414877b63e47fc88eb189ef5e6/msgpack-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3dc2feb0876209d9c38aa56cb1de169bd6c4348f1aa48271f241226590993e6", size = 411273 }, + { url = "https://files.pythonhosted.org/packages/ef/c5/133f4512a56e983a93445c836c9d94d88f3bc2e0980ff4b9e577bd8416ce/msgpack-1.2.1-cp313-cp313-win32.whl", hash = "sha256:6d09badf350af2be9d189184e04e64cf54ad93569ab3d96fca58bd3e84aad707", size = 64471 }, + { url = "https://files.pythonhosted.org/packages/e2/98/577e10b055096a7dd40732358cabaf7180a20c79ed1dcdbb618e4b9deac7/msgpack-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:33f14fba63278b714efe6ad07e50ea5f03d91537aa6a1c5f1ceca4cf44013ca9", size = 71274 }, + { url = "https://files.pythonhosted.org/packages/ba/ee/0c0048e7cfbef23c6a94791b8959ab28155232e7956de8a305b5ff588f05/msgpack-1.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc5febcd4c99effbc02b528e49d6fd0760b2b7d48c05239e345a5fa6e743d9a", size = 64795 }, ] [[package]] name = "multidict" version = "6.7.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174 }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116 }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524 }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368 }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952 }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317 }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132 }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140 }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277 }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291 }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156 }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742 }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221 }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664 }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490 }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695 }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884 }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122 }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175 }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460 }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930 }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582 }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031 }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596 }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492 }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899 }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970 }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060 }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888 }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554 }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341 }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391 }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422 }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770 }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109 }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573 }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319 }, ] [[package]] name = "mypy" version = "2.3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ast-serialize" }, { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, @@ -3036,16 +2822,16 @@ dependencies = [ { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/af/4e516a05d3ca2eb9283e9ec45b2c02225c1514dd6da49fd3c9eaa6639370/mypy-2.3.0.tar.gz", hash = "sha256:465965d41cd9a2726694e983e8ce7113259327bec798115d1e1dfa2a52fb666e", size = 3988104, upload-time = "2026-07-13T11:34:53.387Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/af/4e516a05d3ca2eb9283e9ec45b2c02225c1514dd6da49fd3c9eaa6639370/mypy-2.3.0.tar.gz", hash = "sha256:465965d41cd9a2726694e983e8ce7113259327bec798115d1e1dfa2a52fb666e", size = 3988104 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/ae/f7d056eb0294586a572d0d0d89580ec633c064db520f11d37d5a2fb833bd/mypy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:91ad22a52ae2c7e621c2f67c94d5a17f66b3209a4cff5cf8a573579835c69e97", size = 14947298, upload-time = "2026-07-13T11:27:47.734Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/d5/db3e7af01e7844d21662c6ddc1f7825ec7cb4053f0391ac02faf3638396f/mypy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99ac767cc5d3b64c8d0ae226ead10c96694f94e4e7da1668642225dcd4e75aac", size = 13950768, upload-time = "2026-07-13T11:27:57.726Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/fb/43c031f0190513d1ec248ed037eceb742ddd2a4d74bbf406658a28173837/mypy-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6d2c484742a4d7b0ed6d07b143375624d3b899c5749c7b3c947f56261f48a6", size = 14151586, upload-time = "2026-07-13T11:29:18.615Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/c3/f8b2ffc60883084da91be51af58e88a7ffd4ff9795acb7d902ff88d31eb1/mypy-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7da939dd335cfd2ad788bdfd081c9f4e47634ab995e5a45eb15fd1e5bc052f8b", size = 15227411, upload-time = "2026-07-13T11:30:29.904Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/2e/16b917fc7adcf03f1aadddfc93aab804ffb234b1ab09c0ffd6d92a5d34a2/mypy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7247eb2824f996722a949530183394921ca71deb9680052a338cf53cff7925c2", size = 15478790, upload-time = "2026-07-13T11:33:14.686Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/88/aaa65a93c73d0cdae7e42f8adb302bf6885bb281302084f99d0290a35347/mypy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:75b0984bb3cbd76bb5c9291a8671f7ae66ca3b51c7584c358fc2e923259f0757", size = 11234919, upload-time = "2026-07-13T11:33:39.28Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/19/b40de63f1a80e63bc2d40f0679a6a8dbd34e95176c8122119bdf406aa552/mypy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:d78fcf900b59cb7e82cb7e3a235e31b462d9333d92285bd1e4952d355b8ffba1", size = 10201510, upload-time = "2026-07-13T11:31:52.619Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/fa/fdc54fe583ba3cafbcedfb70eeeaf03849f75b1827a07096c7bd996f582d/mypy-2.3.0-py3-none-any.whl", hash = "sha256:6b1cdb579446b60432432b2b2403a6201b4b475a004d7f488511c9ba177c9e88", size = 2753292, upload-time = "2026-07-13T11:33:18.48Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ae/f7d056eb0294586a572d0d0d89580ec633c064db520f11d37d5a2fb833bd/mypy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:91ad22a52ae2c7e621c2f67c94d5a17f66b3209a4cff5cf8a573579835c69e97", size = 14947298 }, + { url = "https://files.pythonhosted.org/packages/32/d5/db3e7af01e7844d21662c6ddc1f7825ec7cb4053f0391ac02faf3638396f/mypy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99ac767cc5d3b64c8d0ae226ead10c96694f94e4e7da1668642225dcd4e75aac", size = 13950768 }, + { url = "https://files.pythonhosted.org/packages/d9/fb/43c031f0190513d1ec248ed037eceb742ddd2a4d74bbf406658a28173837/mypy-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6d2c484742a4d7b0ed6d07b143375624d3b899c5749c7b3c947f56261f48a6", size = 14151586 }, + { url = "https://files.pythonhosted.org/packages/ec/c3/f8b2ffc60883084da91be51af58e88a7ffd4ff9795acb7d902ff88d31eb1/mypy-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7da939dd335cfd2ad788bdfd081c9f4e47634ab995e5a45eb15fd1e5bc052f8b", size = 15227411 }, + { url = "https://files.pythonhosted.org/packages/83/2e/16b917fc7adcf03f1aadddfc93aab804ffb234b1ab09c0ffd6d92a5d34a2/mypy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7247eb2824f996722a949530183394921ca71deb9680052a338cf53cff7925c2", size = 15478790 }, + { url = "https://files.pythonhosted.org/packages/c0/88/aaa65a93c73d0cdae7e42f8adb302bf6885bb281302084f99d0290a35347/mypy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:75b0984bb3cbd76bb5c9291a8671f7ae66ca3b51c7584c358fc2e923259f0757", size = 11234919 }, + { url = "https://files.pythonhosted.org/packages/35/19/b40de63f1a80e63bc2d40f0679a6a8dbd34e95176c8122119bdf406aa552/mypy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:d78fcf900b59cb7e82cb7e3a235e31b462d9333d92285bd1e4952d355b8ffba1", size = 10201510 }, + { url = "https://files.pythonhosted.org/packages/2c/fa/fdc54fe583ba3cafbcedfb70eeeaf03849f75b1827a07096c7bd996f582d/mypy-2.3.0-py3-none-any.whl", hash = "sha256:6b1cdb579446b60432432b2b2403a6201b4b475a004d7f488511c9ba177c9e88", size = 2753292 }, ] [package.optional-dependencies] @@ -3056,31 +2842,31 @@ faster-cache = [ [[package]] name = "mypy-extensions" version = "1.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, ] [[package]] name = "nab-index" version = "0.0.11" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "truststore" }, { name = "typing-extensions" }, { name = "urllib3" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/26/df8082b11169a8aa0169f67a00085c978f05d5f1a9f0e9895d13085e0220/nab_index-0.0.11.tar.gz", hash = "sha256:cdf3d3868c840896bba56bfc180c5ff5e6f6801ab9697def799208e37b4cf23e", size = 35229, upload-time = "2026-07-19T21:22:15.143Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/26/df8082b11169a8aa0169f67a00085c978f05d5f1a9f0e9895d13085e0220/nab_index-0.0.11.tar.gz", hash = "sha256:cdf3d3868c840896bba56bfc180c5ff5e6f6801ab9697def799208e37b4cf23e", size = 35229 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/00/0816a27374957bc87468e77b9c5f224a8e6d546939d8d672589f7240785e/nab_index-0.0.11-py3-none-any.whl", hash = "sha256:34c7f73f02a714aa3774767ebbf278e4560d50863fc81252f1aab148bd3061fd", size = 41617, upload-time = "2026-07-19T21:22:14.136Z" }, + { url = "https://files.pythonhosted.org/packages/fe/00/0816a27374957bc87468e77b9c5f224a8e6d546939d8d672589f7240785e/nab_index-0.0.11-py3-none-any.whl", hash = "sha256:34c7f73f02a714aa3774767ebbf278e4560d50863fc81252f1aab148bd3061fd", size = 41617 }, ] [[package]] name = "nab-python" version = "0.0.11" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "build" }, { name = "installer" }, @@ -3091,73 +2877,73 @@ dependencies = [ { name = "tomli-w" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/74/6cc630e82b80e255db1d293b1f053ba25d4985d4d8332ca2efb9fac02f78/nab_python-0.0.11.tar.gz", hash = "sha256:c034a432228150afe99c7280d64d4ef738f6182b9079532bc04a2fce94daa2f2", size = 663230, upload-time = "2026-07-19T21:22:23.351Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/74/6cc630e82b80e255db1d293b1f053ba25d4985d4d8332ca2efb9fac02f78/nab_python-0.0.11.tar.gz", hash = "sha256:c034a432228150afe99c7280d64d4ef738f6182b9079532bc04a2fce94daa2f2", size = 663230 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/3f/79932bac990c85a1cf85fa34db9b2a0b98e9bc467203ad233f982ebeeb76/nab_python-0.0.11-py3-none-any.whl", hash = "sha256:92caea881a68862e941533f516f2cae2347c5f3f911ac4cb6fe73ab16dc4f352", size = 350925, upload-time = "2026-07-19T21:22:21.786Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3f/79932bac990c85a1cf85fa34db9b2a0b98e9bc467203ad233f982ebeeb76/nab_python-0.0.11-py3-none-any.whl", hash = "sha256:92caea881a68862e941533f516f2cae2347c5f3f911ac4cb6fe73ab16dc4f352", size = 350925 }, ] [[package]] name = "nab-resolver" version = "0.0.11" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/e4/1c802661870ea6e677b3f48f5751970d5ec4218fe867fe09c9563b13e275/nab_resolver-0.0.11.tar.gz", hash = "sha256:9b32dc5b9936964d00c95e5b5e01ce85a9533b2aa95bd4ed96dd793bac61759c", size = 95136, upload-time = "2026-07-19T21:22:07.389Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/e4/1c802661870ea6e677b3f48f5751970d5ec4218fe867fe09c9563b13e275/nab_resolver-0.0.11.tar.gz", hash = "sha256:9b32dc5b9936964d00c95e5b5e01ce85a9533b2aa95bd4ed96dd793bac61759c", size = 95136 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/cb/e49eed4c8b31daa70b2bb7ab98a56738611fc2e7a82031513144a4eebe5a/nab_resolver-0.0.11-py3-none-any.whl", hash = "sha256:557862e8414658516ae3b3ae4f660dd40dbe13b9c5a16becaed49916bcdc339d", size = 34627, upload-time = "2026-07-19T21:22:06.183Z" }, + { url = "https://files.pythonhosted.org/packages/17/cb/e49eed4c8b31daa70b2bb7ab98a56738611fc2e7a82031513144a4eebe5a/nab_resolver-0.0.11-py3-none-any.whl", hash = "sha256:557862e8414658516ae3b3ae4f660dd40dbe13b9c5a16becaed49916bcdc339d", size = 34627 }, ] [[package]] name = "natsort" version = "8.4.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c" }, + { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, ] [[package]] name = "nest-asyncio" version = "1.6.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, ] [[package]] name = "numpy" version = "2.5.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/fd/89965aa4ac08c74998539fcbf24fa3540f3e15237fbeb6bcf9c908f4aade/numpy-2.5.1.tar.gz", hash = "sha256:a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3", size = 20755553, upload-time = "2026-07-04T17:08:00.933Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/fd/89965aa4ac08c74998539fcbf24fa3540f3e15237fbeb6bcf9c908f4aade/numpy-2.5.1.tar.gz", hash = "sha256:a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3", size = 20755553 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/07/ec2a3f0c91761581d4b7104a740791800025983f9a4dc4e73f91a99aeac4/numpy-2.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0bfebd8695f9863592fe744be833a258120b14a9f39da255e8aa8fade2c0ddd1", size = 16796419, upload-time = "2026-07-04T17:06:40.37Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/ab/ddb499fc4f8780354395face5b65c7fd107bcd6e1d667a5f07d046956f6f/numpy-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30b44a6b53a7ae63c54c089a8726e5563ed302716c5b7ccc85afade40b0e7ff6", size = 11765832, upload-time = "2026-07-04T17:06:42.768Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/b3/3c28c558a09fc72100c646dac6d2fce8e834c471b0edca01a29996706117/numpy-2.5.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6165343f81b56ef8f514f396989e529b61d9dc709b99421b07e9f3e698e2287d", size = 5325143, upload-time = "2026-07-04T17:06:45.466Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/0e/ce19b985bb15c596f4f05954e76cccc77c845083b3b8f938a6c68e523128/numpy-2.5.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4939237038ada79308dda3204ac6462df056b5672b2e25db1149cf873668b3e1", size = 6659749, upload-time = "2026-07-04T17:06:47.288Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/20/1ee6614d64332a1bba6411f38e68cb79eec1b2459e20a623777c5c5492a2/numpy-2.5.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c6759f538fb912fc46de0a6b1758ccf7b57bc7c7ebebc23974fdac3de8db0cd", size = 15164716, upload-time = "2026-07-04T17:06:49.494Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/a7/2bcd3fdbb87804755c35b729bf8709d62025c5f4cfd7d5b2415997097515/numpy-2.5.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9726558e8db4a5bf7929a70ae50f63abda4daf0efe810e3bfbab95976f75fc1a", size = 16661440, upload-time = "2026-07-04T17:06:52.061Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/d7/a41e3310c886fe457d36e670bbf24fae411aca8a7b6ad92a32afd924077c/numpy-2.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3935f3b419b244a02732676fa5317a9193cc596a4c0646db07e5b421229ac9f7", size = 16526305, upload-time = "2026-07-04T17:06:54.605Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/75/4333a9a707c1edd3a4e1a0c58eca52c0f31e55089fa80db02b5565b24df7/numpy-2.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc932a65ded7ce9013d120845a2514dcccb1a67bfc8deb8d37633762951904a6", size = 18423008, upload-time = "2026-07-04T17:06:57.54Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/90/e314a32b1c11a2ffe818ddad3a57b50b4b6e1b6c487192eb50cdef0415d0/numpy-2.5.1-cp313-cp313-win32.whl", hash = "sha256:4b4ff1608417eb7a59da7b967bbb798cacfe071d2caf526a24281cd562072ed9", size = 6063885, upload-time = "2026-07-04T17:07:00.14Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/70/800b3fca480af32df9e8ea9f3d4a0c8feb4b32d7f195d174eabbda4829ad/numpy-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:6c3fe51bc6a16453d452997053454f309e8e0ed7b42d6b361ce4ac8c32913d74", size = 12425674, upload-time = "2026-07-04T17:07:02.387Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/0b/196350c122f50f6ca56846f2d71efd5e0d24b7b2e07355e019b2e2c7a11e/numpy-2.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f7feb014281029e628ba2d5a007407443b06e418b6fe451d1e2adcbc8eba0107", size = 10350256, upload-time = "2026-07-04T17:07:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/eb/07/ec2a3f0c91761581d4b7104a740791800025983f9a4dc4e73f91a99aeac4/numpy-2.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0bfebd8695f9863592fe744be833a258120b14a9f39da255e8aa8fade2c0ddd1", size = 16796419 }, + { url = "https://files.pythonhosted.org/packages/ab/ab/ddb499fc4f8780354395face5b65c7fd107bcd6e1d667a5f07d046956f6f/numpy-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30b44a6b53a7ae63c54c089a8726e5563ed302716c5b7ccc85afade40b0e7ff6", size = 11765832 }, + { url = "https://files.pythonhosted.org/packages/88/b3/3c28c558a09fc72100c646dac6d2fce8e834c471b0edca01a29996706117/numpy-2.5.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6165343f81b56ef8f514f396989e529b61d9dc709b99421b07e9f3e698e2287d", size = 5325143 }, + { url = "https://files.pythonhosted.org/packages/5e/0e/ce19b985bb15c596f4f05954e76cccc77c845083b3b8f938a6c68e523128/numpy-2.5.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4939237038ada79308dda3204ac6462df056b5672b2e25db1149cf873668b3e1", size = 6659749 }, + { url = "https://files.pythonhosted.org/packages/2e/20/1ee6614d64332a1bba6411f38e68cb79eec1b2459e20a623777c5c5492a2/numpy-2.5.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c6759f538fb912fc46de0a6b1758ccf7b57bc7c7ebebc23974fdac3de8db0cd", size = 15164716 }, + { url = "https://files.pythonhosted.org/packages/ed/a7/2bcd3fdbb87804755c35b729bf8709d62025c5f4cfd7d5b2415997097515/numpy-2.5.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9726558e8db4a5bf7929a70ae50f63abda4daf0efe810e3bfbab95976f75fc1a", size = 16661440 }, + { url = "https://files.pythonhosted.org/packages/fc/d7/a41e3310c886fe457d36e670bbf24fae411aca8a7b6ad92a32afd924077c/numpy-2.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3935f3b419b244a02732676fa5317a9193cc596a4c0646db07e5b421229ac9f7", size = 16526305 }, + { url = "https://files.pythonhosted.org/packages/53/75/4333a9a707c1edd3a4e1a0c58eca52c0f31e55089fa80db02b5565b24df7/numpy-2.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc932a65ded7ce9013d120845a2514dcccb1a67bfc8deb8d37633762951904a6", size = 18423008 }, + { url = "https://files.pythonhosted.org/packages/ee/90/e314a32b1c11a2ffe818ddad3a57b50b4b6e1b6c487192eb50cdef0415d0/numpy-2.5.1-cp313-cp313-win32.whl", hash = "sha256:4b4ff1608417eb7a59da7b967bbb798cacfe071d2caf526a24281cd562072ed9", size = 6063885 }, + { url = "https://files.pythonhosted.org/packages/10/70/800b3fca480af32df9e8ea9f3d4a0c8feb4b32d7f195d174eabbda4829ad/numpy-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:6c3fe51bc6a16453d452997053454f309e8e0ed7b42d6b361ce4ac8c32913d74", size = 12425674 }, + { url = "https://files.pythonhosted.org/packages/8b/0b/196350c122f50f6ca56846f2d71efd5e0d24b7b2e07355e019b2e2c7a11e/numpy-2.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f7feb014281029e628ba2d5a007407443b06e418b6fe451d1e2adcbc8eba0107", size = 10350256 }, ] [[package]] name = "oauthlib" version = "3.3.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 }, ] [[package]] name = "onnxruntime" version = "1.27.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flatbuffers" }, { name = "numpy" }, @@ -3165,19 +2951,19 @@ dependencies = [ { name = "protobuf" }, ] wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/2b/54208fd03ad410480bc17edf4869376362da8bbf46fe186ddf4cb5cc20fe/onnxruntime-1.27.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:b3e5b58b8c89c2b20e086e890aa9527377e5c240dc3ecc1640d18e07705eeb1c", size = 18432958, upload-time = "2026-06-15T22:42:53.105Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/88/24fc51fcbb126da6d032372314e47b55c3faad58f2aa78c0e199ccd20b9c/onnxruntime-1.27.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b3d87eb560ff6a772240506f3c78d6d27c63cafedd5c775672e1194f968cfd", size = 16438180, upload-time = "2026-06-15T22:42:43.093Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/19/14929c3c2fe0b79b41cce24463062bf3afa4cdd3c19dccf00319caa92bff/onnxruntime-1.27.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6872443f236a554921cda6f318c900e2d0c226792cf3534d00e5057c6926e5d2", size = 18658445, upload-time = "2026-06-15T22:43:08.053Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/76/59ed932b0244acd7bbbd6449480053a6d958ea66357f022f932872e19287/onnxruntime-1.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:760021bca514d64a811837820d351a08a41741f16f8b4c26450da708fecf14e6", size = 13357856, upload-time = "2026-06-15T22:43:37.315Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/51/d1ec60ec7b1e2ae2d7340ba52b8a13529140039cd4407ba8dddbbc046582/onnxruntime-1.27.0-cp313-cp313-win_arm64.whl", hash = "sha256:2fdfa9df40a0ded0028ce6f9cd863264237f3970559dea2b81456e9ac4622b94", size = 13104412, upload-time = "2026-06-15T22:43:27.457Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/7d/e6bb1c6445c94f708c38cd8fbb7bf0264108c33498b9445c93e60fe6d329/onnxruntime-1.27.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54c0c4e9202c36c4ecdb1f3443f5dfbfd5ee3b54d1362c4b4c6134110e74fb32", size = 16443331, upload-time = "2026-06-15T22:42:45.649Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/1b/b18b31e806eabc41077810199fbbb36fbc2d5f19912416e5ccfbf73053d1/onnxruntime-1.27.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b215aa662c8f983f7d6dedafe65a9be72c26e5338e0fe98b3e0422c32c85428", size = 18670967, upload-time = "2026-06-15T22:43:10.621Z" }, + { url = "https://files.pythonhosted.org/packages/fb/2b/54208fd03ad410480bc17edf4869376362da8bbf46fe186ddf4cb5cc20fe/onnxruntime-1.27.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:b3e5b58b8c89c2b20e086e890aa9527377e5c240dc3ecc1640d18e07705eeb1c", size = 18432958 }, + { url = "https://files.pythonhosted.org/packages/ce/88/24fc51fcbb126da6d032372314e47b55c3faad58f2aa78c0e199ccd20b9c/onnxruntime-1.27.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b3d87eb560ff6a772240506f3c78d6d27c63cafedd5c775672e1194f968cfd", size = 16438180 }, + { url = "https://files.pythonhosted.org/packages/cb/19/14929c3c2fe0b79b41cce24463062bf3afa4cdd3c19dccf00319caa92bff/onnxruntime-1.27.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6872443f236a554921cda6f318c900e2d0c226792cf3534d00e5057c6926e5d2", size = 18658445 }, + { url = "https://files.pythonhosted.org/packages/7f/76/59ed932b0244acd7bbbd6449480053a6d958ea66357f022f932872e19287/onnxruntime-1.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:760021bca514d64a811837820d351a08a41741f16f8b4c26450da708fecf14e6", size = 13357856 }, + { url = "https://files.pythonhosted.org/packages/79/51/d1ec60ec7b1e2ae2d7340ba52b8a13529140039cd4407ba8dddbbc046582/onnxruntime-1.27.0-cp313-cp313-win_arm64.whl", hash = "sha256:2fdfa9df40a0ded0028ce6f9cd863264237f3970559dea2b81456e9ac4622b94", size = 13104412 }, + { url = "https://files.pythonhosted.org/packages/5e/7d/e6bb1c6445c94f708c38cd8fbb7bf0264108c33498b9445c93e60fe6d329/onnxruntime-1.27.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54c0c4e9202c36c4ecdb1f3443f5dfbfd5ee3b54d1362c4b4c6134110e74fb32", size = 16443331 }, + { url = "https://files.pythonhosted.org/packages/72/1b/b18b31e806eabc41077810199fbbb36fbc2d5f19912416e5ccfbf73053d1/onnxruntime-1.27.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b215aa662c8f983f7d6dedafe65a9be72c26e5338e0fe98b3e0422c32c85428", size = 18670967 }, ] [[package]] name = "openai" version = "2.48.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -3188,27 +2974,27 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/ae/d4d1835488c0350424009dac5095b9a3e173bee12fd2e421ee27e2142c42/openai-2.48.0.tar.gz", hash = "sha256:231b1e7661dda14574986c2f71451e9d584b7fe69e0ee6480e12ed090b48fc16", size = 1093427, upload-time = "2026-07-23T20:15:50.402Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/ae/d4d1835488c0350424009dac5095b9a3e173bee12fd2e421ee27e2142c42/openai-2.48.0.tar.gz", hash = "sha256:231b1e7661dda14574986c2f71451e9d584b7fe69e0ee6480e12ed090b48fc16", size = 1093427 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/2a/dcb891114e303c4379d4a498f10222e33eee540bcef4e1e493bd0af2b242/openai-2.48.0-py3-none-any.whl", hash = "sha256:c98df30aaaf93c51979f64d3e7c5b76464f8be0173368266229eb8fe6bd30f2c", size = 1648520, upload-time = "2026-07-23T20:15:48.052Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2a/dcb891114e303c4379d4a498f10222e33eee540bcef4e1e493bd0af2b242/openai-2.48.0-py3-none-any.whl", hash = "sha256:c98df30aaaf93c51979f64d3e7c5b76464f8be0173368266229eb8fe6bd30f2c", size = 1648520 }, ] [[package]] name = "openapi-pydantic" version = "0.5.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 }, ] [[package]] name = "openapi-schema-validator" version = "0.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonschema" }, { name = "jsonschema-specifications" }, @@ -3217,15 +3003,15 @@ dependencies = [ { name = "referencing" }, { name = "rfc3339-validator" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/e8/ab3f27dbca54ec645f7fab714b640907d5d36c2ebb07e87eebd30bd5c81b/openapi_schema_validator-0.9.0.tar.gz", hash = "sha256:b72db64315b89d21834cd3ffef37e3e6893bc876327be2d366e8424b1029afd3", size = 24686, upload-time = "2026-04-27T17:31:27.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/e8/ab3f27dbca54ec645f7fab714b640907d5d36c2ebb07e87eebd30bd5c81b/openapi_schema_validator-0.9.0.tar.gz", hash = "sha256:b72db64315b89d21834cd3ffef37e3e6893bc876327be2d366e8424b1029afd3", size = 24686 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/c0/5467967d95378b2cfce312e09cbd0c9ab64354a0922379b734f793edd04f/openapi_schema_validator-0.9.0-py3-none-any.whl", hash = "sha256:faa3bbe7c3aa8ca2087ad83f709dc3b7d920283153a570c03e24ea182558aa25", size = 19980, upload-time = "2026-04-27T17:31:25.965Z" }, + { url = "https://files.pythonhosted.org/packages/90/c0/5467967d95378b2cfce312e09cbd0c9ab64354a0922379b734f793edd04f/openapi_schema_validator-0.9.0-py3-none-any.whl", hash = "sha256:faa3bbe7c3aa8ca2087ad83f709dc3b7d920283153a570c03e24ea182558aa25", size = 19980 }, ] [[package]] name = "openapi-spec-validator" version = "0.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonschema" }, { name = "jsonschema-path" }, @@ -3234,40 +3020,40 @@ dependencies = [ { name = "pydantic" }, { name = "pydantic-settings" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/d2/640b5149cd5688bc0ad1fdbb4df6a2f7b84a093c8d787c27d566132f8b8b/openapi_spec_validator-0.9.0.tar.gz", hash = "sha256:6d648cff6490ebb799dcfe273792f2941c050158854c721f086599d845da78b8", size = 1756839, upload-time = "2026-05-20T09:23:18.871Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/d2/640b5149cd5688bc0ad1fdbb4df6a2f7b84a093c8d787c27d566132f8b8b/openapi_spec_validator-0.9.0.tar.gz", hash = "sha256:6d648cff6490ebb799dcfe273792f2941c050158854c721f086599d845da78b8", size = 1756839 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/d8/321ff889330acca2e3097f3d4f80a40bcc41b6d34d302978ab32c449520b/openapi_spec_validator-0.9.0-py3-none-any.whl", hash = "sha256:222fecffc7714f6d0a6ad62c0e4b66cc2b7dbfafb7b93acfc6c308abbdb51af8", size = 50328, upload-time = "2026-05-20T09:23:17.017Z" }, + { url = "https://files.pythonhosted.org/packages/95/d8/321ff889330acca2e3097f3d4f80a40bcc41b6d34d302978ab32c449520b/openapi_spec_validator-0.9.0-py3-none-any.whl", hash = "sha256:222fecffc7714f6d0a6ad62c0e4b66cc2b7dbfafb7b93acfc6c308abbdb51af8", size = 50328 }, ] [[package]] name = "opentelemetry-api" version = "1.39.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, + { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" version = "1.39.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409, upload-time = "2025-12-11T13:32:40.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366, upload-time = "2025-12-11T13:32:20.2Z" }, + { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" version = "1.39.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "opentelemetry-api" }, @@ -3277,30 +3063,30 @@ dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/04/2a08fa9c0214ae38880df01e8bfae12b067ec0793446578575e5080d6545/opentelemetry_exporter_otlp_proto_http-1.39.1.tar.gz", hash = "sha256:31bdab9745c709ce90a49a0624c2bd445d31a28ba34275951a6a362d16a0b9cb", size = 17288, upload-time = "2025-12-11T13:32:42.029Z" } +sdist = { url = "https://files.pythonhosted.org/packages/80/04/2a08fa9c0214ae38880df01e8bfae12b067ec0793446578575e5080d6545/opentelemetry_exporter_otlp_proto_http-1.39.1.tar.gz", hash = "sha256:31bdab9745c709ce90a49a0624c2bd445d31a28ba34275951a6a362d16a0b9cb", size = 17288 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/f1/b27d3e2e003cd9a3592c43d099d2ed8d0a947c15281bf8463a256db0b46c/opentelemetry_exporter_otlp_proto_http-1.39.1-py3-none-any.whl", hash = "sha256:d9f5207183dd752a412c4cd564ca8875ececba13be6e9c6c370ffb752fd59985", size = 19641, upload-time = "2025-12-11T13:32:22.248Z" }, + { url = "https://files.pythonhosted.org/packages/95/f1/b27d3e2e003cd9a3592c43d099d2ed8d0a947c15281bf8463a256db0b46c/opentelemetry_exporter_otlp_proto_http-1.39.1-py3-none-any.whl", hash = "sha256:d9f5207183dd752a412c4cd564ca8875ececba13be6e9c6c370ffb752fd59985", size = 19641 }, ] [[package]] name = "opentelemetry-instrumentation" version = "0.60b1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, + { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096 }, ] [[package]] name = "opentelemetry-instrumentation-asgi" version = "0.60b1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, { name = "opentelemetry-api" }, @@ -3308,15 +3094,15 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/db/851fa88db7441da82d50bd80f2de5ee55213782e25dc858e04d0c9961d60/opentelemetry_instrumentation_asgi-0.60b1.tar.gz", hash = "sha256:16bfbe595cd24cda309a957456d0fc2523f41bc7b076d1f2d7e98a1ad9876d6f", size = 26107, upload-time = "2025-12-11T13:36:47.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/db/851fa88db7441da82d50bd80f2de5ee55213782e25dc858e04d0c9961d60/opentelemetry_instrumentation_asgi-0.60b1.tar.gz", hash = "sha256:16bfbe595cd24cda309a957456d0fc2523f41bc7b076d1f2d7e98a1ad9876d6f", size = 26107 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/76/1fb94367cef64420d2171157a6b9509582873bd09a6afe08a78a8d1f59d9/opentelemetry_instrumentation_asgi-0.60b1-py3-none-any.whl", hash = "sha256:d48def2dbed10294c99cfcf41ebbd0c414d390a11773a41f472d20000fcddc25", size = 16933, upload-time = "2025-12-11T13:35:40.462Z" }, + { url = "https://files.pythonhosted.org/packages/76/76/1fb94367cef64420d2171157a6b9509582873bd09a6afe08a78a8d1f59d9/opentelemetry_instrumentation_asgi-0.60b1-py3-none-any.whl", hash = "sha256:d48def2dbed10294c99cfcf41ebbd0c414d390a11773a41f472d20000fcddc25", size = 16933 }, ] [[package]] name = "opentelemetry-instrumentation-fastapi" version = "0.60b1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, @@ -3324,15 +3110,15 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/e7/e7e5e50218cf488377209d85666b182fa2d4928bf52389411ceeee1b2b60/opentelemetry_instrumentation_fastapi-0.60b1.tar.gz", hash = "sha256:de608955f7ff8eecf35d056578346a5365015fd7d8623df9b1f08d1c74769c01", size = 24958, upload-time = "2025-12-11T13:36:59.35Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/e7/e7e5e50218cf488377209d85666b182fa2d4928bf52389411ceeee1b2b60/opentelemetry_instrumentation_fastapi-0.60b1.tar.gz", hash = "sha256:de608955f7ff8eecf35d056578346a5365015fd7d8623df9b1f08d1c74769c01", size = 24958 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/cc/6e808328ba54662e50babdcab21138eae4250bc0fddf67d55526a615a2ca/opentelemetry_instrumentation_fastapi-0.60b1-py3-none-any.whl", hash = "sha256:af94b7a239ad1085fc3a820ecf069f67f579d7faf4c085aaa7bd9b64eafc8eaf", size = 13478, upload-time = "2025-12-11T13:36:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/7d/cc/6e808328ba54662e50babdcab21138eae4250bc0fddf67d55526a615a2ca/opentelemetry_instrumentation_fastapi-0.60b1-py3-none-any.whl", hash = "sha256:af94b7a239ad1085fc3a820ecf069f67f579d7faf4c085aaa7bd9b64eafc8eaf", size = 13478 }, ] [[package]] name = "opentelemetry-instrumentation-httpx" version = "0.60b1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, @@ -3340,269 +3126,269 @@ dependencies = [ { name = "opentelemetry-util-http" }, { name = "wrapt" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/08/11208bcfcab4fc2023252c3f322aa397fd9ad948355fea60f5fc98648603/opentelemetry_instrumentation_httpx-0.60b1.tar.gz", hash = "sha256:a506ebaf28c60112cbe70ad4f0338f8603f148938cb7b6794ce1051cd2b270ae", size = 20611, upload-time = "2025-12-11T13:37:01.661Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/08/11208bcfcab4fc2023252c3f322aa397fd9ad948355fea60f5fc98648603/opentelemetry_instrumentation_httpx-0.60b1.tar.gz", hash = "sha256:a506ebaf28c60112cbe70ad4f0338f8603f148938cb7b6794ce1051cd2b270ae", size = 20611 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/59/b98e84eebf745ffc75397eaad4763795bff8a30cbf2373a50ed4e70646c5/opentelemetry_instrumentation_httpx-0.60b1-py3-none-any.whl", hash = "sha256:f37636dd742ad2af83d896ba69601ed28da51fa4e25d1ab62fde89ce413e275b", size = 15701, upload-time = "2025-12-11T13:36:04.56Z" }, + { url = "https://files.pythonhosted.org/packages/43/59/b98e84eebf745ffc75397eaad4763795bff8a30cbf2373a50ed4e70646c5/opentelemetry_instrumentation_httpx-0.60b1-py3-none-any.whl", hash = "sha256:f37636dd742ad2af83d896ba69601ed28da51fa4e25d1ab62fde89ce413e275b", size = 15701 }, ] [[package]] name = "opentelemetry-proto" version = "1.39.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, + { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535 }, ] [[package]] name = "opentelemetry-sdk" version = "1.39.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, + { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565 }, ] [[package]] name = "opentelemetry-semantic-conventions" version = "0.60b1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982 }, ] [[package]] name = "opentelemetry-util-http" version = "0.60b1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/fc/c47bb04a1d8a941a4061307e1eddfa331ed4d0ab13d8a9781e6db256940a/opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6", size = 11053, upload-time = "2025-12-11T13:37:25.115Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/50/fc/c47bb04a1d8a941a4061307e1eddfa331ed4d0ab13d8a9781e6db256940a/opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6", size = 11053 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/5c/d3f1733665f7cd582ef0842fb1d2ed0bc1fba10875160593342d22bba375/opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199", size = 8947, upload-time = "2025-12-11T13:36:37.151Z" }, + { url = "https://files.pythonhosted.org/packages/16/5c/d3f1733665f7cd582ef0842fb1d2ed0bc1fba10875160593342d22bba375/opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199", size = 8947 }, ] [[package]] name = "openviking-sdk" version = "0.1.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/9c/b75da8d956bbfaf39288dd3cf7c734ba9225fba34e44362d02fbdb9a871d/openviking_sdk-0.1.5.tar.gz", hash = "sha256:f017dec938267aaea659122165f2872e189933a5604ba6d9ec0ae867b558ecaf", size = 33652, upload-time = "2026-07-23T06:18:56.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/9c/b75da8d956bbfaf39288dd3cf7c734ba9225fba34e44362d02fbdb9a871d/openviking_sdk-0.1.5.tar.gz", hash = "sha256:f017dec938267aaea659122165f2872e189933a5604ba6d9ec0ae867b558ecaf", size = 33652 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/c0/481ec44e2f98cf1afd47d4c115756dd4b1fa674caa4b99b0f72723d6fc33/openviking_sdk-0.1.5-py3-none-any.whl", hash = "sha256:f29af1c56e1a4477c881d584fa5178b5a34600847926cfe21fddd333ccb576ec", size = 21740, upload-time = "2026-07-23T06:18:54.864Z" }, + { url = "https://files.pythonhosted.org/packages/53/c0/481ec44e2f98cf1afd47d4c115756dd4b1fa674caa4b99b0f72723d6fc33/openviking_sdk-0.1.5-py3-none-any.whl", hash = "sha256:f29af1c56e1a4477c881d584fa5178b5a34600847926cfe21fddd333ccb576ec", size = 21740 }, ] [[package]] name = "orjson" version = "3.11.9" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458 }, + { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368 }, + { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070 }, + { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892 }, + { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217 }, + { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980 }, + { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738 }, + { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033 }, + { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492 }, + { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087 }, + { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031 }, + { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915 }, + { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613 }, + { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086 }, + { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696 }, ] [[package]] name = "packaging" version = "26.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195 }, ] [[package]] name = "paginate" version = "0.5.7" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, ] [[package]] name = "pathable" version = "0.6.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/f3/5a20387de9bcd0607871bfc2198ee0e15836da7baa4592ccd7f24c27c986/pathable-0.6.0.tar.gz", hash = "sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58", size = 18970, upload-time = "2026-05-19T18:15:11.911Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/f3/5a20387de9bcd0607871bfc2198ee0e15836da7baa4592ccd7f24c27c986/pathable-0.6.0.tar.gz", hash = "sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58", size = 18970 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/e8/6d75ffd9784bce2e93d1ae4415649427e39a53bb172d4672b2b59c6f0a7b/pathable-0.6.0-py3-none-any.whl", hash = "sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566", size = 18983, upload-time = "2026-05-19T18:15:10.728Z" }, + { url = "https://files.pythonhosted.org/packages/a2/e8/6d75ffd9784bce2e93d1ae4415649427e39a53bb172d4672b2b59c6f0a7b/pathable-0.6.0-py3-none-any.whl", hash = "sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566", size = 18983 }, ] [[package]] name = "pathlib-abc" version = "0.5.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/cb/448649d7f25d228bf0be3a04590ab7afa77f15e056f8fa976ed05ec9a78f/pathlib_abc-0.5.2.tar.gz", hash = "sha256:fcd56f147234645e2c59c7ae22808b34c364bb231f685ddd9f96885aed78a94c", size = 33342, upload-time = "2025-10-10T18:37:20.524Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/cb/448649d7f25d228bf0be3a04590ab7afa77f15e056f8fa976ed05ec9a78f/pathlib_abc-0.5.2.tar.gz", hash = "sha256:fcd56f147234645e2c59c7ae22808b34c364bb231f685ddd9f96885aed78a94c", size = 33342 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/29/c028a0731e202035f0e2e0bfbf1a3e46ad6c628cbb17f6f1cc9eea5d9ff1/pathlib_abc-0.5.2-py3-none-any.whl", hash = "sha256:4c9d94cf1b23af417ce7c0417b43333b06a106c01000b286c99de230d95eefbb", size = 19070, upload-time = "2025-10-10T18:37:19.437Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/c028a0731e202035f0e2e0bfbf1a3e46ad6c628cbb17f6f1cc9eea5d9ff1/pathlib_abc-0.5.2-py3-none-any.whl", hash = "sha256:4c9d94cf1b23af417ce7c0417b43333b06a106c01000b286c99de230d95eefbb", size = 19070 }, ] [[package]] name = "pathspec" version = "1.1.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328 }, ] [[package]] name = "pathvalidate" version = "3.3.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, + { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305 }, ] [[package]] name = "pefile" version = "2024.8.26" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/4f/2750f7f6f025a1507cd3b7218691671eecfd0bbebebe8b39aa0fe1d360b8/pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632", size = 76008, upload-time = "2024-08-26T20:58:38.155Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/4f/2750f7f6f025a1507cd3b7218691671eecfd0bbebebe8b39aa0fe1d360b8/pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632", size = 76008 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/16/12b82f791c7f50ddec566873d5bdd245baa1491bac11d15ffb98aecc8f8b/pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f", size = 74766, upload-time = "2024-08-26T21:01:02.632Z" }, + { url = "https://files.pythonhosted.org/packages/54/16/12b82f791c7f50ddec566873d5bdd245baa1491bac11d15ffb98aecc8f8b/pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f", size = 74766 }, ] [[package]] name = "pillow" version = "12.3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684 }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487 }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433 }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889 }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109 }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736 }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129 }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562 }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439 }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287 }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691 }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185 }, ] [[package]] name = "pipdeptree" version = "4.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nab-index" }, { name = "nab-python" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/68/8b0bca63d885a34bc7f8bcbc001bb9ffdc937fdada1d01d09f9af7785250/pipdeptree-4.1.0.tar.gz", hash = "sha256:6eb7e671ffa06ef597d683680d483a4882a86a2a00494971733bd88a3404c44f", size = 135159, upload-time = "2026-07-21T13:27:29.357Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/68/8b0bca63d885a34bc7f8bcbc001bb9ffdc937fdada1d01d09f9af7785250/pipdeptree-4.1.0.tar.gz", hash = "sha256:6eb7e671ffa06ef597d683680d483a4882a86a2a00494971733bd88a3404c44f", size = 135159 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/81/c1faac2e66ea180149158082e0ecaaac9e21362f23e179d145ed24cb1b12/pipdeptree-4.1.0-cp310-abi3-macosx_10_15_x86_64.whl", hash = "sha256:dd43b6af5990fd2d22c9b2d8b51f0fcf75a9019b79c950d62a12629c0c796306", size = 1645717, upload-time = "2026-07-21T13:27:06.442Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/60/bd1bfd035dc60b8b1e699da7210c11d23f55e14d5e110169c84dc3c42f88/pipdeptree-4.1.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:83a812e9fc901e511209b12cc358b73ba9372af0123901f523407c6d6d46f3ca", size = 1536600, upload-time = "2026-07-21T13:27:08.366Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/b4/5c99186ba900cd5fcabea5ceb15dfb54ba5f0f5c4ac36c8ec50bba39bdb6/pipdeptree-4.1.0-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4ac5c99e5305c916c9276689c4a2a00156bf3e72fb6d74da4c7cf9e908034e79", size = 1758348, upload-time = "2026-07-21T13:27:09.866Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/c2/3e7d6554759990dab54ad0ce4312534b0ebb0d0a3db9f171f6445298734e/pipdeptree-4.1.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0b48a42f22d1588feab62ce50f65d20dc9d6f8daf70105e5f27fc0dfc553b13c", size = 1839269, upload-time = "2026-07-21T13:27:11.329Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/07/5d1835791e82699df0383bc432f4c721e95a8aa40fd3e5b3a0d720d551aa/pipdeptree-4.1.0-cp310-abi3-win_amd64.whl", hash = "sha256:24a011ed94b4fb3e7a3395e2a705f5b82661263148609704e871a4b820e07cba", size = 1674697, upload-time = "2026-07-21T13:27:12.761Z" }, + { url = "https://files.pythonhosted.org/packages/66/81/c1faac2e66ea180149158082e0ecaaac9e21362f23e179d145ed24cb1b12/pipdeptree-4.1.0-cp310-abi3-macosx_10_15_x86_64.whl", hash = "sha256:dd43b6af5990fd2d22c9b2d8b51f0fcf75a9019b79c950d62a12629c0c796306", size = 1645717 }, + { url = "https://files.pythonhosted.org/packages/f0/60/bd1bfd035dc60b8b1e699da7210c11d23f55e14d5e110169c84dc3c42f88/pipdeptree-4.1.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:83a812e9fc901e511209b12cc358b73ba9372af0123901f523407c6d6d46f3ca", size = 1536600 }, + { url = "https://files.pythonhosted.org/packages/57/b4/5c99186ba900cd5fcabea5ceb15dfb54ba5f0f5c4ac36c8ec50bba39bdb6/pipdeptree-4.1.0-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4ac5c99e5305c916c9276689c4a2a00156bf3e72fb6d74da4c7cf9e908034e79", size = 1758348 }, + { url = "https://files.pythonhosted.org/packages/a8/c2/3e7d6554759990dab54ad0ce4312534b0ebb0d0a3db9f171f6445298734e/pipdeptree-4.1.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0b48a42f22d1588feab62ce50f65d20dc9d6f8daf70105e5f27fc0dfc553b13c", size = 1839269 }, + { url = "https://files.pythonhosted.org/packages/c1/07/5d1835791e82699df0383bc432f4c721e95a8aa40fd3e5b3a0d720d551aa/pipdeptree-4.1.0-cp310-abi3-win_amd64.whl", hash = "sha256:24a011ed94b4fb3e7a3395e2a705f5b82661263148609704e871a4b820e07cba", size = 1674697 }, ] [[package]] name = "platformdirs" version = "4.11.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/9b/560e4be8e26f6fd133a03630a8df0c663b9e8d61b4ade152b72005aec83b/platformdirs-4.11.0.tar.gz", hash = "sha256:0555d18370482847566ffabcaa53ad7c6c1c29f195989ae1ed634a05f76ea1e0", size = 31953, upload-time = "2026-07-21T13:09:36.565Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/78/9b/560e4be8e26f6fd133a03630a8df0c663b9e8d61b4ade152b72005aec83b/platformdirs-4.11.0.tar.gz", hash = "sha256:0555d18370482847566ffabcaa53ad7c6c1c29f195989ae1ed634a05f76ea1e0", size = 31953 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/68/d8d58938dfb1370b266a1a729e6d77a985be23689a0496498ee17b2cbf90/platformdirs-4.11.0-py3-none-any.whl", hash = "sha256:360ccded2b7fce0af0ff80cc8f5942a1c5d99b0e856033acb030bfc634709e74", size = 23247, upload-time = "2026-07-21T13:09:35.422Z" }, + { url = "https://files.pythonhosted.org/packages/7d/68/d8d58938dfb1370b266a1a729e6d77a985be23689a0496498ee17b2cbf90/platformdirs-4.11.0-py3-none-any.whl", hash = "sha256:360ccded2b7fce0af0ff80cc8f5942a1c5d99b0e856033acb030bfc634709e74", size = 23247 }, ] [[package]] name = "pluggy" version = "1.6.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, ] [[package]] name = "polyleven" version = "0.11.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/c7/e0b3bbe72e0003e5d02726e0d406ea47d523a2aec9c41d831817a8e0bce1/polyleven-0.11.0.tar.gz", hash = "sha256:d74d348387cf340051711c0dd6af993b4c264daa78470098de16f4a2b725785c", size = 6407, upload-time = "2026-02-09T09:41:49.87Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/c7/e0b3bbe72e0003e5d02726e0d406ea47d523a2aec9c41d831817a8e0bce1/polyleven-0.11.0.tar.gz", hash = "sha256:d74d348387cf340051711c0dd6af993b4c264daa78470098de16f4a2b725785c", size = 6407 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/16/5aec69609adc373f10087eb69b0b9d177ae721632715a86348b429030514/polyleven-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cb8ed97b536f9aada3ad45169ee7768c426498bf3fa608a4eabd055dfef795e", size = 7425, upload-time = "2026-02-09T09:41:06.542Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/5b/0542c723aa83833a5090114bc4e5a8e60293873fe60ee8221a5888d87370/polyleven-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2f975ab8cb81fd8eb5a647a3cefb0bb80bc307920a9307f66ab4019d88370ed2", size = 7505, upload-time = "2026-02-09T09:41:07.445Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/8d/c317217734a5bd2011f1128c1a9056477a5148d8d95527fcab2fe3955876/polyleven-0.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16986fd58911d6075b5f63ea001197141145b7a6df48bc4ce4530e79227e74a2", size = 21035, upload-time = "2026-02-09T09:41:08.32Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/8f/8a3e6e4a68dbd9de564fd3d16eee90e3f807a4380fd7192f40af4be47175/polyleven-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6a814629cc0468f9800b1333414a3be08fda9c5ce6b63e97154a9d21732e590", size = 21509, upload-time = "2026-02-09T09:41:09.285Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/da/4097998bea845f0b3a67112200aa08c19d4da0a17d761b35484d695c21e2/polyleven-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88a35ec93ec3d81a7347fd49db314a914798a144dca3d22946d18bba9b597dec", size = 20536, upload-time = "2026-02-09T09:41:10.211Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/71/67b7679ede99589ec749290d938693b87cdb6bb327b062c46d2129a5e6ec/polyleven-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:50bb7d68b790194d552ee1256a02e205486b27eb22ab333eeb0003e0271c4846", size = 20775, upload-time = "2026-02-09T09:41:11.692Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/3e/6f7fad4fee748ba365cb3e1ba2e061a74e18d987eb554ead4757127df2ab/polyleven-0.11.0-cp313-cp313-win32.whl", hash = "sha256:ce264f6a9daa3265299d8ffcb180d8256517a8d9235613a3b267172da0bc1e06", size = 11629, upload-time = "2026-02-09T09:41:12.652Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/cc/4877913dec8fb4f968a070c894254db5811b62128d3a69b05bcd1305b5c3/polyleven-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4648732c8ad3955c8d7b1aa015d92936a150475aaa97ce704fe0c8e7fa7e0c4f", size = 10841, upload-time = "2026-02-09T09:41:13.682Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/e2/039cc477ce73d6184e12cf6341ac200bc9f4c5428254c399015ec30392e1/polyleven-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:166f6c9b161c6af92ff201c734d6437bc7ef74a32dab306c5d47a0bdb7a82d9f", size = 9424, upload-time = "2026-02-09T09:41:14.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/16/5aec69609adc373f10087eb69b0b9d177ae721632715a86348b429030514/polyleven-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cb8ed97b536f9aada3ad45169ee7768c426498bf3fa608a4eabd055dfef795e", size = 7425 }, + { url = "https://files.pythonhosted.org/packages/bd/5b/0542c723aa83833a5090114bc4e5a8e60293873fe60ee8221a5888d87370/polyleven-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2f975ab8cb81fd8eb5a647a3cefb0bb80bc307920a9307f66ab4019d88370ed2", size = 7505 }, + { url = "https://files.pythonhosted.org/packages/4a/8d/c317217734a5bd2011f1128c1a9056477a5148d8d95527fcab2fe3955876/polyleven-0.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16986fd58911d6075b5f63ea001197141145b7a6df48bc4ce4530e79227e74a2", size = 21035 }, + { url = "https://files.pythonhosted.org/packages/5f/8f/8a3e6e4a68dbd9de564fd3d16eee90e3f807a4380fd7192f40af4be47175/polyleven-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6a814629cc0468f9800b1333414a3be08fda9c5ce6b63e97154a9d21732e590", size = 21509 }, + { url = "https://files.pythonhosted.org/packages/6b/da/4097998bea845f0b3a67112200aa08c19d4da0a17d761b35484d695c21e2/polyleven-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88a35ec93ec3d81a7347fd49db314a914798a144dca3d22946d18bba9b597dec", size = 20536 }, + { url = "https://files.pythonhosted.org/packages/a1/71/67b7679ede99589ec749290d938693b87cdb6bb327b062c46d2129a5e6ec/polyleven-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:50bb7d68b790194d552ee1256a02e205486b27eb22ab333eeb0003e0271c4846", size = 20775 }, + { url = "https://files.pythonhosted.org/packages/e9/3e/6f7fad4fee748ba365cb3e1ba2e061a74e18d987eb554ead4757127df2ab/polyleven-0.11.0-cp313-cp313-win32.whl", hash = "sha256:ce264f6a9daa3265299d8ffcb180d8256517a8d9235613a3b267172da0bc1e06", size = 11629 }, + { url = "https://files.pythonhosted.org/packages/2f/cc/4877913dec8fb4f968a070c894254db5811b62128d3a69b05bcd1305b5c3/polyleven-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4648732c8ad3955c8d7b1aa015d92936a150475aaa97ce704fe0c8e7fa7e0c4f", size = 10841 }, + { url = "https://files.pythonhosted.org/packages/59/e2/039cc477ce73d6184e12cf6341ac200bc9f4c5428254c399015ec30392e1/polyleven-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:166f6c9b161c6af92ff201c734d6437bc7ef74a32dab306c5d47a0bdb7a82d9f", size = 9424 }, ] [[package]] name = "prometheus-client" version = "0.25.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035, upload-time = "2026-04-09T19:53:42.359Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154, upload-time = "2026-04-09T19:53:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154 }, ] [[package]] name = "prompt-toolkit" version = "3.0.52" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, ] [[package]] name = "promptantic" version = "1.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "prompt-toolkit" }, { name = "pydantic" }, { name = "schemez" }, { name = "universal-pathlib" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/9d/0581eadf715aa31d61bfe2b7463b65dc6f08bc3220d3b2432d688f1a7979/promptantic-1.0.0.tar.gz", hash = "sha256:84422e4c83bd372c4fd3f13cbff317b8867c3807c4050d88a94496bf5813e663", size = 24765, upload-time = "2025-10-07T20:25:38.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/9d/0581eadf715aa31d61bfe2b7463b65dc6f08bc3220d3b2432d688f1a7979/promptantic-1.0.0.tar.gz", hash = "sha256:84422e4c83bd372c4fd3f13cbff317b8867c3807c4050d88a94496bf5813e663", size = 24765 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/d1/d4987d168b6942fee14cd49b00317658cc24f8db0fe121f4395576616290/promptantic-1.0.0-py3-none-any.whl", hash = "sha256:90ad4e7e860b1296427cd50a99abc1562e90ae482af104174acb99cb9dd6138b", size = 33945, upload-time = "2025-10-07T20:25:36.999Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d1/d4987d168b6942fee14cd49b00317658cc24f8db0fe121f4395576616290/promptantic-1.0.0-py3-none-any.whl", hash = "sha256:90ad4e7e860b1296427cd50a99abc1562e90ae482af104174acb99cb9dd6138b", size = 33945 }, ] [[package]] name = "promptlayer" version = "1.5.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "cachetools" }, @@ -3617,125 +3403,125 @@ dependencies = [ { name = "rich" }, { name = "tenacity" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/da/c93be46621337726ad33ed6f47bb28eba21ae2f95838ab980cfd4814cdbb/promptlayer-1.5.1.tar.gz", hash = "sha256:178dd2440003bc36e31c04833b6fc39ad07e02339499dc80b15d25483eb6e72c", size = 130611, upload-time = "2026-07-22T20:08:20.922Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/da/c93be46621337726ad33ed6f47bb28eba21ae2f95838ab980cfd4814cdbb/promptlayer-1.5.1.tar.gz", hash = "sha256:178dd2440003bc36e31c04833b6fc39ad07e02339499dc80b15d25483eb6e72c", size = 130611 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/06/d7c9bb1c878d413e02484df2108feb1dd1d8b52f83669c697194feecd30e/promptlayer-1.5.1-py3-none-any.whl", hash = "sha256:981e1d6df6e0c608f10fcaa6a6c687d502cd0813bc284efe4ac25bc94f8ca90a", size = 163429, upload-time = "2026-07-22T20:08:22.179Z" }, + { url = "https://files.pythonhosted.org/packages/e1/06/d7c9bb1c878d413e02484df2108feb1dd1d8b52f83669c697194feecd30e/promptlayer-1.5.1-py3-none-any.whl", hash = "sha256:981e1d6df6e0c608f10fcaa6a6c687d502cd0813bc284efe4ac25bc94f8ca90a", size = 163429 }, ] [[package]] name = "propcache" version = "0.5.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457 }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835 }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545 }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886 }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261 }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184 }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534 }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500 }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994 }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884 }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464 }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588 }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667 }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463 }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621 }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649 }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636 }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872 }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257 }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696 }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378 }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283 }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616 }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773 }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664 }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643 }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595 }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711 }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247 }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102 }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964 }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546 }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330 }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521 }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036 }, ] [[package]] name = "protobuf" version = "6.33.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739 }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089 }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737 }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610 }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381 }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436 }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656 }, ] [[package]] name = "psutil" version = "7.2.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595 }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082 }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476 }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062 }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893 }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589 }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090 }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859 }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560 }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997 }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972 }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266 }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737 }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617 }, ] [[package]] name = "psygnal" version = "0.15.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/79/20c3e23e75272e9ddf018097cf872ab088bccba978888472656629efa4a3/psygnal-0.15.1.tar.gz", hash = "sha256:f64f62dee2306fc1c22050a59b6c6cdad126e04b0cf50e393ff858a1da719096", size = 123147, upload-time = "2026-01-04T16:38:41.959Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/79/20c3e23e75272e9ddf018097cf872ab088bccba978888472656629efa4a3/psygnal-0.15.1.tar.gz", hash = "sha256:f64f62dee2306fc1c22050a59b6c6cdad126e04b0cf50e393ff858a1da719096", size = 123147 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/21/5a142165d27063abf5921807d3c3d973f5d44ab414a13b210839a43ead4d/psygnal-0.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2087aadc9404f007f79c2899e329932869e362c50de58b90631c5f49b4768cc5", size = 596768, upload-time = "2026-01-04T16:38:27.053Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/25/c1712931d61c118691e73daf29ef708c679ea9ba187c797dd5deee360411/psygnal-0.15.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f3bf68ca42569dfdce20c6cf915d34b78b9e3ddddacb9f78728224fda6946b4", size = 574808, upload-time = "2026-01-04T16:38:28.779Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/4f/3593e5adb88a188c798604aed95fbc1479f30230e7f51e8f2c770e6a3832/psygnal-0.15.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e9fca977f5335deea39aed22e31d9795983e4f243e59a7d3c4105793adb7693d", size = 885616, upload-time = "2026-01-04T16:38:30.081Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/4c/14779ed4c3a1d71fa1a9a87ecfb184ad3335dd64681067f77c1c47b14ae9/psygnal-0.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c85b7d05b92ccbec47c75ab8a5545eda462e81a492c82424aba5ab81a3ad89d", size = 876516, upload-time = "2026-01-04T16:38:31.422Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/bc/4f771e3cdcde4db4023dbf36d6f0aab44e02b9de719353c22954b655e2ff/psygnal-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:ac0e693b29e0a429e97315a52313321855bef6140e9975b7ae78b4d93c8fbb42", size = 419172, upload-time = "2026-01-04T16:38:32.82Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/49/7742544684bee728ec123515d2694cee859aa2a705951a461230b00f18cc/psygnal-0.15.1-py3-none-any.whl", hash = "sha256:4221140e633e45b076953c64bcb9b41a744833527f9a037c1ca98bc270798cbf", size = 90638, upload-time = "2026-01-04T16:38:40.841Z" }, + { url = "https://files.pythonhosted.org/packages/46/21/5a142165d27063abf5921807d3c3d973f5d44ab414a13b210839a43ead4d/psygnal-0.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2087aadc9404f007f79c2899e329932869e362c50de58b90631c5f49b4768cc5", size = 596768 }, + { url = "https://files.pythonhosted.org/packages/e1/25/c1712931d61c118691e73daf29ef708c679ea9ba187c797dd5deee360411/psygnal-0.15.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f3bf68ca42569dfdce20c6cf915d34b78b9e3ddddacb9f78728224fda6946b4", size = 574808 }, + { url = "https://files.pythonhosted.org/packages/2d/4f/3593e5adb88a188c798604aed95fbc1479f30230e7f51e8f2c770e6a3832/psygnal-0.15.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e9fca977f5335deea39aed22e31d9795983e4f243e59a7d3c4105793adb7693d", size = 885616 }, + { url = "https://files.pythonhosted.org/packages/58/4c/14779ed4c3a1d71fa1a9a87ecfb184ad3335dd64681067f77c1c47b14ae9/psygnal-0.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c85b7d05b92ccbec47c75ab8a5545eda462e81a492c82424aba5ab81a3ad89d", size = 876516 }, + { url = "https://files.pythonhosted.org/packages/3e/bc/4f771e3cdcde4db4023dbf36d6f0aab44e02b9de719353c22954b655e2ff/psygnal-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:ac0e693b29e0a429e97315a52313321855bef6140e9975b7ae78b4d93c8fbb42", size = 419172 }, + { url = "https://files.pythonhosted.org/packages/46/49/7742544684bee728ec123515d2694cee859aa2a705951a461230b00f18cc/psygnal-0.15.1-py3-none-any.whl", hash = "sha256:4221140e633e45b076953c64bcb9b41a744833527f9a037c1ca98bc270798cbf", size = 90638 }, ] [[package]] name = "ptyprocess" version = "0.7.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, ] [[package]] name = "py-key-value-aio" version = "0.4.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beartype" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/e2/d689d922894a7ecde73b6daeaf9b13dab5aae06fe6aaaf7514722644d382/py_key_value_aio-0.4.5.tar.gz", hash = "sha256:c6563a2c6abe5da5e20f4f9e875c2a9b425a2244a54fadbf46cf140a9eea45d7", size = 107547, upload-time = "2026-05-27T16:37:08.107Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/e2/d689d922894a7ecde73b6daeaf9b13dab5aae06fe6aaaf7514722644d382/py_key_value_aio-0.4.5.tar.gz", hash = "sha256:c6563a2c6abe5da5e20f4f9e875c2a9b425a2244a54fadbf46cf140a9eea45d7", size = 107547 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/95/b8ba862968712caa12a19666175334fa979e1f198b896a430adb3bacfe87/py_key_value_aio-0.4.5-py3-none-any.whl", hash = "sha256:ab862adbcb8c72547d1c57821f22cbbb71ab86509039c96f36e914e0336c8dd7", size = 170005, upload-time = "2026-05-27T16:37:06.629Z" }, + { url = "https://files.pythonhosted.org/packages/f6/95/b8ba862968712caa12a19666175334fa979e1f198b896a430adb3bacfe87/py_key_value_aio-0.4.5-py3-none-any.whl", hash = "sha256:ab862adbcb8c72547d1c57821f22cbbb71ab86509039c96f36e914e0336c8dd7", size = 170005 }, ] [package.optional-dependencies] @@ -3760,91 +3546,91 @@ redis = [ [[package]] name = "py-rust-stemmers" version = "0.1.8" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/c1/9763f9fb1cd73f9c317a83feeed6e0d4af320c6bbddab47b4a94f3a47d0c/py_rust_stemmers-0.1.8.tar.gz", hash = "sha256:6b0f6f48bc54d607aed802de872fcd5a71bae969a6760976dc78ce55e8eaf3da", size = 9732, upload-time = "2026-05-22T11:00:24.358Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/c1/9763f9fb1cd73f9c317a83feeed6e0d4af320c6bbddab47b4a94f3a47d0c/py_rust_stemmers-0.1.8.tar.gz", hash = "sha256:6b0f6f48bc54d607aed802de872fcd5a71bae969a6760976dc78ce55e8eaf3da", size = 9732 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/98/f078f3930311e7b6154ccdf9166c4e30a416c7d199e136b5f09265d58a35/py_rust_stemmers-0.1.8-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5bd15b89203ecd886960e237124d1aa6e55498d76418c36c967d3b12168d43dc", size = 290427, upload-time = "2026-05-22T10:59:55.316Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/46/21d784a3f1db6a23051ffd5826d8ee667d26a64587c1cfbda0443ed87fff/py_rust_stemmers-0.1.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c92733b020534470ca5a0d7fe8b85c85622ff383d4f37fec75a1c677aa84921", size = 275628, upload-time = "2026-05-22T10:59:56.687Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/d5/701c73a4f6a7fecfd96a6588f0cafe98d6b0acde93adf8a2e45535f3d1d5/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ab605a86c950ba7e8ab1392cf91296c0bec3084babb897a4aecf90a10c82395", size = 316656, upload-time = "2026-05-22T10:59:57.67Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/0d/c58fe98153cfdb6abf4dfb6ac335c923000d4af4e736080c3a3045b7aea7/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21ed8055cec1f78d666afad8ffd7a51775ba419d2c615b8a1df7b32ca7f33e2b", size = 319377, upload-time = "2026-05-22T10:59:58.664Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/d7/e60d04849e90aa3ad457211cc4999c30401f433341f9a5588c12b81f9877/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae773e1d01e9aa328d175f461475d0cd7074a82bfcc71de6dc5765e51f1cc9f7", size = 323719, upload-time = "2026-05-22T10:59:59.845Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/48/c0e4fb955db784cc354e0756354602f7043ff4c10fcbd9d901a2f8fe3239/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5cc8fab9d0f1b274a26935a632362b8278f03e81b65e8b8644d5ca3f62a5a1a4", size = 324110, upload-time = "2026-05-22T11:00:01.26Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/eb/981b26baff37cf7a26ee206763cc4d2fb3e1db8f0f86ec030074431fae05/py_rust_stemmers-0.1.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35570098da02eb439afcd7270a12bf850bbe874b85cb912e0fb2d87a6e703920", size = 494645, upload-time = "2026-05-22T11:00:02.737Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/af/f16e805b7aefc2257b192b83a89300c8360b0fdffd3dfefa92dee4ec9b15/py_rust_stemmers-0.1.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0a68745d4b3c7f5abc778ca967e8711df6154873abcfe4e62a6631fa2363cc32", size = 596124, upload-time = "2026-05-22T11:00:04.499Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/8c/e7a2c940ba00e0792ae346aed5e755d51d37cf6d6853f6b141e5380e285d/py_rust_stemmers-0.1.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7cc0cc0b8eb45d2158c28ea43e2f338c110aad63052ad3bd00bc7446a595e12f", size = 541771, upload-time = "2026-05-22T11:00:06.081Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/a0/dd7c5fc6ade6d2a2a49e49937f06f2d488511454e8ab1b313d277ee8c3b1/py_rust_stemmers-0.1.8-cp313-cp313-win_amd64.whl", hash = "sha256:15af4e12e1288de2e5241eec375afc6ad6be4c125a28ca010599d9f92db23f01", size = 212438, upload-time = "2026-05-22T11:00:07.244Z" }, + { url = "https://files.pythonhosted.org/packages/c2/98/f078f3930311e7b6154ccdf9166c4e30a416c7d199e136b5f09265d58a35/py_rust_stemmers-0.1.8-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5bd15b89203ecd886960e237124d1aa6e55498d76418c36c967d3b12168d43dc", size = 290427 }, + { url = "https://files.pythonhosted.org/packages/c9/46/21d784a3f1db6a23051ffd5826d8ee667d26a64587c1cfbda0443ed87fff/py_rust_stemmers-0.1.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c92733b020534470ca5a0d7fe8b85c85622ff383d4f37fec75a1c677aa84921", size = 275628 }, + { url = "https://files.pythonhosted.org/packages/57/d5/701c73a4f6a7fecfd96a6588f0cafe98d6b0acde93adf8a2e45535f3d1d5/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ab605a86c950ba7e8ab1392cf91296c0bec3084babb897a4aecf90a10c82395", size = 316656 }, + { url = "https://files.pythonhosted.org/packages/9d/0d/c58fe98153cfdb6abf4dfb6ac335c923000d4af4e736080c3a3045b7aea7/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21ed8055cec1f78d666afad8ffd7a51775ba419d2c615b8a1df7b32ca7f33e2b", size = 319377 }, + { url = "https://files.pythonhosted.org/packages/5c/d7/e60d04849e90aa3ad457211cc4999c30401f433341f9a5588c12b81f9877/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae773e1d01e9aa328d175f461475d0cd7074a82bfcc71de6dc5765e51f1cc9f7", size = 323719 }, + { url = "https://files.pythonhosted.org/packages/6a/48/c0e4fb955db784cc354e0756354602f7043ff4c10fcbd9d901a2f8fe3239/py_rust_stemmers-0.1.8-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5cc8fab9d0f1b274a26935a632362b8278f03e81b65e8b8644d5ca3f62a5a1a4", size = 324110 }, + { url = "https://files.pythonhosted.org/packages/48/eb/981b26baff37cf7a26ee206763cc4d2fb3e1db8f0f86ec030074431fae05/py_rust_stemmers-0.1.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35570098da02eb439afcd7270a12bf850bbe874b85cb912e0fb2d87a6e703920", size = 494645 }, + { url = "https://files.pythonhosted.org/packages/6d/af/f16e805b7aefc2257b192b83a89300c8360b0fdffd3dfefa92dee4ec9b15/py_rust_stemmers-0.1.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0a68745d4b3c7f5abc778ca967e8711df6154873abcfe4e62a6631fa2363cc32", size = 596124 }, + { url = "https://files.pythonhosted.org/packages/76/8c/e7a2c940ba00e0792ae346aed5e755d51d37cf6d6853f6b141e5380e285d/py_rust_stemmers-0.1.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7cc0cc0b8eb45d2158c28ea43e2f338c110aad63052ad3bd00bc7446a595e12f", size = 541771 }, + { url = "https://files.pythonhosted.org/packages/c2/a0/dd7c5fc6ade6d2a2a49e49937f06f2d488511454e8ab1b313d277ee8c3b1/py_rust_stemmers-0.1.8-cp313-cp313-win_amd64.whl", hash = "sha256:15af4e12e1288de2e5241eec375afc6ad6be4c125a28ca010599d9f92db23f01", size = 212438 }, ] [[package]] name = "pyarrow" version = "25.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/f3/95428098d1fa7d04432fb750eed06b41304c2f6a5d3319985e64db2d9d41/pyarrow-25.0.0.tar.gz", hash = "sha256:d2d697008b5ec06d75952ef260c2e9a8a0f6ccfce24266c04c9c8ade927cb3b4", size = 1199181, upload-time = "2026-07-10T08:29:50.116Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/f3/95428098d1fa7d04432fb750eed06b41304c2f6a5d3319985e64db2d9d41/pyarrow-25.0.0.tar.gz", hash = "sha256:d2d697008b5ec06d75952ef260c2e9a8a0f6ccfce24266c04c9c8ade927cb3b4", size = 1199181 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/c8/098ce17d778fd9d29e40bb8c5f19a40cc90c3f0b46c9057b0d7993f42f54/pyarrow-25.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:8831a3ba52fa7cdb78d368d968b1dcd06171e6dff5461e16d90de91d371e47bc", size = 35844549, upload-time = "2026-07-10T08:27:37.956Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/66/24c28877219abf6263d909b1592c97ff82c59f13a59acbed11fc87c0654f/pyarrow-25.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:5f4bacb60f91dd2fca6c52f1b9a0012cd090e0294f1f781dc1881a247a352f8e", size = 37610397, upload-time = "2026-07-10T08:27:43.803Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/55/6d1d5f5aff317ec5de9421594679ed51ed828fe7e2ce209327f819d801e4/pyarrow-25.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:59516c822d5fd8e544aaa0dfe72f36fed5d4c24ea8390aab1bcd31d7e959c6be", size = 46841701, upload-time = "2026-07-10T08:27:49.741Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/5d/f790fb6965ab54c9da0dda7856abc75fd0d7648d865f8d603c111d203a64/pyarrow-25.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f9dbd83e91c239a1f5ee7ce13f108b5f6c0efbe40a4375260d8f08b43ad05e9", size = 50090118, upload-time = "2026-07-10T08:27:56.051Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/8c/faf025357ebf31bc96777f234277aa31e2aeca6dd4ecaa391f29085473c2/pyarrow-25.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18dcc8cc50b5e72eae6fcbfc6c8776c21a007176b27a3cdec5c2f5bcf126708d", size = 49945559, upload-time = "2026-07-10T08:28:01.927Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/a1/bd051871708ea99a5e0fc711926c26c6f2c6d0130c7aaac8093e34998af6/pyarrow-25.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4ec1895a87aa834c3b99b7a1e758747eb8bb57f922b32c0e0fa04afb8d6998b1", size = 53114238, upload-time = "2026-07-10T08:28:08.594Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/31/737f0c3cffcd6af647849477d1dd68045deac2e3963c3f9f211bedc48540/pyarrow-25.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:77c8d1ae46a44b4006e8db1cc977bbcc6ce4873c92f74137d68e45503b97fb18", size = 27861162, upload-time = "2026-07-10T08:28:12.975Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c8/098ce17d778fd9d29e40bb8c5f19a40cc90c3f0b46c9057b0d7993f42f54/pyarrow-25.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:8831a3ba52fa7cdb78d368d968b1dcd06171e6dff5461e16d90de91d371e47bc", size = 35844549 }, + { url = "https://files.pythonhosted.org/packages/bc/66/24c28877219abf6263d909b1592c97ff82c59f13a59acbed11fc87c0654f/pyarrow-25.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:5f4bacb60f91dd2fca6c52f1b9a0012cd090e0294f1f781dc1881a247a352f8e", size = 37610397 }, + { url = "https://files.pythonhosted.org/packages/53/55/6d1d5f5aff317ec5de9421594679ed51ed828fe7e2ce209327f819d801e4/pyarrow-25.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:59516c822d5fd8e544aaa0dfe72f36fed5d4c24ea8390aab1bcd31d7e959c6be", size = 46841701 }, + { url = "https://files.pythonhosted.org/packages/b5/5d/f790fb6965ab54c9da0dda7856abc75fd0d7648d865f8d603c111d203a64/pyarrow-25.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f9dbd83e91c239a1f5ee7ce13f108b5f6c0efbe40a4375260d8f08b43ad05e9", size = 50090118 }, + { url = "https://files.pythonhosted.org/packages/0c/8c/faf025357ebf31bc96777f234277aa31e2aeca6dd4ecaa391f29085473c2/pyarrow-25.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18dcc8cc50b5e72eae6fcbfc6c8776c21a007176b27a3cdec5c2f5bcf126708d", size = 49945559 }, + { url = "https://files.pythonhosted.org/packages/07/a1/bd051871708ea99a5e0fc711926c26c6f2c6d0130c7aaac8093e34998af6/pyarrow-25.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4ec1895a87aa834c3b99b7a1e758747eb8bb57f922b32c0e0fa04afb8d6998b1", size = 53114238 }, + { url = "https://files.pythonhosted.org/packages/7c/31/737f0c3cffcd6af647849477d1dd68045deac2e3963c3f9f211bedc48540/pyarrow-25.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:77c8d1ae46a44b4006e8db1cc977bbcc6ce4873c92f74137d68e45503b97fb18", size = 27861162 }, ] [[package]] name = "pyasn1" version = "0.6.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410 }, ] [[package]] name = "pyasn1-modules" version = "0.4.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, ] [[package]] name = "pyconify" version = "0.2.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/7f/94d424dc756a6287271cf40cf1b2a44c10e3f137bf3246a2b4a7416ca3d3/pyconify-0.2.1.tar.gz", hash = "sha256:8dd53757d9fbed41711434460932b2b5dbc25da720cd9f9a44af0187b2dfc07d", size = 22478, upload-time = "2025-02-06T13:20:53.592Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/7f/94d424dc756a6287271cf40cf1b2a44c10e3f137bf3246a2b4a7416ca3d3/pyconify-0.2.1.tar.gz", hash = "sha256:8dd53757d9fbed41711434460932b2b5dbc25da720cd9f9a44af0187b2dfc07d", size = 22478 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/40/50dd2e8bfec81676e4619903bd452c10dc0d8efac1533e79e67cc76759b5/pyconify-0.2.1-py3-none-any.whl", hash = "sha256:d3b53eee1f8a2d60c1d135610f42e789774dbe71c6d8af68af0a21d3b3ec9eb7", size = 19459, upload-time = "2025-02-06T13:20:51.613Z" }, + { url = "https://files.pythonhosted.org/packages/72/40/50dd2e8bfec81676e4619903bd452c10dc0d8efac1533e79e67cc76759b5/pyconify-0.2.1-py3-none-any.whl", hash = "sha256:d3b53eee1f8a2d60c1d135610f42e789774dbe71c6d8af68af0a21d3b3ec9eb7", size = 19459 }, ] [[package]] name = "pycparser" version = "3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, ] [[package]] name = "pydantic" version = "2.13.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262 }, ] [package.optional-dependencies] @@ -3855,19 +3641,19 @@ email = [ [[package]] name = "pydantic-ai" version = "2.17.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic-ai-slim", extra = ["anthropic", "cli", "evals", "google", "logfire", "mcp", "openai", "retries", "web"] }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/4a/749ffa1e82989fbe46452aefa667d4b97e6471f24de6c60e563a8953c289/pydantic_ai-2.17.0.tar.gz", hash = "sha256:32bfa1eb91bedf611f3dea59063765b4fce8d644c85bccb8e79bbb2f58e9ffa2", size = 18760, upload-time = "2026-07-24T01:55:37.467Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/4a/749ffa1e82989fbe46452aefa667d4b97e6471f24de6c60e563a8953c289/pydantic_ai-2.17.0.tar.gz", hash = "sha256:32bfa1eb91bedf611f3dea59063765b4fce8d644c85bccb8e79bbb2f58e9ffa2", size = 18760 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/af/07b7dc9c078a48201aadd8ce48c96e57c6936f8518b30fdb810b59adf62a/pydantic_ai-2.17.0-py3-none-any.whl", hash = "sha256:f780f7fde4b302efbf311b03a15a4b08836efc1b07f1497065200b656f72e2fa", size = 7733, upload-time = "2026-07-24T01:55:28.876Z" }, + { url = "https://files.pythonhosted.org/packages/9e/af/07b7dc9c078a48201aadd8ce48c96e57c6936f8518b30fdb810b59adf62a/pydantic_ai-2.17.0-py3-none-any.whl", hash = "sha256:f780f7fde4b302efbf311b03a15a4b08836efc1b07f1497065200b656f72e2fa", size = 7733 }, ] [[package]] name = "pydantic-ai-slim" version = "2.17.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "genai-prices" }, { name = "griffelib" }, @@ -3877,9 +3663,9 @@ dependencies = [ { name = "pydantic-graph" }, { name = "typing-inspection" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/52/ab9c7617b42727ba6ef6cc71f9f76f188cfe9387c42e9cdbf0ebe7f5d45d/pydantic_ai_slim-2.17.0.tar.gz", hash = "sha256:2ce0e81a270c99d7c8fb01b9675fe3fc6097efc99943f314bbf4b33f87adfab8", size = 893164, upload-time = "2026-07-24T01:55:39.431Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/52/ab9c7617b42727ba6ef6cc71f9f76f188cfe9387c42e9cdbf0ebe7f5d45d/pydantic_ai_slim-2.17.0.tar.gz", hash = "sha256:2ce0e81a270c99d7c8fb01b9675fe3fc6097efc99943f314bbf4b33f87adfab8", size = 893164 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/27/dc5d0e29bd24c73fa10fa45d4c4416f7cf7212f8ebdf139f5f134a9d2483/pydantic_ai_slim-2.17.0-py3-none-any.whl", hash = "sha256:9d236918df3504452f961b237266e3c025575ea366a155faefe3c04805fd2fa3", size = 1080947, upload-time = "2026-07-24T01:55:31.877Z" }, + { url = "https://files.pythonhosted.org/packages/3d/27/dc5d0e29bd24c73fa10fa45d4c4416f7cf7212f8ebdf139f5f134a9d2483/pydantic_ai_slim-2.17.0-py3-none-any.whl", hash = "sha256:9d236918df3504452f961b237266e3c025575ea366a155faefe3c04805fd2fa3", size = 1080947 }, ] [package.optional-dependencies] @@ -3924,33 +3710,33 @@ web = [ [[package]] name = "pydantic-core" version = "2.46.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306 }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906 }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802 }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446 }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757 }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275 }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467 }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417 }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782 }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782 }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334 }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986 }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693 }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819 }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411 }, ] [[package]] name = "pydantic-evals" version = "2.17.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "logfire-api" }, @@ -3959,57 +3745,57 @@ dependencies = [ { name = "pyyaml" }, { name = "rich" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/70/77768745b74a35d254d0e3c75ad8bd5bcc3c3d9383c6d38ad10bd331f1d8/pydantic_evals-2.17.0.tar.gz", hash = "sha256:4db59787f178a4ee12815a2e176774aa1c3c64df27b2037f05fec9677b08f200", size = 85147, upload-time = "2026-07-24T01:55:41.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/70/77768745b74a35d254d0e3c75ad8bd5bcc3c3d9383c6d38ad10bd331f1d8/pydantic_evals-2.17.0.tar.gz", hash = "sha256:4db59787f178a4ee12815a2e176774aa1c3c64df27b2037f05fec9677b08f200", size = 85147 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/7d/302c489844fb12396cae20804c161e71210f028436e89027d3d3ace8809d/pydantic_evals-2.17.0-py3-none-any.whl", hash = "sha256:be2ee15fa8d33646e0f2ab537df3e7feda15f87f641d763773ca378735e931bd", size = 100440, upload-time = "2026-07-24T01:55:33.665Z" }, + { url = "https://files.pythonhosted.org/packages/23/7d/302c489844fb12396cae20804c161e71210f028436e89027d3d3ace8809d/pydantic_evals-2.17.0-py3-none-any.whl", hash = "sha256:be2ee15fa8d33646e0f2ab537df3e7feda15f87f641d763773ca378735e931bd", size = 100440 }, ] [[package]] name = "pydantic-graph" version = "2.17.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "logfire-api" }, { name = "pydantic" }, { name = "typing-inspection" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/60/796977f734a7a089bd0ae3a2c33bdb4dec8ab4201aad5928b620c27df26c/pydantic_graph-2.17.0.tar.gz", hash = "sha256:28162287995cf46eb2f34d5e06e8da96f5fe167534b811bf6e80db382f065a91", size = 43982, upload-time = "2026-07-24T01:55:42.52Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/60/796977f734a7a089bd0ae3a2c33bdb4dec8ab4201aad5928b620c27df26c/pydantic_graph-2.17.0.tar.gz", hash = "sha256:28162287995cf46eb2f34d5e06e8da96f5fe167534b811bf6e80db382f065a91", size = 43982 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/a9/f4ea2cba136af41c5f69e089a3557a1d6b91771f59b43af5f9595105ca24/pydantic_graph-2.17.0-py3-none-any.whl", hash = "sha256:38e40ce971552fde4bc72f58d2e8f90478fbb3f9282be43dc471b48ba52660da", size = 51661, upload-time = "2026-07-24T01:55:35.212Z" }, + { url = "https://files.pythonhosted.org/packages/90/a9/f4ea2cba136af41c5f69e089a3557a1d6b91771f59b43af5f9595105ca24/pydantic_graph-2.17.0-py3-none-any.whl", hash = "sha256:38e40ce971552fde4bc72f58d2e8f90478fbb3f9282be43dc471b48ba52660da", size = 51661 }, ] [[package]] name = "pydantic-settings" version = "2.14.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715, upload-time = "2026-06-19T13:44:55.02Z" }, + { url = "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715 }, ] [[package]] name = "pydantic-xml" version = "2.21.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "pydantic-core" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/87/085533e0873b2f80bdeb12156ffe5dddae7ee55573e2057ad574d73e3b6a/pydantic_xml-2.21.0.tar.gz", hash = "sha256:678ba9149c24e190ad94d6e3ce47f94cbb1409fd88d7df65d3d26939ff669ed0", size = 26825, upload-time = "2026-05-17T15:24:19.534Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/87/085533e0873b2f80bdeb12156ffe5dddae7ee55573e2057ad574d73e3b6a/pydantic_xml-2.21.0.tar.gz", hash = "sha256:678ba9149c24e190ad94d6e3ce47f94cbb1409fd88d7df65d3d26939ff669ed0", size = 26825 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/b1/278da37b539595b2cb6bb4fb3da1d69ff084c7bf5cc143f0a444550dc41a/pydantic_xml-2.21.0-py3-none-any.whl", hash = "sha256:966b8990746528304ebb96c97db7603ee3faf38f06266247b1fa444dbc16f8d6", size = 43449, upload-time = "2026-05-17T15:24:21.182Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b1/278da37b539595b2cb6bb4fb3da1d69ff084c7bf5cc143f0a444550dc41a/pydantic_xml-2.21.0-py3-none-any.whl", hash = "sha256:966b8990746528304ebb96c97db7603ee3faf38f06266247b1fa444dbc16f8d6", size = 43449 }, ] [[package]] name = "pydocket" version = "0.23.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "burner-redis" }, { name = "cloudpickle" }, @@ -4025,24 +3811,24 @@ dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, { name = "uncalled-for" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/72/f5a0960249d32a213fb26297a55f842ce8bc90bb19adbf47fefebb9c99da/pydocket-0.23.1.tar.gz", hash = "sha256:136c1cc8b7aa7c4c08ea12431e966ae75eb093bb6ce6e0ac9db3391ba3d6034b", size = 425621, upload-time = "2026-07-20T18:49:24.592Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/72/f5a0960249d32a213fb26297a55f842ce8bc90bb19adbf47fefebb9c99da/pydocket-0.23.1.tar.gz", hash = "sha256:136c1cc8b7aa7c4c08ea12431e966ae75eb093bb6ce6e0ac9db3391ba3d6034b", size = 425621 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/67/b57ac7cfd2c2f30d58ff2c996591880d200078fc828cbbad8da69d634c0e/pydocket-0.23.1-py3-none-any.whl", hash = "sha256:3da279978fd0d07eb96d4119e1a213de61d6b4acd1792a28bc62e67f3e7e5929", size = 129230, upload-time = "2026-07-20T18:49:23.243Z" }, + { url = "https://files.pythonhosted.org/packages/e6/67/b57ac7cfd2c2f30d58ff2c996591880d200078fc828cbbad8da69d634c0e/pydocket-0.23.1-py3-none-any.whl", hash = "sha256:3da279978fd0d07eb96d4119e1a213de61d6b4acd1792a28bc62e67f3e7e5929", size = 129230 }, ] [[package]] name = "pygments" version = "2.20.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 }, ] [[package]] name = "pyinstaller" version = "6.21.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "altgraph" }, { name = "macholib", marker = "sys_platform == 'darwin'" }, @@ -4052,56 +3838,56 @@ dependencies = [ { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, { name = "setuptools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/4d/ec706c3fcf39e26888c35b39615ff4d5865d184069666c47492cff1fbe50/pyinstaller-6.21.0.tar.gz", hash = "sha256:bb9fab705983e393a2d1cac77d6972513057ad800215fd861dc15ff5272e98fd", size = 4061519, upload-time = "2026-06-13T14:15:06.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/4d/ec706c3fcf39e26888c35b39615ff4d5865d184069666c47492cff1fbe50/pyinstaller-6.21.0.tar.gz", hash = "sha256:bb9fab705983e393a2d1cac77d6972513057ad800215fd861dc15ff5272e98fd", size = 4061519 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/4a/53cf98bf66daed012dc9cd78c8203f19a675d696f2fc12afcf8c5049a0e0/pyinstaller-6.21.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:327d132389f37912609e01be62810cf96b5aa95b613903e4b8692e0d12fb0eda", size = 1052350, upload-time = "2026-06-13T14:13:55.88Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/83/b591295c352ef464c50b4c6ffff1c4f771d875c9e833f578d1b9f564f6b3/pyinstaller-6.21.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7071d4b094d5b40deeef5fa3d3b98a1b846087f7562b49209663d5f9281fe251", size = 748477, upload-time = "2026-06-13T14:14:00.327Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/8f/88fff4e403873b1e22286911350e75ff00db014aa08e57045da9d4328993/pyinstaller-6.21.0-py3-none-manylinux2014_i686.whl", hash = "sha256:6b6374d652107dd4a2eeece903ff82bb4045bb5e1006c5a158a6dcdbefe84bf2", size = 760877, upload-time = "2026-06-13T14:14:04.836Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/13/f0e48fbdfd1d05d948157121cea8b1b823dcb89efe6934b71fdd8bdb3f0f/pyinstaller-6.21.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:4e3108b3f02384560da70e39b8bf22b0ad597d02bd68a40d76ea91c1cfa00cad", size = 759194, upload-time = "2026-06-13T14:14:10.61Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/d5/ea7878cf9924ed30d946d8288777424e6d069d94f5bde56b4d0890069664/pyinstaller-6.21.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:697532279f535ad572bda613db4f821540e235c7854ca6da4d3bf0373f4415ee", size = 754979, upload-time = "2026-06-13T14:14:15.226Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/09/51b8905714b733bac66dbc041a7821372d70d888d273ae474c4037d4202d/pyinstaller-6.21.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:605169523a6b5ace39f13dfbff21add9f2bc43df99c7daf9394fefb2c45e8b6f", size = 754812, upload-time = "2026-06-13T14:14:20.264Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/43/d77779439d8c6c2e27a77bcfbd1d5cc0f568ebb611bb472b11af81b5f177/pyinstaller-6.21.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5fa56746c1e76f93634d018502301378a2d0c382553d37d8c3c34ff436c12dd1", size = 753887, upload-time = "2026-06-13T14:14:25.268Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/8f/c22df1f6837784ac349057ba693f08e7b1ca7a0e06f9c33c63bc6280007b/pyinstaller-6.21.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:42395ec76df8e8120c36b13339d9db8cab83e316a12839ee303cc00fc941bb74", size = 753779, upload-time = "2026-06-13T14:14:29.445Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/76/1ce8a27ce62ba8cf3a87c9ce6d575610f4e55d7cb0123e7512fc3f4b921a/pyinstaller-6.21.0-py3-none-win32.whl", hash = "sha256:c6b28d30d8fd99ce162ff3aab5013ed44dbfb747566b1f01b9bed7964d7c14e9", size = 1336462, upload-time = "2026-06-13T14:14:35.785Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/fa/ca1d7e5257dd8566a9dfc0dfb02f8a8075eeb53d4b2d3c579f1276759042/pyinstaller-6.21.0-py3-none-win_amd64.whl", hash = "sha256:7fae06c494ce0ebfe6bd3055c0e409def884f63af2e3705d06bd431ad9237fc7", size = 1397487, upload-time = "2026-06-13T14:14:42.328Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/75/21b51523ce8d96629b71311775a0a65f5f5a872124ab0de33e5c848f8bff/pyinstaller-6.21.0-py3-none-win_arm64.whl", hash = "sha256:f13c95c9c03fb567217135919f93815c305813126780b0ed6e0123cb8acaf025", size = 1346094, upload-time = "2026-06-13T14:14:48.914Z" }, + { url = "https://files.pythonhosted.org/packages/0c/4a/53cf98bf66daed012dc9cd78c8203f19a675d696f2fc12afcf8c5049a0e0/pyinstaller-6.21.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:327d132389f37912609e01be62810cf96b5aa95b613903e4b8692e0d12fb0eda", size = 1052350 }, + { url = "https://files.pythonhosted.org/packages/30/83/b591295c352ef464c50b4c6ffff1c4f771d875c9e833f578d1b9f564f6b3/pyinstaller-6.21.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7071d4b094d5b40deeef5fa3d3b98a1b846087f7562b49209663d5f9281fe251", size = 748477 }, + { url = "https://files.pythonhosted.org/packages/3d/8f/88fff4e403873b1e22286911350e75ff00db014aa08e57045da9d4328993/pyinstaller-6.21.0-py3-none-manylinux2014_i686.whl", hash = "sha256:6b6374d652107dd4a2eeece903ff82bb4045bb5e1006c5a158a6dcdbefe84bf2", size = 760877 }, + { url = "https://files.pythonhosted.org/packages/8a/13/f0e48fbdfd1d05d948157121cea8b1b823dcb89efe6934b71fdd8bdb3f0f/pyinstaller-6.21.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:4e3108b3f02384560da70e39b8bf22b0ad597d02bd68a40d76ea91c1cfa00cad", size = 759194 }, + { url = "https://files.pythonhosted.org/packages/dd/d5/ea7878cf9924ed30d946d8288777424e6d069d94f5bde56b4d0890069664/pyinstaller-6.21.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:697532279f535ad572bda613db4f821540e235c7854ca6da4d3bf0373f4415ee", size = 754979 }, + { url = "https://files.pythonhosted.org/packages/9f/09/51b8905714b733bac66dbc041a7821372d70d888d273ae474c4037d4202d/pyinstaller-6.21.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:605169523a6b5ace39f13dfbff21add9f2bc43df99c7daf9394fefb2c45e8b6f", size = 754812 }, + { url = "https://files.pythonhosted.org/packages/4b/43/d77779439d8c6c2e27a77bcfbd1d5cc0f568ebb611bb472b11af81b5f177/pyinstaller-6.21.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5fa56746c1e76f93634d018502301378a2d0c382553d37d8c3c34ff436c12dd1", size = 753887 }, + { url = "https://files.pythonhosted.org/packages/51/8f/c22df1f6837784ac349057ba693f08e7b1ca7a0e06f9c33c63bc6280007b/pyinstaller-6.21.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:42395ec76df8e8120c36b13339d9db8cab83e316a12839ee303cc00fc941bb74", size = 753779 }, + { url = "https://files.pythonhosted.org/packages/c9/76/1ce8a27ce62ba8cf3a87c9ce6d575610f4e55d7cb0123e7512fc3f4b921a/pyinstaller-6.21.0-py3-none-win32.whl", hash = "sha256:c6b28d30d8fd99ce162ff3aab5013ed44dbfb747566b1f01b9bed7964d7c14e9", size = 1336462 }, + { url = "https://files.pythonhosted.org/packages/c1/fa/ca1d7e5257dd8566a9dfc0dfb02f8a8075eeb53d4b2d3c579f1276759042/pyinstaller-6.21.0-py3-none-win_amd64.whl", hash = "sha256:7fae06c494ce0ebfe6bd3055c0e409def884f63af2e3705d06bd431ad9237fc7", size = 1397487 }, + { url = "https://files.pythonhosted.org/packages/dc/75/21b51523ce8d96629b71311775a0a65f5f5a872124ab0de33e5c848f8bff/pyinstaller-6.21.0-py3-none-win_arm64.whl", hash = "sha256:f13c95c9c03fb567217135919f93815c305813126780b0ed6e0123cb8acaf025", size = 1346094 }, ] [[package]] name = "pyinstaller-hooks-contrib" version = "2026.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/5b/c9fe0db5e83ee1c39b2258fa21d23b15e1a60786b6c5990ee5074ead8bb6/pyinstaller_hooks_contrib-2026.6.tar.gz", hash = "sha256:bef5002c32f4f50bd55b005da12cff64eca8783e7eaf86a06a62410164bab725", size = 173354, upload-time = "2026-06-08T22:37:16.152Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/5b/c9fe0db5e83ee1c39b2258fa21d23b15e1a60786b6c5990ee5074ead8bb6/pyinstaller_hooks_contrib-2026.6.tar.gz", hash = "sha256:bef5002c32f4f50bd55b005da12cff64eca8783e7eaf86a06a62410164bab725", size = 173354 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/31/f2d7343d8ed5f7c4678377886f6ce533e6eaaa131b252ce950114c2a7efa/pyinstaller_hooks_contrib-2026.6-py3-none-any.whl", hash = "sha256:fd13b8ac126b35361175edacd41a0d97080b75dd5f4b594ecefefff969509dd3", size = 457159, upload-time = "2026-06-08T22:37:14.722Z" }, + { url = "https://files.pythonhosted.org/packages/e7/31/f2d7343d8ed5f7c4678377886f6ce533e6eaaa131b252ce950114c2a7efa/pyinstaller_hooks_contrib-2026.6-py3-none-any.whl", hash = "sha256:fd13b8ac126b35361175edacd41a0d97080b75dd5f4b594ecefefff969509dd3", size = 457159 }, ] [[package]] name = "pyinstrument" version = "5.1.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/7f/d3c4ef7c43f3294bd5a475dfa6f295a9fee5243c292d5c8122044fa83bcb/pyinstrument-5.1.2.tar.gz", hash = "sha256:af149d672da9493fa37334a1cc68f7b80c3e6cb9fd99b9e426c447db5c650bf0", size = 266889, upload-time = "2026-01-04T18:38:58.464Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/7f/d3c4ef7c43f3294bd5a475dfa6f295a9fee5243c292d5c8122044fa83bcb/pyinstrument-5.1.2.tar.gz", hash = "sha256:af149d672da9493fa37334a1cc68f7b80c3e6cb9fd99b9e426c447db5c650bf0", size = 266889 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/8e/b9aea969eec67c129652000446384d550a0df45c297adc9fd74da2f8482c/pyinstrument-5.1.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7b8bab2334bf1d4c9e92d61db574300b914b594588a6b6dd67c45450152dfc29", size = 131418, upload-time = "2026-01-04T18:37:58.642Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/62/76418eb29b5591f3e5500369a6777ce928135c3aa6ccdb0c861a9c6ca93b/pyinstrument-5.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:13dcc138a61298ef4994b7aebff509d2c06db89dfd6e2021f0b9cd96aaa44ec3", size = 124448, upload-time = "2026-01-04T18:37:59.95Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/73/874bccc04bcf6f4babc3de1a9568e209e7e40998563974f5030b0fb4d3e0/pyinstrument-5.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8abd4a7ffa2e7f9e00039a5e549e8eebc80d7ca8d43f0fb51a50ff2b117ce4a", size = 149853, upload-time = "2026-01-04T18:38:01.405Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/85/268446c4388d77ff4abdeaff202356e1527b3ff9576f5587443a24980bec/pyinstrument-5.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb3a05108edebc30f31e2c69c904576042f1158b2513ab80adc08f7848a7a8f0", size = 148641, upload-time = "2026-01-04T18:38:03.086Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/15/4f8dea3381483e68d00582a9b823a21a088acfe77a847a7991a1a8feed76/pyinstrument-5.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f70d588b53f3f35829d1d1ddfa05e07fcebf1434b3b1509d542ca317d8e9a2a5", size = 148674, upload-time = "2026-01-04T18:38:04.805Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/61/72c180454b6511d5b90166f8828e1bab3b45d0489952a1fe48c5c585233d/pyinstrument-5.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b007327e0d6a6a01d5064883dd27c19996f044ce7488d507826fee7884e6a32e", size = 148315, upload-time = "2026-01-04T18:38:06.114Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/f0/4c27cebddf22a8840bd8b419366bb321ce41f921ca1893e309c932ab28bf/pyinstrument-5.1.2-cp313-cp313-win32.whl", hash = "sha256:9ba0e6b17a7e86c3dc02d208e4c25506e8f914d9964ae89449f1f37f0b70abc0", size = 125926, upload-time = "2026-01-04T18:38:07.507Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/20/6b1bee88ddef065b0df3a3ba4ba60ed8a9ca443d5cded7152a8a9750914f/pyinstrument-5.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:660d7fc486a839814db0b2f716bc13d8b99b9c780aaeb47f74a70a34adc02a7b", size = 126678, upload-time = "2026-01-04T18:38:08.826Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8e/b9aea969eec67c129652000446384d550a0df45c297adc9fd74da2f8482c/pyinstrument-5.1.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7b8bab2334bf1d4c9e92d61db574300b914b594588a6b6dd67c45450152dfc29", size = 131418 }, + { url = "https://files.pythonhosted.org/packages/8f/62/76418eb29b5591f3e5500369a6777ce928135c3aa6ccdb0c861a9c6ca93b/pyinstrument-5.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:13dcc138a61298ef4994b7aebff509d2c06db89dfd6e2021f0b9cd96aaa44ec3", size = 124448 }, + { url = "https://files.pythonhosted.org/packages/07/73/874bccc04bcf6f4babc3de1a9568e209e7e40998563974f5030b0fb4d3e0/pyinstrument-5.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8abd4a7ffa2e7f9e00039a5e549e8eebc80d7ca8d43f0fb51a50ff2b117ce4a", size = 149853 }, + { url = "https://files.pythonhosted.org/packages/cf/85/268446c4388d77ff4abdeaff202356e1527b3ff9576f5587443a24980bec/pyinstrument-5.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb3a05108edebc30f31e2c69c904576042f1158b2513ab80adc08f7848a7a8f0", size = 148641 }, + { url = "https://files.pythonhosted.org/packages/fc/15/4f8dea3381483e68d00582a9b823a21a088acfe77a847a7991a1a8feed76/pyinstrument-5.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f70d588b53f3f35829d1d1ddfa05e07fcebf1434b3b1509d542ca317d8e9a2a5", size = 148674 }, + { url = "https://files.pythonhosted.org/packages/fa/61/72c180454b6511d5b90166f8828e1bab3b45d0489952a1fe48c5c585233d/pyinstrument-5.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b007327e0d6a6a01d5064883dd27c19996f044ce7488d507826fee7884e6a32e", size = 148315 }, + { url = "https://files.pythonhosted.org/packages/2c/f0/4c27cebddf22a8840bd8b419366bb321ce41f921ca1893e309c932ab28bf/pyinstrument-5.1.2-cp313-cp313-win32.whl", hash = "sha256:9ba0e6b17a7e86c3dc02d208e4c25506e8f914d9964ae89449f1f37f0b70abc0", size = 125926 }, + { url = "https://files.pythonhosted.org/packages/6c/20/6b1bee88ddef065b0df3a3ba4ba60ed8a9ca443d5cded7152a8a9750914f/pyinstrument-5.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:660d7fc486a839814db0b2f716bc13d8b99b9c780aaeb47f74a70a34adc02a7b", size = 126678 }, ] [[package]] name = "pyjwt" version = "2.13.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274 }, ] [package.optional-dependencies] @@ -4112,66 +3898,66 @@ crypto = [ [[package]] name = "pymdown-extensions" version = "11.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/a9/5f0c535ba3b08fe09270c16808e053a968868242ecbd5676d4e3a488bf28/pymdown_extensions-11.0.1.tar.gz", hash = "sha256:dd2905ae6fc5b75582fafb139a1266ffc754705efa902aa50067fa7ff4f94ec0", size = 857113, upload-time = "2026-07-02T17:59:22.955Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/a9/5f0c535ba3b08fe09270c16808e053a968868242ecbd5676d4e3a488bf28/pymdown_extensions-11.0.1.tar.gz", hash = "sha256:dd2905ae6fc5b75582fafb139a1266ffc754705efa902aa50067fa7ff4f94ec0", size = 857113 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/54/da572c98c0b77626a91b5d3b89f0231d8bff5125c225420908632f8b342d/pymdown_extensions-11.0.1-py3-none-any.whl", hash = "sha256:db3943a62bab7e03af1364f0c4083e64b91fb097675a4b6cceccfbe9a77e5eb2", size = 269455, upload-time = "2026-07-02T17:59:21.271Z" }, + { url = "https://files.pythonhosted.org/packages/d6/54/da572c98c0b77626a91b5d3b89f0231d8bff5125c225420908632f8b342d/pymdown_extensions-11.0.1-py3-none-any.whl", hash = "sha256:db3943a62bab7e03af1364f0c4083e64b91fb097675a4b6cceccfbe9a77e5eb2", size = 269455 }, ] [[package]] name = "pypdf" version = "6.14.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/72/7dfd5ff1c9c37de97a731701f51af091325f123d9d4270361c9c69e4431f/pypdf-6.14.2.tar.gz", hash = "sha256:7873f502fe4385e79539b21d872392dc0c4e3714327c15881cbc7fbfd1f95b25", size = 6491182, upload-time = "2026-06-23T14:18:30.859Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/72/7dfd5ff1c9c37de97a731701f51af091325f123d9d4270361c9c69e4431f/pypdf-6.14.2.tar.gz", hash = "sha256:7873f502fe4385e79539b21d872392dc0c4e3714327c15881cbc7fbfd1f95b25", size = 6491182 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/e6/136aa8993a2ae7214e0b0ef2edaa0d2e08d1d4e4982635b08a835ff31ec8/pypdf-6.14.2-py3-none-any.whl", hash = "sha256:3f07891af76dc002657e04993ab9b4de81de29f9013b9761d0b7968bff12e946", size = 349514, upload-time = "2026-06-23T14:18:28.867Z" }, + { url = "https://files.pythonhosted.org/packages/49/e6/136aa8993a2ae7214e0b0ef2edaa0d2e08d1d4e4982635b08a835ff31ec8/pypdf-6.14.2-py3-none-any.whl", hash = "sha256:3f07891af76dc002657e04993ab9b4de81de29f9013b9761d0b7968bff12e946", size = 349514 }, ] [[package]] name = "pyperclip" version = "1.11.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063 }, ] [[package]] name = "pyproject-hooks" version = "1.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216 }, ] [[package]] name = "pyreadline3" version = "3.5.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/6d/f94028646d7bbe6d9d873c47ee7c246f2d29129d253f0d96cb6fcab70733/pyreadline3-3.5.6.tar.gz", hash = "sha256:61e53218b99656091ddb077df9e71f25850e72e030b6183b39c9b7e6e4f4a9bf", size = 100368, upload-time = "2026-05-14T17:55:04.471Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/6d/f94028646d7bbe6d9d873c47ee7c246f2d29129d253f0d96cb6fcab70733/pyreadline3-3.5.6.tar.gz", hash = "sha256:61e53218b99656091ddb077df9e71f25850e72e030b6183b39c9b7e6e4f4a9bf", size = 100368 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/5e/35c856e186b74678c24927847ad9895a51f1bc02a0c6126477a6c6040064/pyreadline3-3.5.6-py3-none-any.whl", hash = "sha256:8449b734232e42a5dcd74048e39b60db2839a4c38cf3ae2bf7707d58b5389c0d", size = 85243, upload-time = "2026-05-14T17:55:03.262Z" }, + { url = "https://files.pythonhosted.org/packages/f7/5e/35c856e186b74678c24927847ad9895a51f1bc02a0c6126477a6c6040064/pyreadline3-3.5.6-py3-none-any.whl", hash = "sha256:8449b734232e42a5dcd74048e39b60db2839a4c38cf3ae2bf7707d58b5389c0d", size = 85243 }, ] [[package]] name = "pysher" version = "1.0.8" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "websocket-client" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/a0/d0638470df605ce266991fb04f74c69ab1bed3b90ac3838e9c3c8b69b66a/Pysher-1.0.8.tar.gz", hash = "sha256:7849c56032b208e49df67d7bd8d49029a69042ab0bb45b2ed59fa08f11ac5988", size = 9071, upload-time = "2022-10-10T13:41:09.936Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/a0/d0638470df605ce266991fb04f74c69ab1bed3b90ac3838e9c3c8b69b66a/Pysher-1.0.8.tar.gz", hash = "sha256:7849c56032b208e49df67d7bd8d49029a69042ab0bb45b2ed59fa08f11ac5988", size = 9071 } [[package]] name = "pytest" version = "9.1.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "iniconfig" }, @@ -4179,162 +3965,162 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536 }, ] [[package]] name = "pytest-asyncio" version = "1.4.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930 }, ] [[package]] name = "pytest-cov" version = "7.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage" }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876 }, ] [[package]] name = "pytest-docker" version = "3.2.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "pytest" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/05/b7e47dc3e01b505838372e296bd780180b3b699a9a134bb8d6be85f3d567/pytest_docker-3.2.5.tar.gz", hash = "sha256:c9662567522911280b394af4da2edd57facaf644494601fac962ff1e396d7ab6", size = 13717, upload-time = "2025-11-12T13:42:19.641Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/05/b7e47dc3e01b505838372e296bd780180b3b699a9a134bb8d6be85f3d567/pytest_docker-3.2.5.tar.gz", hash = "sha256:c9662567522911280b394af4da2edd57facaf644494601fac962ff1e396d7ab6", size = 13717 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/e4/3a76a393f808edb0ee08ecc25e5c00bce0522a45b8fcf8693ec9441739c8/pytest_docker-3.2.5-py3-none-any.whl", hash = "sha256:79f3d209f928f45d4385cb825944861bc8a8cccd309804d9c9cd63bcef03edba", size = 8724, upload-time = "2025-11-12T13:42:18.631Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e4/3a76a393f808edb0ee08ecc25e5c00bce0522a45b8fcf8693ec9441739c8/pytest_docker-3.2.5-py3-none-any.whl", hash = "sha256:79f3d209f928f45d4385cb825944861bc8a8cccd309804d9c9cd63bcef03edba", size = 8724 }, ] [[package]] name = "pytest-recording" version = "0.13.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, { name = "vcrpy" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576, upload-time = "2025-05-08T10:41:11.231Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723, upload-time = "2025-05-08T10:41:09.684Z" }, + { url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 }, ] [[package]] name = "pytest-rerunfailures" version = "16.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "pytest" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/60/a90ca1cc6cffcb97b4260ed0ad2b7934b999d7c48abe4ea0840344862a3b/pytest_rerunfailures-16.4.tar.gz", hash = "sha256:8222d17c37eb7b9e4d6fc96a3c724ff4e1a5c97a5cc7cbb2c19e9282cfd21a11", size = 36635, upload-time = "2026-07-01T06:30:56.813Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/60/a90ca1cc6cffcb97b4260ed0ad2b7934b999d7c48abe4ea0840344862a3b/pytest_rerunfailures-16.4.tar.gz", hash = "sha256:8222d17c37eb7b9e4d6fc96a3c724ff4e1a5c97a5cc7cbb2c19e9282cfd21a11", size = 36635 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/93/3cdcc4033444e822e01b573414b03fd37fd5533070c750477b8f5fa5224b/pytest_rerunfailures-16.4-py3-none-any.whl", hash = "sha256:f69b5beb39622c90d1e44bd945d826eff6db545dcf0b68f52b7e4ad15eaf6d6c", size = 16955, upload-time = "2026-07-01T06:30:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/ce/93/3cdcc4033444e822e01b573414b03fd37fd5533070c750477b8f5fa5224b/pytest_rerunfailures-16.4-py3-none-any.whl", hash = "sha256:f69b5beb39622c90d1e44bd945d826eff6db545dcf0b68f52b7e4ad15eaf6d6c", size = 16955 }, ] [[package]] name = "pytest-timeout" version = "2.4.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382 }, ] [[package]] name = "pytest-xdist" version = "3.8.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "execnet" }, { name = "pytest" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396 }, ] [[package]] name = "python-dateutil" version = "2.9.0.post0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] [[package]] name = "python-dotenv" version = "1.2.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101 }, ] [[package]] name = "python-json-logger" version = "4.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573, upload-time = "2026-03-29T04:39:56.805Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021, upload-time = "2026-03-29T04:39:55.266Z" }, + { url = "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021 }, ] [[package]] name = "python-multipart" version = "0.0.32" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042 }, ] [[package]] name = "python-slugify" version = "8.0.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "text-unidecode" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921, upload-time = "2024-02-08T18:32:45.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051, upload-time = "2024-02-08T18:32:43.911Z" }, + { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051 }, ] [[package]] name = "python-telegram-bot" version = "22.8" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/77/153517bb1ac1bba670c6fb1dbf09e1fd0730494b1705934e715391413a0d/python_telegram_bot-22.8.tar.gz", hash = "sha256:f9d3847fcb23ee603477e442800b33bb4adf851a73e0619d2050be879decf1ef", size = 1551700, upload-time = "2026-06-12T08:10:29.1Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/77/153517bb1ac1bba670c6fb1dbf09e1fd0730494b1705934e715391413a0d/python_telegram_bot-22.8.tar.gz", hash = "sha256:f9d3847fcb23ee603477e442800b33bb4adf851a73e0619d2050be879decf1ef", size = 1551700 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/7c/ed7d4dd94280bd434173cae9f7a7aedaaab9af128ae4f494423a5687c820/python_telegram_bot-22.8-py3-none-any.whl", hash = "sha256:42373918097f1b837cc4e717d588c19ea79651497ec712bb5b0c76e5e63c50e1", size = 769397, upload-time = "2026-06-12T08:10:27.066Z" }, + { url = "https://files.pythonhosted.org/packages/60/7c/ed7d4dd94280bd434173cae9f7a7aedaaab9af128ae4f494423a5687c820/python_telegram_bot-22.8-py3-none-any.whl", hash = "sha256:42373918097f1b837cc4e717d588c19ea79651497ec712bb5b0c76e5e63c50e1", size = 769397 }, ] [package.optional-dependencies] @@ -4345,359 +4131,359 @@ socks = [ [[package]] name = "pytokens" version = "0.4.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720 }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204 }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423 }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859 }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520 }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729 }, ] [[package]] name = "pytz" version = "2026.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141 }, ] [[package]] name = "pywin32" version = "312" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640, upload-time = "2026-06-04T07:49:41.083Z" }, + { url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184 }, + { url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298 }, + { url = "https://files.pythonhosted.org/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640 }, ] [[package]] name = "pywin32-ctypes" version = "0.2.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 }, ] [[package]] name = "pyyaml" version = "6.0.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, ] [[package]] name = "pyyaml-env-tag" version = "1.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722 }, ] [[package]] name = "pyyaml-include" version = "2.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fsspec" }, { name = "pyyaml" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/8c/4bdc1bd9676e9eb49237b3750562e9794b7585281448909fa1837c92ca27/pyyaml_include-2.2.tar.gz", hash = "sha256:6f0c7e2ac56cdd9cc305b04122817b55514e6ce8584869fae2bc2a4ef2e0d40f", size = 29854, upload-time = "2024-11-09T09:36:16.915Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/8c/4bdc1bd9676e9eb49237b3750562e9794b7585281448909fa1837c92ca27/pyyaml_include-2.2.tar.gz", hash = "sha256:6f0c7e2ac56cdd9cc305b04122817b55514e6ce8584869fae2bc2a4ef2e0d40f", size = 29854 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/1f/ae83ff547e70cd3aeefe2ebaeeef41c4e1fd5ed2453396d249d17c3a7ead/pyyaml_include-2.2-py3-none-any.whl", hash = "sha256:489fff69f78bad8b9509d006297a0140fd91382a66775b8b1da0ce7e126c1815", size = 29565, upload-time = "2024-11-09T09:36:15.241Z" }, + { url = "https://files.pythonhosted.org/packages/56/1f/ae83ff547e70cd3aeefe2ebaeeef41c4e1fd5ed2453396d249d17c3a7ead/pyyaml_include-2.2-py3-none-any.whl", hash = "sha256:489fff69f78bad8b9509d006297a0140fd91382a66775b8b1da0ce7e126c1815", size = 29565 }, ] [[package]] name = "redis" version = "8.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/c3/928b290c2c0ca99ab96eea5b4ff8f30be8112b075301a7d3ba214a3c8c12/redis-8.0.1.tar.gz", hash = "sha256:afc5a7a2f5a084f5b1880dec548dd45be17db7e43c82a30d84f952aefb05cfb0", size = 5114170, upload-time = "2026-06-23T14:52:37.728Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c3/928b290c2c0ca99ab96eea5b4ff8f30be8112b075301a7d3ba214a3c8c12/redis-8.0.1.tar.gz", hash = "sha256:afc5a7a2f5a084f5b1880dec548dd45be17db7e43c82a30d84f952aefb05cfb0", size = 5114170 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/0a/c2345ebf1ebe70840ce3f6c6ee612f8fa749cfbd1b03069c53bf0c62aaad/redis-8.0.1-py3-none-any.whl", hash = "sha256:47daa35a058c23468d6437f17a8c76882cb316b838ef763036af99b96cedd743", size = 502406, upload-time = "2026-06-23T14:52:36.137Z" }, + { url = "https://files.pythonhosted.org/packages/fd/0a/c2345ebf1ebe70840ce3f6c6ee612f8fa749cfbd1b03069c53bf0c62aaad/redis-8.0.1-py3-none-any.whl", hash = "sha256:47daa35a058c23468d6437f17a8c76882cb316b838ef763036af99b96cedd743", size = 502406 }, ] [[package]] name = "referencing" version = "0.37.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, ] [[package]] name = "regex" version = "2026.7.19" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/98/04b13f1ddfb63158025291c02e03eb42fbb7acb51d091d541050eb4e35e8/regex-2026.7.19.tar.gz", hash = "sha256:7e77b324909c1617cbb4c668677e2c6ae13f44d7c1de0d4f15f2e3c10f3315b5", size = 416440, upload-time = "2026-07-19T00:19:48.923Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/3d/84165e4299ff76f3a40fe1f2abf939e976f693383a08d2beea6af62bd2c1/regex-2026.7.19-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f035d9dc1d25eff9d361456572231c7d27b5ccd473ca7dc0adfce732bd006d40", size = 496552, upload-time = "2026-07-19T00:17:36.808Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/a2/a65293e6e4cf28eb7ee1be5335a5386c40d6742e9f47fafc8fec785e16c7/regex-2026.7.19-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c42572142ed0b9d5d261ba727157c426510da78e20828b66bbb855098b8a4e38", size = 296983, upload-time = "2026-07-19T00:17:38.816Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/47/2d0564e93d87bc48618360ddca232a2ca612bbdf53ce8465d45ca5ce14ee/regex-2026.7.19-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40b34dd88658e4fedd2fddbf0275ac970d00614b731357f425722a3ed1983d11", size = 291832, upload-time = "2026-07-19T00:17:40.726Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/cd/42dfbabff3dfc9603c501c0e2e2c5adbb09d127b267bf5348de0af338c15/regex-2026.7.19-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c41c63992bf1874cebb6e7f56fd7d3c007924659a604ae3d90e427d40d4fd13", size = 796775, upload-time = "2026-07-19T00:17:42.382Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/5d/f6a4839f2b934e3eed5973fd07f5929ee97d4c98939fb275ea23c274ee16/regex-2026.7.19-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d3372064506b94dd2c67c845f2db8062e9e9ba84d04e33cb96d7d33c11fe1ae", size = 865687, upload-time = "2026-07-19T00:17:44.185Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/b0/b47d6c36049bc59806a50bd4c86ced70bbe058d787f80281b1d7a9b0e024/regex-2026.7.19-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fce7760bf283405b2c7999cab3da4e72f7deca6396013115e3f7a955db9760da", size = 911962, upload-time = "2026-07-19T00:17:46.442Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/be/ff61f28f9273658cfe23acbbac5217221f6519960ed401e61dfdab12bc35/regex-2026.7.19-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0d702548d89d572b2929879bc883bb7a4c4709efafe4512cadee56c55c9bd15", size = 801817, upload-time = "2026-07-19T00:17:48.25Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/bb/8b4f7f26b333f9f79e1b453613c39bb4776f51d38ae66dd0ba31d6b354ca/regex-2026.7.19-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d446c6ac40bb6e05025ccee55b84d80fe9bf8e93010ffc4bb9484f13d498835f", size = 776908, upload-time = "2026-07-19T00:17:50.183Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/13/610110fc5921d380516d03c26b652555f08aa0d23ea78a771231873c3638/regex-2026.7.19-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4c3501bfa814ab07b5580741f9bf78dfdfe146a04057f82df9e2402d2a975939", size = 784426, upload-time = "2026-07-19T00:17:52.454Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/f5/1ef9e2a83a5947c57ebff0b377cb5727c3d5ec1992317a320d035cd0dbb6/regex-2026.7.19-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c4585c3e64b4f9e583b4d2683f18f5d5d872b3d71dcf24594b74ecc23602fa96", size = 860600, upload-time = "2026-07-19T00:17:54.229Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/02/073af33a3ec149241d11c80acea91e722aa0adbf05addd50f251c4fe89c3/regex-2026.7.19-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:571fde9741eb0ccde23dd4e0c1d50fbae910e901fa7e629faf39b2dda740d220", size = 765950, upload-time = "2026-07-19T00:17:56.041Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/a9/d1e9f819dc394a568ef370cd56cf25394e957a2235f8370f23b576e5a475/regex-2026.7.19-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:15b364b9b98d6d2fe1a85034c23a3180ff913f46caddc3895f6fd65186255ccc", size = 851794, upload-time = "2026-07-19T00:17:57.897Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/3a/8ae83eda7579feacdf984e71fb9e70635fb6f832eeddca58427ec4fca926/regex-2026.7.19-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffd8893ccc1c2fce6e0d6ca402d716fe1b29db70c7132609a05955e31b2aa8f2", size = 789845, upload-time = "2026-07-19T00:17:59.97Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/23/c195cbfe5a75fdec64d8f6554fd15237b837919d2c61bdc141d7c807b08b/regex-2026.7.19-cp313-cp313-win32.whl", hash = "sha256:f0fa4fa9c3632d708742baf2282f2055c11d888a790362670a403cbf48a2c404", size = 267135, upload-time = "2026-07-19T00:18:01.958Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/80/a11de8404b7272b70acb45c1c05987cce60b45d5693da2e176f0e390d564/regex-2026.7.19-cp313-cp313-win_amd64.whl", hash = "sha256:d51ffd3427640fa2da6ade574ceba932f210ad095f65fcc450a2b0a0d454868e", size = 277747, upload-time = "2026-07-19T00:18:04.121Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/29/0f5c8eff1b4f1f3d83276d365fccecf666afcc7d947420943bf394d07adb/regex-2026.7.19-cp313-cp313-win_arm64.whl", hash = "sha256:c670fe7be5b6020b76bc6e8d2196074657e1327595bca93a389e1a76ab130ad8", size = 277129, upload-time = "2026-07-19T00:18:05.821Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/4c/44b74742052cedda40f9ae469532a037112f7311a36669a891fba8984bb0/regex-2026.7.19-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db47b561c9afd884baa1f96f797c9ca369872c4b65912bc691cfa99e68340af2", size = 501134, upload-time = "2026-07-19T00:18:07.567Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/45/bbd038b5e39ee5613a5a689290145b40058cc152c41de9cc23639d2b9734/regex-2026.7.19-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65dcd28d3eba2ab7c2fd906485cc301392b47cc2234790d27d4e4814e02cdfda", size = 299418, upload-time = "2026-07-19T00:18:09.38Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/38/c5bde94b4cedfd5850d64c3f08222d8e1600e84f6ee71d9b44b4b8163f74/regex-2026.7.19-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f2e7f8e2ab6c2922be02c7ec45185aa5bd771e2e57b95455ee343a44d8130dff", size = 294486, upload-time = "2026-07-19T00:18:11.188Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/6a/2f5e107cb26c960b781967178899daf2787a7ab151844ed3c01d6fc95474/regex-2026.7.19-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe31f28c94402043161876a258a9c6f757cb485905c7614ce8d6cd40e6b7bdc1", size = 811643, upload-time = "2026-07-19T00:18:12.975Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/d4/a2f963406d7d73a62eed84ba05a258afb6cad1b21aa4517443ce40506b78/regex-2026.7.19-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8f6fa298bb4f7f58a33334406218ba74716e68feddf5e4e54cd5d8082705abf", size = 871081, upload-time = "2026-07-19T00:18:14.733Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/a3/44be546340bedb15f13063f5e7fe16793ea4d9ea2e805d09bd174ac27724/regex-2026.7.19-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cc1b2440423a851fad781309dd87843868f4f66a6bcd1ddb9225cf4ec2c84732", size = 917372, upload-time = "2026-07-19T00:18:16.724Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/f6/e0870b0fd2a40dba0074e4b76e514b21313d37946c9248453e34ec43923e/regex-2026.7.19-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ac59a0900474a52b7c04af8196affc22bd9842acb0950df12f7b813e983609a", size = 816089, upload-time = "2026-07-19T00:18:18.617Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/27/957e8e22690ad6634572b39b71f130a6105f4d0718bb16849eac00fff147/regex-2026.7.19-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4896db1f4ce0576765b8272aa922df324e0f5b9bb2c3d03044ff32a7234a9aba", size = 785206, upload-time = "2026-07-19T00:18:20.464Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/a4/186e410941e731037c01166069ab86da9f65e8f8110c18009ccf4bd623ee/regex-2026.7.19-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4e6883a021db30511d9fb8cfb0f222ce1f2c369f7d4d8b0448f449a93ba0bdfc", size = 800431, upload-time = "2026-07-19T00:18:22.716Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/9f/e4e10e023d291d64a33e246610b724493bf1ce98e0e59c9b7c837e5acfb7/regex-2026.7.19-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:09523a592938aa9f587fb74467c63ff0cf88fc3df14c82ab0f0517dcf76aaa62", size = 864906, upload-time = "2026-07-19T00:18:24.772Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/57/ccb20b6be5f1f52a053d1ba2a8f7a077edb9d918248b8490d7506c6832b3/regex-2026.7.19-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1ebac3474b8589fce2f9b225b650afd61448f7c73a5d0255a10cc6366471aed1", size = 773559, upload-time = "2026-07-19T00:18:27.008Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/82/f3b263cf8fad927dc102891da8502e718b7ff9d19af7a2a07c03865d7188/regex-2026.7.19-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4a0530bb1b8c1c985e7e2122e2b4d3aedd8a3c21c6bfddae6767c4405668b56e", size = 857739, upload-time = "2026-07-19T00:18:29.107Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/2e/1687bd1b6c2aed5e672ccf845fc11557821fe7366d921b50889ea5ce57bf/regex-2026.7.19-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef7eeb108c47ce7bcc9513e51bcb1bf57e8f483d52fce68a8642e3527141ae0", size = 804522, upload-time = "2026-07-19T00:18:31.362Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/7c/cc4e7655181b2d9235b704f2c5e19d8eff002bbc437bae59baee0e381aca/regex-2026.7.19-cp313-cp313t-win32.whl", hash = "sha256:64b6ca7391a1395c2638dd5c7456d67bea44fc6c5e8e92c5dc8aa6a8f23292b4", size = 269141, upload-time = "2026-07-19T00:18:33.479Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/14/961b4c7b05a2391c32dbc85e27773076671ef8f97f36cec70fe414734c02/regex-2026.7.19-cp313-cp313t-win_amd64.whl", hash = "sha256:f04b9f56b0e0614c0126be12c2c2d9f8850c1e57af302bd0a63bed379d4af974", size = 280036, upload-time = "2026-07-19T00:18:35.419Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/67/795644550d788ddbb6dc458c95895f8009978ea6d6ea76b005eb3f45e8c9/regex-2026.7.19-cp313-cp313t-win_arm64.whl", hash = "sha256:fcee38cd8e5089d6d4f048ba1233b3ad76e5954f545382180889112ff5cb712d", size = 279394, upload-time = "2026-07-19T00:18:37.454Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/98/04b13f1ddfb63158025291c02e03eb42fbb7acb51d091d541050eb4e35e8/regex-2026.7.19.tar.gz", hash = "sha256:7e77b324909c1617cbb4c668677e2c6ae13f44d7c1de0d4f15f2e3c10f3315b5", size = 416440 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/3d/84165e4299ff76f3a40fe1f2abf939e976f693383a08d2beea6af62bd2c1/regex-2026.7.19-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f035d9dc1d25eff9d361456572231c7d27b5ccd473ca7dc0adfce732bd006d40", size = 496552 }, + { url = "https://files.pythonhosted.org/packages/02/a2/a65293e6e4cf28eb7ee1be5335a5386c40d6742e9f47fafc8fec785e16c7/regex-2026.7.19-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c42572142ed0b9d5d261ba727157c426510da78e20828b66bbb855098b8a4e38", size = 296983 }, + { url = "https://files.pythonhosted.org/packages/95/47/2d0564e93d87bc48618360ddca232a2ca612bbdf53ce8465d45ca5ce14ee/regex-2026.7.19-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40b34dd88658e4fedd2fddbf0275ac970d00614b731357f425722a3ed1983d11", size = 291832 }, + { url = "https://files.pythonhosted.org/packages/07/cd/42dfbabff3dfc9603c501c0e2e2c5adbb09d127b267bf5348de0af338c15/regex-2026.7.19-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c41c63992bf1874cebb6e7f56fd7d3c007924659a604ae3d90e427d40d4fd13", size = 796775 }, + { url = "https://files.pythonhosted.org/packages/df/5d/f6a4839f2b934e3eed5973fd07f5929ee97d4c98939fb275ea23c274ee16/regex-2026.7.19-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d3372064506b94dd2c67c845f2db8062e9e9ba84d04e33cb96d7d33c11fe1ae", size = 865687 }, + { url = "https://files.pythonhosted.org/packages/14/b0/b47d6c36049bc59806a50bd4c86ced70bbe058d787f80281b1d7a9b0e024/regex-2026.7.19-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fce7760bf283405b2c7999cab3da4e72f7deca6396013115e3f7a955db9760da", size = 911962 }, + { url = "https://files.pythonhosted.org/packages/2a/be/ff61f28f9273658cfe23acbbac5217221f6519960ed401e61dfdab12bc35/regex-2026.7.19-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0d702548d89d572b2929879bc883bb7a4c4709efafe4512cadee56c55c9bd15", size = 801817 }, + { url = "https://files.pythonhosted.org/packages/c3/bb/8b4f7f26b333f9f79e1b453613c39bb4776f51d38ae66dd0ba31d6b354ca/regex-2026.7.19-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d446c6ac40bb6e05025ccee55b84d80fe9bf8e93010ffc4bb9484f13d498835f", size = 776908 }, + { url = "https://files.pythonhosted.org/packages/09/13/610110fc5921d380516d03c26b652555f08aa0d23ea78a771231873c3638/regex-2026.7.19-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4c3501bfa814ab07b5580741f9bf78dfdfe146a04057f82df9e2402d2a975939", size = 784426 }, + { url = "https://files.pythonhosted.org/packages/ca/f5/1ef9e2a83a5947c57ebff0b377cb5727c3d5ec1992317a320d035cd0dbb6/regex-2026.7.19-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c4585c3e64b4f9e583b4d2683f18f5d5d872b3d71dcf24594b74ecc23602fa96", size = 860600 }, + { url = "https://files.pythonhosted.org/packages/a0/02/073af33a3ec149241d11c80acea91e722aa0adbf05addd50f251c4fe89c3/regex-2026.7.19-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:571fde9741eb0ccde23dd4e0c1d50fbae910e901fa7e629faf39b2dda740d220", size = 765950 }, + { url = "https://files.pythonhosted.org/packages/81/a9/d1e9f819dc394a568ef370cd56cf25394e957a2235f8370f23b576e5a475/regex-2026.7.19-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:15b364b9b98d6d2fe1a85034c23a3180ff913f46caddc3895f6fd65186255ccc", size = 851794 }, + { url = "https://files.pythonhosted.org/packages/03/3a/8ae83eda7579feacdf984e71fb9e70635fb6f832eeddca58427ec4fca926/regex-2026.7.19-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffd8893ccc1c2fce6e0d6ca402d716fe1b29db70c7132609a05955e31b2aa8f2", size = 789845 }, + { url = "https://files.pythonhosted.org/packages/4b/23/c195cbfe5a75fdec64d8f6554fd15237b837919d2c61bdc141d7c807b08b/regex-2026.7.19-cp313-cp313-win32.whl", hash = "sha256:f0fa4fa9c3632d708742baf2282f2055c11d888a790362670a403cbf48a2c404", size = 267135 }, + { url = "https://files.pythonhosted.org/packages/b2/80/a11de8404b7272b70acb45c1c05987cce60b45d5693da2e176f0e390d564/regex-2026.7.19-cp313-cp313-win_amd64.whl", hash = "sha256:d51ffd3427640fa2da6ade574ceba932f210ad095f65fcc450a2b0a0d454868e", size = 277747 }, + { url = "https://files.pythonhosted.org/packages/d1/29/0f5c8eff1b4f1f3d83276d365fccecf666afcc7d947420943bf394d07adb/regex-2026.7.19-cp313-cp313-win_arm64.whl", hash = "sha256:c670fe7be5b6020b76bc6e8d2196074657e1327595bca93a389e1a76ab130ad8", size = 277129 }, + { url = "https://files.pythonhosted.org/packages/dc/4c/44b74742052cedda40f9ae469532a037112f7311a36669a891fba8984bb0/regex-2026.7.19-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db47b561c9afd884baa1f96f797c9ca369872c4b65912bc691cfa99e68340af2", size = 501134 }, + { url = "https://files.pythonhosted.org/packages/f0/45/bbd038b5e39ee5613a5a689290145b40058cc152c41de9cc23639d2b9734/regex-2026.7.19-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65dcd28d3eba2ab7c2fd906485cc301392b47cc2234790d27d4e4814e02cdfda", size = 299418 }, + { url = "https://files.pythonhosted.org/packages/65/38/c5bde94b4cedfd5850d64c3f08222d8e1600e84f6ee71d9b44b4b8163f74/regex-2026.7.19-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f2e7f8e2ab6c2922be02c7ec45185aa5bd771e2e57b95455ee343a44d8130dff", size = 294486 }, + { url = "https://files.pythonhosted.org/packages/d7/6a/2f5e107cb26c960b781967178899daf2787a7ab151844ed3c01d6fc95474/regex-2026.7.19-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe31f28c94402043161876a258a9c6f757cb485905c7614ce8d6cd40e6b7bdc1", size = 811643 }, + { url = "https://files.pythonhosted.org/packages/37/d4/a2f963406d7d73a62eed84ba05a258afb6cad1b21aa4517443ce40506b78/regex-2026.7.19-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8f6fa298bb4f7f58a33334406218ba74716e68feddf5e4e54cd5d8082705abf", size = 871081 }, + { url = "https://files.pythonhosted.org/packages/45/a3/44be546340bedb15f13063f5e7fe16793ea4d9ea2e805d09bd174ac27724/regex-2026.7.19-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cc1b2440423a851fad781309dd87843868f4f66a6bcd1ddb9225cf4ec2c84732", size = 917372 }, + { url = "https://files.pythonhosted.org/packages/f8/f6/e0870b0fd2a40dba0074e4b76e514b21313d37946c9248453e34ec43923e/regex-2026.7.19-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ac59a0900474a52b7c04af8196affc22bd9842acb0950df12f7b813e983609a", size = 816089 }, + { url = "https://files.pythonhosted.org/packages/ae/27/957e8e22690ad6634572b39b71f130a6105f4d0718bb16849eac00fff147/regex-2026.7.19-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4896db1f4ce0576765b8272aa922df324e0f5b9bb2c3d03044ff32a7234a9aba", size = 785206 }, + { url = "https://files.pythonhosted.org/packages/76/a4/186e410941e731037c01166069ab86da9f65e8f8110c18009ccf4bd623ee/regex-2026.7.19-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4e6883a021db30511d9fb8cfb0f222ce1f2c369f7d4d8b0448f449a93ba0bdfc", size = 800431 }, + { url = "https://files.pythonhosted.org/packages/73/9f/e4e10e023d291d64a33e246610b724493bf1ce98e0e59c9b7c837e5acfb7/regex-2026.7.19-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:09523a592938aa9f587fb74467c63ff0cf88fc3df14c82ab0f0517dcf76aaa62", size = 864906 }, + { url = "https://files.pythonhosted.org/packages/24/57/ccb20b6be5f1f52a053d1ba2a8f7a077edb9d918248b8490d7506c6832b3/regex-2026.7.19-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1ebac3474b8589fce2f9b225b650afd61448f7c73a5d0255a10cc6366471aed1", size = 773559 }, + { url = "https://files.pythonhosted.org/packages/a3/82/f3b263cf8fad927dc102891da8502e718b7ff9d19af7a2a07c03865d7188/regex-2026.7.19-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4a0530bb1b8c1c985e7e2122e2b4d3aedd8a3c21c6bfddae6767c4405668b56e", size = 857739 }, + { url = "https://files.pythonhosted.org/packages/47/2e/1687bd1b6c2aed5e672ccf845fc11557821fe7366d921b50889ea5ce57bf/regex-2026.7.19-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef7eeb108c47ce7bcc9513e51bcb1bf57e8f483d52fce68a8642e3527141ae0", size = 804522 }, + { url = "https://files.pythonhosted.org/packages/76/7c/cc4e7655181b2d9235b704f2c5e19d8eff002bbc437bae59baee0e381aca/regex-2026.7.19-cp313-cp313t-win32.whl", hash = "sha256:64b6ca7391a1395c2638dd5c7456d67bea44fc6c5e8e92c5dc8aa6a8f23292b4", size = 269141 }, + { url = "https://files.pythonhosted.org/packages/bb/14/961b4c7b05a2391c32dbc85e27773076671ef8f97f36cec70fe414734c02/regex-2026.7.19-cp313-cp313t-win_amd64.whl", hash = "sha256:f04b9f56b0e0614c0126be12c2c2d9f8850c1e57af302bd0a63bed379d4af974", size = 280036 }, + { url = "https://files.pythonhosted.org/packages/ce/67/795644550d788ddbb6dc458c95895f8009978ea6d6ea76b005eb3f45e8c9/regex-2026.7.19-cp313-cp313t-win_arm64.whl", hash = "sha256:fcee38cd8e5089d6d4f048ba1233b3ad76e5954f545382180889112ff5cb712d", size = 279394 }, ] [[package]] name = "regress" version = "2025.10.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/bf/faa406189856f9b566fa1c42d173188d3e86cd5116484a663922365e0004/regress-2025.10.1.tar.gz", hash = "sha256:dcc0a8af0cdbc3d6e0d4725f113335d0a5ffbba86ae3ca18d2b5b352c5f2c8ed", size = 11567, upload-time = "2025-10-09T07:09:48.397Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/bd/f7ff13577c688efec36488554489635c135ab7c2b3652c00aaa3d74bd761/regress-2025.10.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9100da34f69e18ad6d545f5d74f8ee729e42ce200a73752dd6d94f8a373d0e71", size = 445469, upload-time = "2025-10-09T07:08:16.825Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/3d/e8fc72e7424c168dfc4cdf3a864f189f6d3afbb6e3ed66455774d1f5e2ba/regress-2025.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e25096697b034848d8fc7910cdb38b7abc2bd2d7ade8767893359918ed4efd0", size = 434712, upload-time = "2025-10-09T07:08:17.817Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/d0/a18cc39008765d2b964bc2eba423416545d97b4f2623b99549c5e97e4d37/regress-2025.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4c40a8c9f3e0119d2384a52b55dcc770461e1ead6ae7b41314999223116a15", size = 514752, upload-time = "2025-10-09T07:08:19.148Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/11/57e64ba417867b39ad9ca55feaea80d3176735dba36e6a92ff21b8224fcb/regress-2025.10.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25d8518285aa3ce67ddbede90a1a9ca6a5d23a1b8275dd5a9722af0c64b37b2a", size = 497412, upload-time = "2025-10-09T07:08:20.448Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/6c/2fd3d3877c1957408548f7a380580f0b64ef8aff69f7fc2cc182a79b0b0b/regress-2025.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8051fac3696730bb84d7675c62c7073792a0e105233d4f8e1055f2cab9b04fce", size = 676998, upload-time = "2025-10-09T07:08:21.881Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/54/acde146473453f3acb5dd29e5be0fb627d87ad020ec5674ee133cde24261/regress-2025.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e337ce3b150ca0ff599b1150b995ff6a2e32b5940e17ac3f30a29133960709e", size = 576129, upload-time = "2025-10-09T07:08:23.354Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/b0/8007436b33496ec8f10d7b7a67a7a669569c82fc2a182d4f928ab4fd11da/regress-2025.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:290a15652c3fafe387db02061d4ed9a100804ed5a162d069b5a0ac28b5df162a", size = 507510, upload-time = "2025-10-09T07:08:24.34Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/9e/6ac6cd1de7e34e34cb492df627847de0d01cee982c7dd7d100e67cf1aa93/regress-2025.10.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b4bc2008b59e5124c1672d7fd9f203e5d4a4ff88ebaf4666e3281141e2d8db20", size = 520595, upload-time = "2025-10-09T07:08:25.35Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/26/35556d1dd6f64122a16166db7651d1a5804edebea9835cbcd452d9b7c2ff/regress-2025.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:56ef2f8ef9a102a7d42cbbc2f3c0c5e1b186bd8eaa78d564da566b0bf20653dc", size = 695584, upload-time = "2025-10-09T07:08:26.52Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/33/07650dfc042b55270fc269276112c592d458cdfb8d31c85447ff743dcf10/regress-2025.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0680ac0b0a0058acb55b64aa56956732cf56b0baa84ea95e3fa124ab16da58aa", size = 695277, upload-time = "2025-10-09T07:08:27.705Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/6b/80f446103df39fc1ce6e271e7c274687bb830402de23e68795ba2ab13214/regress-2025.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47c03bfd853651241fc11436fb14ef7c9b312bb9a9c2828aa5c93943e945f1ef", size = 690951, upload-time = "2025-10-09T07:08:29.295Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/10/43cd990934ddb2be3318380fa380f7ca19e0eda0c30793b73e8ab8839367/regress-2025.10.1-cp313-cp313-win32.whl", hash = "sha256:3d12e6a834ed5f6d9dc7e86ea8fd77d37bb13900129930151d87c3261c3a799e", size = 282592, upload-time = "2025-10-09T07:08:30.695Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/22/4855af0447c98793d3a406ddf42680736e700b8c78336d6199dff9a5fd1b/regress-2025.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:ee3b7325ea849097d674020c72b2e6deb8f1018f085a7ecbd22831cd217b7f80", size = 301551, upload-time = "2025-10-09T07:08:31.715Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/99/cd16dce5d1cb98970a8d7c577529e5890d938b54ade223d532fad0343221/regress-2025.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:e5f35e04fe6382c236d60c98b3f0a4a22dea75398b99c9c9bf3fb9d386cd7ebb", size = 288909, upload-time = "2025-10-09T07:08:32.605Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/32/35dbbdff29174dcc253bb00b2a0d2aaf3f47a2b1d174ab4ab828feca63f1/regress-2025.10.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:0ee12c4e1c4e6e3609f41dd58065bc241945e912772f7320238d6544f0745950", size = 444440, upload-time = "2025-10-09T07:08:33.635Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/1b/d97cf19b3de5ff8d78d03e0c470af48f7e8b6814b197014fda3f8a3aaca5/regress-2025.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:389c2278848cbfed81753a04ce8ea6c037271179cb9ef4decac7d3c65ae3330b", size = 434626, upload-time = "2025-10-09T07:08:34.75Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/f1/c0900448a4fd6248cd05c04c83aebb31f22ba3886965a5667f6ac4edc2b0/regress-2025.10.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf743b00cac20f8e8d271f275df7bc192ebb4faa7aee8fe98484df338786dec6", size = 514470, upload-time = "2025-10-09T07:08:35.75Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/a1/6346a314230af2859d2f0af5c4534ee497b9b13983ee06489652d7e1895b/regress-2025.10.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5541da1f581377bc20d2f77e017453aa8f2c2f4bfe7679dee00e139ec700abe8", size = 497546, upload-time = "2025-10-09T07:08:36.739Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/ff/09605137dff09594c9e12ce555a80d8405a6ad76faa1db702bfba57468d0/regress-2025.10.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a67216efa72bb27db170f637d67eb9acdc167da5d617163b057803de7aed1e6d", size = 676318, upload-time = "2025-10-09T07:08:37.72Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/37/4a65635c18844d687f9ad5bdd79b72fac8d15b131264616d6b1bb52614c2/regress-2025.10.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb61e653a325aea4681cf7b96ba9bbabc1aeb3f3d8fe877a07800024907398b9", size = 576190, upload-time = "2025-10-09T07:08:38.782Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/87/90a0a77a0416d7e3255e606f848021949b2f53ca18c9e4e240edb42e0929/regress-2025.10.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2a0a7da1d42fdff8068ab976a66beea572621514a130b37592489ae134a0e27", size = 507768, upload-time = "2025-10-09T07:08:39.797Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/62/82c8147027f0012dd7e743267d60dcf4218b2e061825b89cddddaa7fbe49/regress-2025.10.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:337ef62f785dc6e05c4a09be7a902980cf4d4f15346f4eca9eaf02745485440c", size = 520555, upload-time = "2025-10-09T07:08:40.801Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/ce/5dd5dda048173b644a475ae9f4643733b31c3543e861569af4257c60d32a/regress-2025.10.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:aaf5d102e05b109dde13363e705969b3ffdbbfeb880270187eed0871e75f0c8f", size = 695587, upload-time = "2025-10-09T07:08:42.136Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/08/7f6615d8ce3088feb82843ff4d48191600602d624021dbef797d274a043d/regress-2025.10.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:adf331c8938e0d8705fc5d05d08fab09c11cb4bcdf8a64fa21902972c4cd38f4", size = 694933, upload-time = "2025-10-09T07:08:43.171Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/87/94524b996745f892b30dec6e770701956f7125459c6a6b2c075af4b8f0a6/regress-2025.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:287f86b5c0bf3bc9c0abd45bf6745ba9c6a5624c3132b07631bac4403b45143f", size = 691029, upload-time = "2025-10-09T07:08:44.369Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/e1/612c3b8afa448747e9133a95b1e18d0ee8f3f5e930f7048d1af03976f883/regress-2025.10.1-cp313-cp313t-win32.whl", hash = "sha256:877e05e7c570ee1e077e8b587cca8a318b7675f3c94c6c4e25d0d145abf7c0b6", size = 282136, upload-time = "2025-10-09T07:08:45.477Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/1d/9d5a476a6b16a41e724e864afb834947323bfa4b309978dbdc2bf8dbdb02/regress-2025.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:97307f87b128389d8b3f385c8e431fc318263281d1a1c0394606bc813aea05fc", size = 301674, upload-time = "2025-10-09T07:08:46.513Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/bf/faa406189856f9b566fa1c42d173188d3e86cd5116484a663922365e0004/regress-2025.10.1.tar.gz", hash = "sha256:dcc0a8af0cdbc3d6e0d4725f113335d0a5ffbba86ae3ca18d2b5b352c5f2c8ed", size = 11567 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/bd/f7ff13577c688efec36488554489635c135ab7c2b3652c00aaa3d74bd761/regress-2025.10.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9100da34f69e18ad6d545f5d74f8ee729e42ce200a73752dd6d94f8a373d0e71", size = 445469 }, + { url = "https://files.pythonhosted.org/packages/89/3d/e8fc72e7424c168dfc4cdf3a864f189f6d3afbb6e3ed66455774d1f5e2ba/regress-2025.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e25096697b034848d8fc7910cdb38b7abc2bd2d7ade8767893359918ed4efd0", size = 434712 }, + { url = "https://files.pythonhosted.org/packages/63/d0/a18cc39008765d2b964bc2eba423416545d97b4f2623b99549c5e97e4d37/regress-2025.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4c40a8c9f3e0119d2384a52b55dcc770461e1ead6ae7b41314999223116a15", size = 514752 }, + { url = "https://files.pythonhosted.org/packages/0b/11/57e64ba417867b39ad9ca55feaea80d3176735dba36e6a92ff21b8224fcb/regress-2025.10.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25d8518285aa3ce67ddbede90a1a9ca6a5d23a1b8275dd5a9722af0c64b37b2a", size = 497412 }, + { url = "https://files.pythonhosted.org/packages/72/6c/2fd3d3877c1957408548f7a380580f0b64ef8aff69f7fc2cc182a79b0b0b/regress-2025.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8051fac3696730bb84d7675c62c7073792a0e105233d4f8e1055f2cab9b04fce", size = 676998 }, + { url = "https://files.pythonhosted.org/packages/cd/54/acde146473453f3acb5dd29e5be0fb627d87ad020ec5674ee133cde24261/regress-2025.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e337ce3b150ca0ff599b1150b995ff6a2e32b5940e17ac3f30a29133960709e", size = 576129 }, + { url = "https://files.pythonhosted.org/packages/f2/b0/8007436b33496ec8f10d7b7a67a7a669569c82fc2a182d4f928ab4fd11da/regress-2025.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:290a15652c3fafe387db02061d4ed9a100804ed5a162d069b5a0ac28b5df162a", size = 507510 }, + { url = "https://files.pythonhosted.org/packages/4a/9e/6ac6cd1de7e34e34cb492df627847de0d01cee982c7dd7d100e67cf1aa93/regress-2025.10.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b4bc2008b59e5124c1672d7fd9f203e5d4a4ff88ebaf4666e3281141e2d8db20", size = 520595 }, + { url = "https://files.pythonhosted.org/packages/11/26/35556d1dd6f64122a16166db7651d1a5804edebea9835cbcd452d9b7c2ff/regress-2025.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:56ef2f8ef9a102a7d42cbbc2f3c0c5e1b186bd8eaa78d564da566b0bf20653dc", size = 695584 }, + { url = "https://files.pythonhosted.org/packages/e9/33/07650dfc042b55270fc269276112c592d458cdfb8d31c85447ff743dcf10/regress-2025.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0680ac0b0a0058acb55b64aa56956732cf56b0baa84ea95e3fa124ab16da58aa", size = 695277 }, + { url = "https://files.pythonhosted.org/packages/6a/6b/80f446103df39fc1ce6e271e7c274687bb830402de23e68795ba2ab13214/regress-2025.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47c03bfd853651241fc11436fb14ef7c9b312bb9a9c2828aa5c93943e945f1ef", size = 690951 }, + { url = "https://files.pythonhosted.org/packages/b5/10/43cd990934ddb2be3318380fa380f7ca19e0eda0c30793b73e8ab8839367/regress-2025.10.1-cp313-cp313-win32.whl", hash = "sha256:3d12e6a834ed5f6d9dc7e86ea8fd77d37bb13900129930151d87c3261c3a799e", size = 282592 }, + { url = "https://files.pythonhosted.org/packages/ab/22/4855af0447c98793d3a406ddf42680736e700b8c78336d6199dff9a5fd1b/regress-2025.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:ee3b7325ea849097d674020c72b2e6deb8f1018f085a7ecbd22831cd217b7f80", size = 301551 }, + { url = "https://files.pythonhosted.org/packages/82/99/cd16dce5d1cb98970a8d7c577529e5890d938b54ade223d532fad0343221/regress-2025.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:e5f35e04fe6382c236d60c98b3f0a4a22dea75398b99c9c9bf3fb9d386cd7ebb", size = 288909 }, + { url = "https://files.pythonhosted.org/packages/a3/32/35dbbdff29174dcc253bb00b2a0d2aaf3f47a2b1d174ab4ab828feca63f1/regress-2025.10.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:0ee12c4e1c4e6e3609f41dd58065bc241945e912772f7320238d6544f0745950", size = 444440 }, + { url = "https://files.pythonhosted.org/packages/6f/1b/d97cf19b3de5ff8d78d03e0c470af48f7e8b6814b197014fda3f8a3aaca5/regress-2025.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:389c2278848cbfed81753a04ce8ea6c037271179cb9ef4decac7d3c65ae3330b", size = 434626 }, + { url = "https://files.pythonhosted.org/packages/04/f1/c0900448a4fd6248cd05c04c83aebb31f22ba3886965a5667f6ac4edc2b0/regress-2025.10.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf743b00cac20f8e8d271f275df7bc192ebb4faa7aee8fe98484df338786dec6", size = 514470 }, + { url = "https://files.pythonhosted.org/packages/11/a1/6346a314230af2859d2f0af5c4534ee497b9b13983ee06489652d7e1895b/regress-2025.10.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5541da1f581377bc20d2f77e017453aa8f2c2f4bfe7679dee00e139ec700abe8", size = 497546 }, + { url = "https://files.pythonhosted.org/packages/3d/ff/09605137dff09594c9e12ce555a80d8405a6ad76faa1db702bfba57468d0/regress-2025.10.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a67216efa72bb27db170f637d67eb9acdc167da5d617163b057803de7aed1e6d", size = 676318 }, + { url = "https://files.pythonhosted.org/packages/26/37/4a65635c18844d687f9ad5bdd79b72fac8d15b131264616d6b1bb52614c2/regress-2025.10.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb61e653a325aea4681cf7b96ba9bbabc1aeb3f3d8fe877a07800024907398b9", size = 576190 }, + { url = "https://files.pythonhosted.org/packages/93/87/90a0a77a0416d7e3255e606f848021949b2f53ca18c9e4e240edb42e0929/regress-2025.10.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2a0a7da1d42fdff8068ab976a66beea572621514a130b37592489ae134a0e27", size = 507768 }, + { url = "https://files.pythonhosted.org/packages/7d/62/82c8147027f0012dd7e743267d60dcf4218b2e061825b89cddddaa7fbe49/regress-2025.10.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:337ef62f785dc6e05c4a09be7a902980cf4d4f15346f4eca9eaf02745485440c", size = 520555 }, + { url = "https://files.pythonhosted.org/packages/09/ce/5dd5dda048173b644a475ae9f4643733b31c3543e861569af4257c60d32a/regress-2025.10.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:aaf5d102e05b109dde13363e705969b3ffdbbfeb880270187eed0871e75f0c8f", size = 695587 }, + { url = "https://files.pythonhosted.org/packages/9e/08/7f6615d8ce3088feb82843ff4d48191600602d624021dbef797d274a043d/regress-2025.10.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:adf331c8938e0d8705fc5d05d08fab09c11cb4bcdf8a64fa21902972c4cd38f4", size = 694933 }, + { url = "https://files.pythonhosted.org/packages/63/87/94524b996745f892b30dec6e770701956f7125459c6a6b2c075af4b8f0a6/regress-2025.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:287f86b5c0bf3bc9c0abd45bf6745ba9c6a5624c3132b07631bac4403b45143f", size = 691029 }, + { url = "https://files.pythonhosted.org/packages/64/e1/612c3b8afa448747e9133a95b1e18d0ee8f3f5e930f7048d1af03976f883/regress-2025.10.1-cp313-cp313t-win32.whl", hash = "sha256:877e05e7c570ee1e077e8b587cca8a318b7675f3c94c6c4e25d0d145abf7c0b6", size = 282136 }, + { url = "https://files.pythonhosted.org/packages/51/1d/9d5a476a6b16a41e724e864afb834947323bfa4b309978dbdc2bf8dbdb02/regress-2025.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:97307f87b128389d8b3f385c8e431fc318263281d1a1c0394606bc813aea05fc", size = 301674 }, ] [[package]] name = "requests" version = "2.34.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075 }, ] [[package]] name = "requests-oauthlib" version = "2.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "oauthlib" }, { name = "requests" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 }, ] [[package]] name = "rfc3339-validator" version = "0.1.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, ] [[package]] name = "rich" version = "15.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654 }, ] [[package]] name = "rich-rst" version = "2.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pygments" }, { name = "rich" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/d6/d0b9fafc73b65767200da027acab1db1bdb1048f4fea5ebf659df01c700e/rich_rst-2.1.0.tar.gz", hash = "sha256:f4d117b49697f338769759fa5cacf5197da4888b347b9fda2e50aef5cd8d93bd", size = 302732, upload-time = "2026-07-05T02:59:44.308Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/d6/d0b9fafc73b65767200da027acab1db1bdb1048f4fea5ebf659df01c700e/rich_rst-2.1.0.tar.gz", hash = "sha256:f4d117b49697f338769759fa5cacf5197da4888b347b9fda2e50aef5cd8d93bd", size = 302732 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/68/1fc93dd759605b5d00fc98b50200739e41ed32bd22d6ba35ca6c3932371b/rich_rst-2.1.0-py3-none-any.whl", hash = "sha256:7ecd1343ee12c879d0e7ae74c3eb6d263b023d2929c6d114212eb1fd91057255", size = 272987, upload-time = "2026-07-05T02:59:42.792Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/1fc93dd759605b5d00fc98b50200739e41ed32bd22d6ba35ca6c3932371b/rich_rst-2.1.0-py3-none-any.whl", hash = "sha256:7ecd1343ee12c879d0e7ae74c3eb6d263b023d2929c6d114212eb1fd91057255", size = 272987 }, ] [[package]] name = "ripgrep-rs" version = "0.4.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/8c/de0b9456c057f56454a0269e536949405001ce97b96d18797c99ea724767/ripgrep_rs-0.4.2.tar.gz", hash = "sha256:a109ad6a59f83a3cd71b274a16d722637414feb6a43ab43764fafe65206e5795", size = 61127, upload-time = "2026-02-07T09:50:39.88Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/8c/de0b9456c057f56454a0269e536949405001ce97b96d18797c99ea724767/ripgrep_rs-0.4.2.tar.gz", hash = "sha256:a109ad6a59f83a3cd71b274a16d722637414feb6a43ab43764fafe65206e5795", size = 61127 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/32/4ac331c46b0cf3965c507b43e95802a709b41e8b5b07f033769d6d03c90b/ripgrep_rs-0.4.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:53127ef9b69a97f34fd2ba61810fa20e7ff98541c4adaa7b4d46cbbbb8163582", size = 1774241, upload-time = "2026-02-07T09:50:10.046Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/d4/dadfbcdf81393834889002e4ff2bcbb6218c90c154cf96693153ebe928ea/ripgrep_rs-0.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b50783ef15ec317f86049eea0fe33f8b277b89d0e3eff394300e3db9abc63d1f", size = 1728477, upload-time = "2026-02-07T09:50:11.856Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/1a/b8961b8bc0878883e0ca404345a81ae5c694ceeeda917f529bd76f77cb83/ripgrep_rs-0.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cba85d71eeaaef013687073035ee1347c10a017948091d2853cf47d13f310f2e", size = 1925332, upload-time = "2026-02-07T09:50:13.765Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/31/130c04ad66e09cdec9dc34af35910be75572cc8b257aeeae4fd9d3911061/ripgrep_rs-0.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8834718d3f0228d9d5bfd817bb2154f74891abc257553c6014117e9cfc374d1", size = 1932723, upload-time = "2026-02-07T09:50:15.159Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/63/1db6dbd943bf0110baa794e4c478d832afb740fecdd27f7800123fde081b/ripgrep_rs-0.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:fcde19c665343365625a790c81e33b9190cebe445af2135329eb243cda5aee33", size = 1583932, upload-time = "2026-02-07T09:50:16.513Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/c6/3e0c9cd1ffb98bf25c087db3e5d588f9a58b13b11b576dba5d6e4d2e2905/ripgrep_rs-0.4.2-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a78cee10e18ac93ecef63bd88267f0e193e73b31cb64ee3ec155885253221e26", size = 1774040, upload-time = "2026-02-07T09:50:17.904Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/5d/5f2fb2c148c37a848d16b3605366ff41662663d3d1c936c6a81ef952f9b6/ripgrep_rs-0.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:161592c6749d5ef04f8240e400ab2b4fe8087d0ee4f3199c30f17ee0ba73b284", size = 1727369, upload-time = "2026-02-07T09:50:19.705Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/4e/bf49e3ee7b0359abd1b99e0eac0b701d44cb0645df9eaa6e4ce9ee2761c9/ripgrep_rs-0.4.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5cf0c934f00d3acfb646509ae8ea9f9a12be6f85ba4deaae180e29ba7f9fe8c", size = 1926431, upload-time = "2026-02-07T09:50:21.485Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/ba/f4f5e2a1fd49da57cb07b752ab61e9ec8afcdff5af423a8c698fa64a523d/ripgrep_rs-0.4.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:683138a58ca29641519f7932c8ca3bff6bf9a78b8744145f68299b3c818dab33", size = 1932875, upload-time = "2026-02-07T09:50:23.304Z" }, + { url = "https://files.pythonhosted.org/packages/10/32/4ac331c46b0cf3965c507b43e95802a709b41e8b5b07f033769d6d03c90b/ripgrep_rs-0.4.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:53127ef9b69a97f34fd2ba61810fa20e7ff98541c4adaa7b4d46cbbbb8163582", size = 1774241 }, + { url = "https://files.pythonhosted.org/packages/91/d4/dadfbcdf81393834889002e4ff2bcbb6218c90c154cf96693153ebe928ea/ripgrep_rs-0.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b50783ef15ec317f86049eea0fe33f8b277b89d0e3eff394300e3db9abc63d1f", size = 1728477 }, + { url = "https://files.pythonhosted.org/packages/05/1a/b8961b8bc0878883e0ca404345a81ae5c694ceeeda917f529bd76f77cb83/ripgrep_rs-0.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cba85d71eeaaef013687073035ee1347c10a017948091d2853cf47d13f310f2e", size = 1925332 }, + { url = "https://files.pythonhosted.org/packages/44/31/130c04ad66e09cdec9dc34af35910be75572cc8b257aeeae4fd9d3911061/ripgrep_rs-0.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8834718d3f0228d9d5bfd817bb2154f74891abc257553c6014117e9cfc374d1", size = 1932723 }, + { url = "https://files.pythonhosted.org/packages/4d/63/1db6dbd943bf0110baa794e4c478d832afb740fecdd27f7800123fde081b/ripgrep_rs-0.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:fcde19c665343365625a790c81e33b9190cebe445af2135329eb243cda5aee33", size = 1583932 }, + { url = "https://files.pythonhosted.org/packages/d7/c6/3e0c9cd1ffb98bf25c087db3e5d588f9a58b13b11b576dba5d6e4d2e2905/ripgrep_rs-0.4.2-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a78cee10e18ac93ecef63bd88267f0e193e73b31cb64ee3ec155885253221e26", size = 1774040 }, + { url = "https://files.pythonhosted.org/packages/f5/5d/5f2fb2c148c37a848d16b3605366ff41662663d3d1c936c6a81ef952f9b6/ripgrep_rs-0.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:161592c6749d5ef04f8240e400ab2b4fe8087d0ee4f3199c30f17ee0ba73b284", size = 1727369 }, + { url = "https://files.pythonhosted.org/packages/73/4e/bf49e3ee7b0359abd1b99e0eac0b701d44cb0645df9eaa6e4ce9ee2761c9/ripgrep_rs-0.4.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5cf0c934f00d3acfb646509ae8ea9f9a12be6f85ba4deaae180e29ba7f9fe8c", size = 1926431 }, + { url = "https://files.pythonhosted.org/packages/ab/ba/f4f5e2a1fd49da57cb07b752ab61e9ec8afcdff5af423a8c698fa64a523d/ripgrep_rs-0.4.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:683138a58ca29641519f7932c8ca3bff6bf9a78b8744145f68299b3c818dab33", size = 1932875 }, ] [[package]] name = "rpds-py" version = "2026.6.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012 }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203 }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984 }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815 }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545 }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828 }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678 }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811 }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382 }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832 }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011 }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431 }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710 }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454 }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063 }, ] [[package]] name = "ruamel-yaml" version = "0.19.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102 }, ] [[package]] name = "ruff" version = "0.16.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/94/1e5e4967626faf12fa56999cd6222dff6992ceb086ad7945756baf70c7a7/ruff-0.16.0.tar.gz", hash = "sha256:e460aafd5495ec89efaa6ced2e4a9a581116451e1c88b9d37ef497e0f8e93982", size = 4790557, upload-time = "2026-07-23T19:11:30.981Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/81/1c8818fee7ce1a04cd7d1b3172e0a8f8e4f1dc4feb7fc390e16daa8af323/ruff-0.16.0-py3-none-linux_armv6l.whl", hash = "sha256:e5115729eb08c585e5121978ba5d5b60caeae394ce21b9fb5e6cd33a1c6c9b1e", size = 10754633, upload-time = "2026-07-23T19:10:46.415Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/df/beaf59c09d68db84304d555f188b276a77132a5d5b0b67a5c762aa143628/ruff-0.16.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c954b1d580bfa035b41654f7858cc7e71d5fc3ac5b723dd62bd9133830ed522", size = 10969164, upload-time = "2026-07-23T19:10:50.271Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/ce/741cd197496a1abbf51352710fd15ed995d2a2be87189c1da26a450d6e83/ruff-0.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e01c21d10eb1b29f47b7454e1f4056db9a3f0260c646aa88457c610291db9f81", size = 10488846, upload-time = "2026-07-23T19:10:52.639Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/2a/a2db8e88cade358f5cdcb05674a917751074109315d014eb6352d9a893f7/ruff-0.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e364e5ed22ed8dc05082fd78e35308618260907ac2d3c1d637b2e682415b6c9", size = 10889729, upload-time = "2026-07-23T19:10:54.89Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/65/62a771694ebd63029dc953e27dbad40e1588bd4860ff9fe881018fddaa49/ruff-0.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d327b8fc113a1d4421a04f3839d3752057c8dd1ee320223a6f3f52d04ada462a", size = 10568275, upload-time = "2026-07-23T19:10:56.993Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/e2/ced249fe8af5f086c5c58cc21cc3356d50f32f7401c5df87050c999620a7/ruff-0.16.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b50c55e263103586b3dcf5f73d479eb8cb5fdb6098fec59a62891dab653717", size = 11385112, upload-time = "2026-07-23T19:10:59.615Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/0b/05154977a8fd69eeb6c103271f55403bfd8711f5c0f8ed07489d95a504e7/ruff-0.16.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff4a79ce3ec0172f3241943835de1c4cb4e2dcd07f0f8c2d02603dbbbee4b17", size = 12207008, upload-time = "2026-07-23T19:11:02.154Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/29/98225831a3a1eab0e02f4acc6ca6559a98611dcc68b6965ff4b7234627c1/ruff-0.16.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e95c448fca1fb2a18372a9440926c5a6ee789639bb975c72e7ae6d0b04218ab4", size = 11650842, upload-time = "2026-07-23T19:11:04.557Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/66/6bd3cf90500653d55dc0ffc8507aa8300bd49d0214b2e8cb4d3fef2943ba/ruff-0.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f11a8d11010301d0a398a2fdef67691feca7294da6aef55e2150e8fa2cd520b", size = 11400718, upload-time = "2026-07-23T19:11:09.233Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/a2/a54eb4eae05d66364050a5d3b8a9c5ef88196531b3cbe7109d873f87f819/ruff-0.16.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48044c678e9cb8698246c99b14aaccfa6601dea7379eb48a6f8f73f7a6d86cd0", size = 11426177, upload-time = "2026-07-23T19:11:11.994Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/be/16e3eea4b2a478a496919f5e36f17c4559e54620bd3bbac5d6affa068006/ruff-0.16.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7aa0959bad8eb8bef50340154fc9b58678dae31fa4293afa38b44b6e552c0213", size = 10856126, upload-time = "2026-07-23T19:11:14.221Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/84/252eb8b868a16eec7257c14f504f77537e734b2d69c762e639e588e304a3/ruff-0.16.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28ea2b7df8ebf7f9da6b7d47b230ab48f387c0a29be3b474c4d0740e197bb9af", size = 10571208, upload-time = "2026-07-23T19:11:16.378Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/09/817a482f542f7570cbb4554b26e896610c7114f539b1d9e2d2145bf6bef6/ruff-0.16.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:33a3dfac8c35f81498dea9181bccc2f4c4bc8f1521a1dd9406e77643e0f0fb09", size = 11063329, upload-time = "2026-07-23T19:11:19.173Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/23/9403c180ca1cb9b1f7335f5c3e5305c09d49ea5b345196682a36028bde4a/ruff-0.16.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5237a0bda500d30d81b8e07a6973a5cbc772864cbf746ae2f4e8a2e01c9f4ed", size = 11489751, upload-time = "2026-07-23T19:11:21.74Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/1d/1b2ef7bcde851c78d7f17f1cca13fd6dc695fc4b3d6197941e72cae5b132/ruff-0.16.0-py3-none-win32.whl", hash = "sha256:7fab76fa065c873f41ff744347c6e77bcc3dfec4bcc754dc26b63d23c0f7f5fb", size = 10785885, upload-time = "2026-07-23T19:11:23.947Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/a3/d5e4ef7a56be3f928ffb90b94c25ba7d3cb9c7fe0736aeaaedf361770712/ruff-0.16.0-py3-none-win_amd64.whl", hash = "sha256:429c117f022bf481fabd9d551e7a3952b24c65e6ef44337ea09d90bebef14472", size = 11923141, upload-time = "2026-07-23T19:11:26.409Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/9a/8415f2657cbe200f41a4531ccededf135505a92d4a012229121f885b26f9/ruff-0.16.0-py3-none-win_arm64.whl", hash = "sha256:14296fedcd2705c77ab8235439278bbb38f285cf7da5528b00b3e330c3d4872d", size = 11273407, upload-time = "2026-07-23T19:11:28.705Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/94/1e5e4967626faf12fa56999cd6222dff6992ceb086ad7945756baf70c7a7/ruff-0.16.0.tar.gz", hash = "sha256:e460aafd5495ec89efaa6ced2e4a9a581116451e1c88b9d37ef497e0f8e93982", size = 4790557 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/81/1c8818fee7ce1a04cd7d1b3172e0a8f8e4f1dc4feb7fc390e16daa8af323/ruff-0.16.0-py3-none-linux_armv6l.whl", hash = "sha256:e5115729eb08c585e5121978ba5d5b60caeae394ce21b9fb5e6cd33a1c6c9b1e", size = 10754633 }, + { url = "https://files.pythonhosted.org/packages/23/df/beaf59c09d68db84304d555f188b276a77132a5d5b0b67a5c762aa143628/ruff-0.16.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c954b1d580bfa035b41654f7858cc7e71d5fc3ac5b723dd62bd9133830ed522", size = 10969164 }, + { url = "https://files.pythonhosted.org/packages/42/ce/741cd197496a1abbf51352710fd15ed995d2a2be87189c1da26a450d6e83/ruff-0.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e01c21d10eb1b29f47b7454e1f4056db9a3f0260c646aa88457c610291db9f81", size = 10488846 }, + { url = "https://files.pythonhosted.org/packages/52/2a/a2db8e88cade358f5cdcb05674a917751074109315d014eb6352d9a893f7/ruff-0.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e364e5ed22ed8dc05082fd78e35308618260907ac2d3c1d637b2e682415b6c9", size = 10889729 }, + { url = "https://files.pythonhosted.org/packages/42/65/62a771694ebd63029dc953e27dbad40e1588bd4860ff9fe881018fddaa49/ruff-0.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d327b8fc113a1d4421a04f3839d3752057c8dd1ee320223a6f3f52d04ada462a", size = 10568275 }, + { url = "https://files.pythonhosted.org/packages/3f/e2/ced249fe8af5f086c5c58cc21cc3356d50f32f7401c5df87050c999620a7/ruff-0.16.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b50c55e263103586b3dcf5f73d479eb8cb5fdb6098fec59a62891dab653717", size = 11385112 }, + { url = "https://files.pythonhosted.org/packages/87/0b/05154977a8fd69eeb6c103271f55403bfd8711f5c0f8ed07489d95a504e7/ruff-0.16.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff4a79ce3ec0172f3241943835de1c4cb4e2dcd07f0f8c2d02603dbbbee4b17", size = 12207008 }, + { url = "https://files.pythonhosted.org/packages/fb/29/98225831a3a1eab0e02f4acc6ca6559a98611dcc68b6965ff4b7234627c1/ruff-0.16.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e95c448fca1fb2a18372a9440926c5a6ee789639bb975c72e7ae6d0b04218ab4", size = 11650842 }, + { url = "https://files.pythonhosted.org/packages/91/66/6bd3cf90500653d55dc0ffc8507aa8300bd49d0214b2e8cb4d3fef2943ba/ruff-0.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f11a8d11010301d0a398a2fdef67691feca7294da6aef55e2150e8fa2cd520b", size = 11400718 }, + { url = "https://files.pythonhosted.org/packages/8e/a2/a54eb4eae05d66364050a5d3b8a9c5ef88196531b3cbe7109d873f87f819/ruff-0.16.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48044c678e9cb8698246c99b14aaccfa6601dea7379eb48a6f8f73f7a6d86cd0", size = 11426177 }, + { url = "https://files.pythonhosted.org/packages/1a/be/16e3eea4b2a478a496919f5e36f17c4559e54620bd3bbac5d6affa068006/ruff-0.16.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7aa0959bad8eb8bef50340154fc9b58678dae31fa4293afa38b44b6e552c0213", size = 10856126 }, + { url = "https://files.pythonhosted.org/packages/a2/84/252eb8b868a16eec7257c14f504f77537e734b2d69c762e639e588e304a3/ruff-0.16.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28ea2b7df8ebf7f9da6b7d47b230ab48f387c0a29be3b474c4d0740e197bb9af", size = 10571208 }, + { url = "https://files.pythonhosted.org/packages/21/09/817a482f542f7570cbb4554b26e896610c7114f539b1d9e2d2145bf6bef6/ruff-0.16.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:33a3dfac8c35f81498dea9181bccc2f4c4bc8f1521a1dd9406e77643e0f0fb09", size = 11063329 }, + { url = "https://files.pythonhosted.org/packages/2e/23/9403c180ca1cb9b1f7335f5c3e5305c09d49ea5b345196682a36028bde4a/ruff-0.16.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5237a0bda500d30d81b8e07a6973a5cbc772864cbf746ae2f4e8a2e01c9f4ed", size = 11489751 }, + { url = "https://files.pythonhosted.org/packages/b2/1d/1b2ef7bcde851c78d7f17f1cca13fd6dc695fc4b3d6197941e72cae5b132/ruff-0.16.0-py3-none-win32.whl", hash = "sha256:7fab76fa065c873f41ff744347c6e77bcc3dfec4bcc754dc26b63d23c0f7f5fb", size = 10785885 }, + { url = "https://files.pythonhosted.org/packages/b2/a3/d5e4ef7a56be3f928ffb90b94c25ba7d3cb9c7fe0736aeaaedf361770712/ruff-0.16.0-py3-none-win_amd64.whl", hash = "sha256:429c117f022bf481fabd9d551e7a3952b24c65e6ef44337ea09d90bebef14472", size = 11923141 }, + { url = "https://files.pythonhosted.org/packages/cb/9a/8415f2657cbe200f41a4531ccededf135505a92d4a012229121f885b26f9/ruff-0.16.0-py3-none-win_arm64.whl", hash = "sha256:14296fedcd2705c77ab8235439278bbb38f285cf7da5528b00b3e330c3d4872d", size = 11273407 }, ] [[package]] name = "rustworkx" version = "0.18.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/1a/545aa3a3e251e9da00e4acf0e5472b7fbbb15360f56d9d5342c1f93b8b93/rustworkx-0.18.0.tar.gz", hash = "sha256:5ca9cf8dbee50f8def012119ebb64771ae86916993417415aba9e845831cb436", size = 894567, upload-time = "2026-06-18T04:38:56.141Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/1a/545aa3a3e251e9da00e4acf0e5472b7fbbb15360f56d9d5342c1f93b8b93/rustworkx-0.18.0.tar.gz", hash = "sha256:5ca9cf8dbee50f8def012119ebb64771ae86916993417415aba9e845831cb436", size = 894567 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/70/18b310752b0652d4a755b46853268c00c33401101a47f87697ad25966453/rustworkx-0.18.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7440ba70b87bd16811d92e57d76108840dbdd89aa2fc55e4d324fa2fb6b7f9c9", size = 2295936, upload-time = "2026-06-18T04:38:01.598Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/4e/09152f4422204f020346a8a2b0e2f05f12d8cc60eeeee99b24f649985514/rustworkx-0.18.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:7fce5218ebfb8d8f5313def1f15aad8d3c3c2bbe03e33805dff8cf8bb70370a1", size = 2145057, upload-time = "2026-06-18T04:38:03.253Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/68/69198f41f7f9c39fb5b2e561bf41d4cbd117fa5dfdf4631d8ba28e5ff69b/rustworkx-0.18.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7e0c626f76bc71d414a02502be7ec0ac2dd6eca369886bc66a2650607f2d9de6", size = 2391659, upload-time = "2026-06-18T04:38:04.942Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/81/8ac568efe0289b0ecd826cd697c3581b6a25e134145926cbfef762656167/rustworkx-0.18.0-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:91b37c5bb54e233e16c4f47383385f17be03e6a344821c2f6a39a0aebee54538", size = 2189469, upload-time = "2026-06-18T04:38:06.51Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/a4/96492d7af2ddcd15368f80329e3514060b122f306c4406fac98c08a97865/rustworkx-0.18.0-cp310-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:bcd15f7a637ca0654f329ed42a6db0cf7eac15ed89e0b32cb9d16b1b21937979", size = 2504116, upload-time = "2026-06-18T04:38:43.441Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/b0/005383bd7dc110b06ca731bd2115482f3a8c6389e0c8fe1f7f259380c4c8/rustworkx-0.18.0-cp310-abi3-manylinux_2_28_s390x.whl", hash = "sha256:567ace3b0d8ac709dc4ad4ce164472f5f59508739355221480170327a6736777", size = 3170967, upload-time = "2026-06-18T04:38:39.929Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/2f/95ca9ef9285cc6793b3e041efca6c5a194ca6731b0af732631ee074a67b3/rustworkx-0.18.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b91cdcffeab5f498de44905a373e47e345da8f6c4f40fc969421548c4e7a5219", size = 2254008, upload-time = "2026-06-18T04:38:08.245Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/7b/9df6a80162e6f90fd9945e3f8063c2aa41285c27aefce1d171f99c2b829f/rustworkx-0.18.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03dd1ab42974bc0ff957eccc330c0c0d9887d88240c0cd050039e215af5a3c45", size = 2447140, upload-time = "2026-06-18T04:38:09.767Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/b3/4d159b33bbc73dbebad050a0226dcfb0753d7f05fa55a8d5351e85c8c953/rustworkx-0.18.0-cp310-abi3-win32.whl", hash = "sha256:eb87a45864ffe36e4d86e826d12bb54dac9e4f76c214997dee624281d8711684", size = 2057237, upload-time = "2026-06-18T04:38:11.28Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/1b/b0dc8d3751c0c58d16db72bff2954a9d3871c166147007590a3aaec4d42e/rustworkx-0.18.0-cp310-abi3-win_amd64.whl", hash = "sha256:602df896b4479b83c6456f702f8ba2ac1cbb972b30723d5fe2e84e6ff3de7d70", size = 2289224, upload-time = "2026-06-18T04:38:12.961Z" }, + { url = "https://files.pythonhosted.org/packages/2a/70/18b310752b0652d4a755b46853268c00c33401101a47f87697ad25966453/rustworkx-0.18.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7440ba70b87bd16811d92e57d76108840dbdd89aa2fc55e4d324fa2fb6b7f9c9", size = 2295936 }, + { url = "https://files.pythonhosted.org/packages/02/4e/09152f4422204f020346a8a2b0e2f05f12d8cc60eeeee99b24f649985514/rustworkx-0.18.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:7fce5218ebfb8d8f5313def1f15aad8d3c3c2bbe03e33805dff8cf8bb70370a1", size = 2145057 }, + { url = "https://files.pythonhosted.org/packages/22/68/69198f41f7f9c39fb5b2e561bf41d4cbd117fa5dfdf4631d8ba28e5ff69b/rustworkx-0.18.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7e0c626f76bc71d414a02502be7ec0ac2dd6eca369886bc66a2650607f2d9de6", size = 2391659 }, + { url = "https://files.pythonhosted.org/packages/e1/81/8ac568efe0289b0ecd826cd697c3581b6a25e134145926cbfef762656167/rustworkx-0.18.0-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:91b37c5bb54e233e16c4f47383385f17be03e6a344821c2f6a39a0aebee54538", size = 2189469 }, + { url = "https://files.pythonhosted.org/packages/f2/a4/96492d7af2ddcd15368f80329e3514060b122f306c4406fac98c08a97865/rustworkx-0.18.0-cp310-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:bcd15f7a637ca0654f329ed42a6db0cf7eac15ed89e0b32cb9d16b1b21937979", size = 2504116 }, + { url = "https://files.pythonhosted.org/packages/4d/b0/005383bd7dc110b06ca731bd2115482f3a8c6389e0c8fe1f7f259380c4c8/rustworkx-0.18.0-cp310-abi3-manylinux_2_28_s390x.whl", hash = "sha256:567ace3b0d8ac709dc4ad4ce164472f5f59508739355221480170327a6736777", size = 3170967 }, + { url = "https://files.pythonhosted.org/packages/0d/2f/95ca9ef9285cc6793b3e041efca6c5a194ca6731b0af732631ee074a67b3/rustworkx-0.18.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b91cdcffeab5f498de44905a373e47e345da8f6c4f40fc969421548c4e7a5219", size = 2254008 }, + { url = "https://files.pythonhosted.org/packages/19/7b/9df6a80162e6f90fd9945e3f8063c2aa41285c27aefce1d171f99c2b829f/rustworkx-0.18.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03dd1ab42974bc0ff957eccc330c0c0d9887d88240c0cd050039e215af5a3c45", size = 2447140 }, + { url = "https://files.pythonhosted.org/packages/5d/b3/4d159b33bbc73dbebad050a0226dcfb0753d7f05fa55a8d5351e85c8c953/rustworkx-0.18.0-cp310-abi3-win32.whl", hash = "sha256:eb87a45864ffe36e4d86e826d12bb54dac9e4f76c214997dee624281d8711684", size = 2057237 }, + { url = "https://files.pythonhosted.org/packages/99/1b/b0dc8d3751c0c58d16db72bff2954a9d3871c166147007590a3aaec4d42e/rustworkx-0.18.0-cp310-abi3-win_amd64.whl", hash = "sha256:602df896b4479b83c6456f702f8ba2ac1cbb972b30723d5fe2e84e6ff3de7d70", size = 2289224 }, ] [[package]] name = "schemez" version = "2.2.29" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docstring-parser" }, { name = "griffelib" }, { name = "pydantic" }, { name = "universal-pathlib" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/f9/394ef53eca56a08448feebd4645fc1f10719e9286faaaf19b02277ec03be/schemez-2.2.29.tar.gz", hash = "sha256:801fd18eeda003c061f43d1d8943fe206fd3d64dc2da0531bda49334c9056579", size = 76147, upload-time = "2026-03-16T01:02:52.627Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/f9/394ef53eca56a08448feebd4645fc1f10719e9286faaaf19b02277ec03be/schemez-2.2.29.tar.gz", hash = "sha256:801fd18eeda003c061f43d1d8943fe206fd3d64dc2da0531bda49334c9056579", size = 76147 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/97/086c3f07ba3a0bbe13e553e59666bba49e7393f3a9cb451e6dc3867c2ea8/schemez-2.2.29-py3-none-any.whl", hash = "sha256:7517451ee5c0a8d3b3ebcd5327622ac3c8e9b09e3f7fad476c5ffd9e78f16d47", size = 94033, upload-time = "2026-03-16T01:02:51.055Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/086c3f07ba3a0bbe13e553e59666bba49e7393f3a9cb451e6dc3867c2ea8/schemez-2.2.29-py3-none-any.whl", hash = "sha256:7517451ee5c0a8d3b3ebcd5327622ac3c8e9b09e3f7fad476c5ffd9e78f16d47", size = 94033 }, ] [package.optional-dependencies] @@ -4708,16 +4494,16 @@ codegen = [ [[package]] name = "searchly" version = "2.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv", extra = ["httpx"] }, { name = "pydantic" }, { name = "schemez" }, { name = "universal-pathlib" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/99/adf947a5815289e0aea96080e2714b31212331701647cc8a12789ffd91cf/searchly-2.1.0.tar.gz", hash = "sha256:63b861aecfb2938b5484a4566eeb4e8909cd6b8ea468d80490af69bbd490fe7c", size = 17852, upload-time = "2026-01-04T06:50:14.096Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/99/adf947a5815289e0aea96080e2714b31212331701647cc8a12789ffd91cf/searchly-2.1.0.tar.gz", hash = "sha256:63b861aecfb2938b5484a4566eeb4e8909cd6b8ea468d80490af69bbd490fe7c", size = 17852 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/69/02d619394e196cc4b57b841790f769c6fcf2050ad0249880a0e1b9064ab6/searchly-2.1.0-py3-none-any.whl", hash = "sha256:70272f8e7e6c08ffd6ac2963e9c7aba6769afe97295b1030fe88abc8cea6c310", size = 32982, upload-time = "2026-01-04T06:50:15.455Z" }, + { url = "https://files.pythonhosted.org/packages/67/69/02d619394e196cc4b57b841790f769c6fcf2050ad0249880a0e1b9064ab6/searchly-2.1.0-py3-none-any.whl", hash = "sha256:70272f8e7e6c08ffd6ac2963e9c7aba6769afe97295b1030fe88abc8cea6c310", size = 32982 }, ] [package.optional-dependencies] @@ -4730,157 +4516,157 @@ all = [ [[package]] name = "secretstorage" version = "3.5.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, + { name = "cryptography", marker = "sys_platform != 'win32'" }, + { name = "jeepney", marker = "sys_platform != 'win32'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554 }, ] [[package]] name = "semver" version = "3.0.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730, upload-time = "2025-01-24T13:19:27.617Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912, upload-time = "2025-01-24T13:19:24.949Z" }, + { url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912 }, ] [[package]] name = "setuptools" version = "83.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090 }, ] [[package]] name = "shellingham" version = "1.5.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, ] [[package]] name = "six" version = "1.17.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] [[package]] name = "slack-sdk" version = "3.43.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/75/a4964eb771a0c74d79ee7a3bee6fb5d9718909dd1b675e80d62a6a0ad90a/slack_sdk-3.43.0.tar.gz", hash = "sha256:0553152e46c4259eb69f7464cdadc35ba4802ca10f9f5a849c92cf03d6c2ba07", size = 252769, upload-time = "2026-06-30T18:04:41.59Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/75/a4964eb771a0c74d79ee7a3bee6fb5d9718909dd1b675e80d62a6a0ad90a/slack_sdk-3.43.0.tar.gz", hash = "sha256:0553152e46c4259eb69f7464cdadc35ba4802ca10f9f5a849c92cf03d6c2ba07", size = 252769 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/55/42141b8338d46323d5b3c6095201b044c670c20f898643b322ea9b1543a1/slack_sdk-3.43.0-py2.py3-none-any.whl", hash = "sha256:4b6557c65577fc172f685af218b811f9f3b4909e24cddd839ada09565f10c585", size = 315866, upload-time = "2026-06-30T18:04:39.636Z" }, + { url = "https://files.pythonhosted.org/packages/e4/55/42141b8338d46323d5b3c6095201b044c670c20f898643b322ea9b1543a1/slack_sdk-3.43.0-py2.py3-none-any.whl", hash = "sha256:4b6557c65577fc172f685af218b811f9f3b4909e24cddd839ada09565f10c585", size = 315866 }, ] [[package]] name = "slackify-markdown" version = "0.2.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/07/e6f7fe419b21e7bc198f62c9074f7d5ab398b67dc455ccb3190dc31f91f7/slackify_markdown-0.2.4.tar.gz", hash = "sha256:90b57c0a4ef81306c2208573ab4d3afc945797eed0dd04c4876b4a0ca7222619", size = 13604, upload-time = "2026-05-25T15:04:08.074Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/07/e6f7fe419b21e7bc198f62c9074f7d5ab398b67dc455ccb3190dc31f91f7/slackify_markdown-0.2.4.tar.gz", hash = "sha256:90b57c0a4ef81306c2208573ab4d3afc945797eed0dd04c4876b4a0ca7222619", size = 13604 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/36/b4f9788907b95b5f217fa9602c94f97ceb2af902aaa2396f37ee7b1c74a0/slackify_markdown-0.2.4-py3-none-any.whl", hash = "sha256:5dd7539facafb6c0add7d895bf3501ba25cd7539ee8e06fc0a3adfef237d647c", size = 7453, upload-time = "2026-05-25T15:04:06.825Z" }, + { url = "https://files.pythonhosted.org/packages/b9/36/b4f9788907b95b5f217fa9602c94f97ceb2af902aaa2396f37ee7b1c74a0/slackify_markdown-0.2.4-py3-none-any.whl", hash = "sha256:5dd7539facafb6c0add7d895bf3501ba25cd7539ee8e06fc0a3adfef237d647c", size = 7453 }, ] [[package]] name = "slashed" version = "1.2.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bashlex" }, { name = "psygnal" }, { name = "universal-pathlib" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/08/667f754d5e9ae6b59fd93d5f91e4bb70a54ca6daff00daa117748c4e9a10/slashed-1.2.1.tar.gz", hash = "sha256:35928d84c7813ae6a744aa4a772d1526b56d69b7a8a8d42005ae3b1667505ee5", size = 48309, upload-time = "2026-02-26T12:10:22.819Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/08/667f754d5e9ae6b59fd93d5f91e4bb70a54ca6daff00daa117748c4e9a10/slashed-1.2.1.tar.gz", hash = "sha256:35928d84c7813ae6a744aa4a772d1526b56d69b7a8a8d42005ae3b1667505ee5", size = 48309 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/b6/906a4894d28eda3b15d2409aebf0943826b3aa1b611528ef5434c44b47f8/slashed-1.2.1-py3-none-any.whl", hash = "sha256:e6b381d6e5f6d6bb9529c2be0c8f9be83687d5797f99839609b78fc13f166848", size = 61284, upload-time = "2026-02-26T12:10:21.45Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b6/906a4894d28eda3b15d2409aebf0943826b3aa1b611528ef5434c44b47f8/slashed-1.2.1-py3-none-any.whl", hash = "sha256:e6b381d6e5f6d6bb9529c2be0c8f9be83687d5797f99839609b78fc13f166848", size = 61284 }, ] [[package]] name = "smmap" version = "5.0.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390 }, ] [[package]] name = "sniffio" version = "1.3.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, ] [[package]] name = "socksio" version = "1.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/5c/48a7d9495be3d1c651198fd99dbb6ce190e2274d0f28b9051307bdec6b85/socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac", size = 19055, upload-time = "2020-04-17T15:50:34.664Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/48a7d9495be3d1c651198fd99dbb6ce190e2274d0f28b9051307bdec6b85/socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac", size = 19055 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/c3/6eeb6034408dac0fa653d126c9204ade96b819c936e136c5e8a6897eee9c/socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3", size = 12763, upload-time = "2020-04-17T15:50:31.878Z" }, + { url = "https://files.pythonhosted.org/packages/37/c3/6eeb6034408dac0fa653d126c9204ade96b819c936e136c5e8a6897eee9c/socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3", size = 12763 }, ] [[package]] name = "sounddevice" version = "0.5.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/f9/2592608737553638fca98e21e54bfec40bf577bb98a61b2770c912aab25e/sounddevice-0.5.5.tar.gz", hash = "sha256:22487b65198cb5bf2208755105b524f78ad173e5ab6b445bdab1c989f6698df3", size = 143191, upload-time = "2026-01-23T18:36:43.529Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/f9/2592608737553638fca98e21e54bfec40bf577bb98a61b2770c912aab25e/sounddevice-0.5.5.tar.gz", hash = "sha256:22487b65198cb5bf2208755105b524f78ad173e5ab6b445bdab1c989f6698df3", size = 143191 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/0a/478e441fd049002cf308520c0d62dd8333e7c6cc8d997f0dda07b9fbcc46/sounddevice-0.5.5-py3-none-any.whl", hash = "sha256:30ff99f6c107f49d25ad16a45cacd8d91c25a1bcdd3e81a206b921a3a6405b1f", size = 32807, upload-time = "2026-01-23T18:36:35.649Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/f9/c037c35f6d0b6bc3bc7bfb314f1d6f1f9a341328ef47cd63fc4f850a7b27/sounddevice-0.5.5-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:05eb9fd6c54c38d67741441c19164c0dae8ce80453af2d8c4ad2e7823d15b722", size = 108557, upload-time = "2026-01-23T18:36:37.41Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/a1/d19dd9889cd4bce2e233c4fac007cd8daaf5b9fe6e6a5d432cf17be0b807/sounddevice-0.5.5-py3-none-win32.whl", hash = "sha256:1234cc9b4c9df97b6cbe748146ae0ec64dd7d6e44739e8e42eaa5b595313a103", size = 317765, upload-time = "2026-01-23T18:36:39.047Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/0e/002ed7c4c1c2ab69031f78989d3b789fee3a7fba9e586eb2b81688bf4961/sounddevice-0.5.5-py3-none-win_amd64.whl", hash = "sha256:cfc6b2c49fb7f555591c78cb8ecf48d6a637fd5b6e1db5fec6ed9365d64b3519", size = 365324, upload-time = "2026-01-23T18:36:40.496Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/39/a61d4b83a7746b70d23d9173be688c0c6bfc7173772344b7442c2c155497/sounddevice-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3861901ddd8230d2e0e8ae62ac320cdd4c688d81df89da036dcb812f757bb3e6", size = 317115, upload-time = "2026-01-23T18:36:42.235Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0a/478e441fd049002cf308520c0d62dd8333e7c6cc8d997f0dda07b9fbcc46/sounddevice-0.5.5-py3-none-any.whl", hash = "sha256:30ff99f6c107f49d25ad16a45cacd8d91c25a1bcdd3e81a206b921a3a6405b1f", size = 32807 }, + { url = "https://files.pythonhosted.org/packages/56/f9/c037c35f6d0b6bc3bc7bfb314f1d6f1f9a341328ef47cd63fc4f850a7b27/sounddevice-0.5.5-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:05eb9fd6c54c38d67741441c19164c0dae8ce80453af2d8c4ad2e7823d15b722", size = 108557 }, + { url = "https://files.pythonhosted.org/packages/88/a1/d19dd9889cd4bce2e233c4fac007cd8daaf5b9fe6e6a5d432cf17be0b807/sounddevice-0.5.5-py3-none-win32.whl", hash = "sha256:1234cc9b4c9df97b6cbe748146ae0ec64dd7d6e44739e8e42eaa5b595313a103", size = 317765 }, + { url = "https://files.pythonhosted.org/packages/c3/0e/002ed7c4c1c2ab69031f78989d3b789fee3a7fba9e586eb2b81688bf4961/sounddevice-0.5.5-py3-none-win_amd64.whl", hash = "sha256:cfc6b2c49fb7f555591c78cb8ecf48d6a637fd5b6e1db5fec6ed9365d64b3519", size = 365324 }, + { url = "https://files.pythonhosted.org/packages/4e/39/a61d4b83a7746b70d23d9173be688c0c6bfc7173772344b7442c2c155497/sounddevice-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3861901ddd8230d2e0e8ae62ac320cdd4c688d81df89da036dcb812f757bb3e6", size = 317115 }, ] [[package]] name = "soupsieve" version = "2.9.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/38/e12680bbe6b4f8f3d17adcaf38d26850aa756c85cf4a80e79fc12a018fe8/soupsieve-2.9.1.tar.gz", hash = "sha256:c33e6605bbc71dd628b00c632d58ae607c22bade247e52553928f83bbb75b4ba", size = 122261, upload-time = "2026-07-21T16:57:17.452Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/38/e12680bbe6b4f8f3d17adcaf38d26850aa756c85cf4a80e79fc12a018fe8/soupsieve-2.9.1.tar.gz", hash = "sha256:c33e6605bbc71dd628b00c632d58ae607c22bade247e52553928f83bbb75b4ba", size = 122261 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/2c/437fe806897c2d6cfdc3ee43a18da8bf8e568530a4ae9bac781541ca9896/soupsieve-2.9.1-py3-none-any.whl", hash = "sha256:4f4477399246b7a0c720a88ca2454b11cd6bb9ae4c9d170140786e916776c14c", size = 37404, upload-time = "2026-07-21T16:57:16.421Z" }, + { url = "https://files.pythonhosted.org/packages/0f/2c/437fe806897c2d6cfdc3ee43a18da8bf8e568530a4ae9bac781541ca9896/soupsieve-2.9.1-py3-none-any.whl", hash = "sha256:4f4477399246b7a0c720a88ca2454b11cd6bb9ae4c9d170140786e916776c14c", size = 37404 }, ] [[package]] name = "sqlalchemy" version = "2.0.51" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184, upload-time = "2026-06-15T16:08:50.374Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735, upload-time = "2026-06-15T16:19:46.934Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756, upload-time = "2026-06-15T16:26:41.336Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055, upload-time = "2026-06-15T16:19:49.286Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850, upload-time = "2026-06-15T16:26:43.017Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721, upload-time = "2026-06-15T16:23:12.36Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615, upload-time = "2026-06-15T16:23:13.906Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334, upload-time = "2026-06-15T16:09:22.418Z" }, + { url = "https://files.pythonhosted.org/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184 }, + { url = "https://files.pythonhosted.org/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735 }, + { url = "https://files.pythonhosted.org/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756 }, + { url = "https://files.pythonhosted.org/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055 }, + { url = "https://files.pythonhosted.org/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850 }, + { url = "https://files.pythonhosted.org/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721 }, + { url = "https://files.pythonhosted.org/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615 }, + { url = "https://files.pythonhosted.org/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334 }, ] [package.optional-dependencies] @@ -4893,184 +4679,184 @@ aiosqlite = [ [[package]] name = "sqlmodel" version = "0.0.39" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/ee/22a0559283c3cf6048678e787ed5d4959dcd00dedd8ba4567eeae684eeb1/sqlmodel-0.0.39.tar.gz", hash = "sha256:23d8e50a8d8ee936032ed79c55023a5d618dd6bc3c510bbf4909d1a7a605a570", size = 91057, upload-time = "2026-06-25T13:01:38.475Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/ee/22a0559283c3cf6048678e787ed5d4959dcd00dedd8ba4567eeae684eeb1/sqlmodel-0.0.39.tar.gz", hash = "sha256:23d8e50a8d8ee936032ed79c55023a5d618dd6bc3c510bbf4909d1a7a605a570", size = 91057 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/7d/b9813a582d4eb310be35e1fc7dfaae71207d7b62e9e53be314ebd251b53b/sqlmodel-0.0.39-py3-none-any.whl", hash = "sha256:90ebe92ce5cc11d7fff8dc7cb594790a102333c8fe7c14865254f6fc5c939795", size = 29680, upload-time = "2026-06-25T13:01:37.494Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7d/b9813a582d4eb310be35e1fc7dfaae71207d7b62e9e53be314ebd251b53b/sqlmodel-0.0.39-py3-none-any.whl", hash = "sha256:90ebe92ce5cc11d7fff8dc7cb594790a102333c8fe7c14865254f6fc5c939795", size = 29680 }, ] [[package]] name = "sse-starlette" version = "3.4.6" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "starlette" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/10/a34c656829ffc1c4b22ef36d70d9ebb6b99c020e2aeb17cee5485099f028/sse_starlette-3.4.6.tar.gz", hash = "sha256:725f8a1bd6d26ae1b2c9610c0ef5065dfdd496f3988d28adcf8c4b49dc25c627", size = 32542, upload-time = "2026-07-20T14:16:32.201Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/10/a34c656829ffc1c4b22ef36d70d9ebb6b99c020e2aeb17cee5485099f028/sse_starlette-3.4.6.tar.gz", hash = "sha256:725f8a1bd6d26ae1b2c9610c0ef5065dfdd496f3988d28adcf8c4b49dc25c627", size = 32542 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/36/e10c1d1b7ca881d2625db2ec28508578499187bb1c389952c398474e1834/sse_starlette-3.4.6-py3-none-any.whl", hash = "sha256:56217ab4c9a9f9c5db7b21e08732d3e7c2b807f45231ad23de0551a24c4a41f6", size = 16516, upload-time = "2026-07-20T14:16:30.978Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/e10c1d1b7ca881d2625db2ec28508578499187bb1c389952c398474e1834/sse_starlette-3.4.6-py3-none-any.whl", hash = "sha256:56217ab4c9a9f9c5db7b21e08732d3e7c2b807f45231ad23de0551a24c4a41f6", size = 16516 }, ] [[package]] name = "sseclient-py" version = "1.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/2e/59920f7d66b7f9932a3d83dd0ec53fab001be1e058bf582606fe414a5198/sseclient_py-1.9.0-py3-none-any.whl", hash = "sha256:340062b1587fc2880892811e2ab5b176d98ef3eee98b3672ff3a3ba1e8ed0f6f", size = 8351, upload-time = "2026-01-02T23:39:30.995Z" }, + { url = "https://files.pythonhosted.org/packages/4d/2e/59920f7d66b7f9932a3d83dd0ec53fab001be1e058bf582606fe414a5198/sseclient_py-1.9.0-py3-none-any.whl", hash = "sha256:340062b1587fc2880892811e2ab5b176d98ef3eee98b3672ff3a3ba1e8ed0f6f", size = 8351 }, ] [[package]] name = "starlette" version = "1.3.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632 }, ] [[package]] name = "structlog" version = "26.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/89/b4a0bcfdf4f71a3dea31379f095929613d7e4528a0996bca6aa964cd0dca/structlog-26.1.0.tar.gz", hash = "sha256:f63a716cbd1b1291cf7661de7794b455acfa4c43c5bcf1630e6ad5ddc1adb3b7", size = 1459881, upload-time = "2026-06-06T07:33:39.348Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/89/b4a0bcfdf4f71a3dea31379f095929613d7e4528a0996bca6aa964cd0dca/structlog-26.1.0.tar.gz", hash = "sha256:f63a716cbd1b1291cf7661de7794b455acfa4c43c5bcf1630e6ad5ddc1adb3b7", size = 1459881 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/18/489c97b834dfff9cf2fc2507cede4bcd4b11e67f84bc462acd1992496f86/structlog-26.1.0-py3-none-any.whl", hash = "sha256:e081a26d6c373e6d201eca24eede26d8ffab07f88f477822e679183428d3d91e", size = 73764, upload-time = "2026-06-06T07:33:38.046Z" }, + { url = "https://files.pythonhosted.org/packages/a9/18/489c97b834dfff9cf2fc2507cede4bcd4b11e67f84bc462acd1992496f86/structlog-26.1.0-py3-none-any.whl", hash = "sha256:e081a26d6c373e6d201eca24eede26d8ffab07f88f477822e679183428d3d91e", size = 73764 }, ] [[package]] name = "sublime-search" version = "0.5.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/c9/134ea2f0569f432439e8d5d4b834a9445064720bec13f9304aaa836cdb34/sublime_search-0.5.0.tar.gz", hash = "sha256:c316df5cc5481e602205a5abff5e669b1bb960afb25e6578abfc15a23bdd1ce1", size = 44314, upload-time = "2026-01-30T04:26:32.34Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/c8/848124cf507fb58956ea4058045e654cbfac9fb684a85435e67d9d62c0e9/sublime_search-0.5.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6f7edd7dd33aac1036af793f0bf8fc0d65233803280a9ac9d7fb01c54a19cb9f", size = 970575, upload-time = "2026-01-30T04:25:55.346Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/5b/360045d55180358b242a913d11528745e5fa001575606dc7a88c8346c168/sublime_search-0.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dda52fc8c1cfd23764813a70f767f62fcd78fb703759e73fd69d5902c6a8df9", size = 923303, upload-time = "2026-01-30T04:25:49.275Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/36/e3d4542b8bc3ab244f0405e2da9e89f4fe60a07d7f6a6b427d5a9e815e63/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5eba812aaa5335b8d18f741ba6a8f8a9ce34fb277021e809738991ac88a34b5", size = 1044339, upload-time = "2026-01-30T04:24:59.107Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/63/8b77d4bdf761abc41edd616dfda907b2bd0d82b6b43ad1c28958a9474150/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5393bc2abe371aa1c49c99ddcb170a2a2526f6e99cd724aeb2621e63e776d9ab", size = 999821, upload-time = "2026-01-30T04:25:08.129Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/89/4c32e535172269899f1b92ec75b80e8b21a6f3f806831e46baa982ece336/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8586c501e25c8f148643052b3441729190e390115e480051da6c05f00848fa71", size = 1220622, upload-time = "2026-01-30T04:25:18.464Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/e6/e72fca8916a6e149bcd7f388b2487f4fecca0c2d888ffa5f53d75bb988f2/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d02882d07a246092dd544ca56d52493dbf4333a006b4ba41a56c7dde22412c", size = 1108664, upload-time = "2026-01-30T04:25:28.086Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/94/0eaa0e2f7e1d664b0a538d2292fd11776d471322055970b778f4bfa622de/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4439832149ad6cac4cdd051feb584be31eccf3fa5fcf8a2259c4e0dd5113887d", size = 1065522, upload-time = "2026-01-30T04:25:43.296Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/37/fc178eabbd15e7fd26cf3672ab10b45f657ec32b793a98e8d7e1ab73be66/sublime_search-0.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:203c79a7b1b1989acf215c850dd7b9070e3db8742cd782dc9f7186bc07f10592", size = 1093571, upload-time = "2026-01-30T04:25:37.107Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/64/68f01cdcf27ee3b6da7a7829ade6fb6002f51ae4f1081ebda473c5fa3e99/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5c8d5798549bd60f11f99dc4a999ded9d572c6f26a4febe46f8ff11a2ca32936", size = 1220745, upload-time = "2026-01-30T04:25:59.892Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/22/e417f582863e96ab8953dc026243ca5cb2b5b026ef5f85adfca0b8abd9b7/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bd07603402f0078d225513154bfbfde50d28566004f15b0097bb995767273e85", size = 1265335, upload-time = "2026-01-30T04:26:09.102Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/cb/40df16111219449ef63a543371c5782e43a3a75dd844c7ff2056320495e4/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9fbb0abdae6f4241c91c041f23977a7836a3632da2efee76e170857393b813c6", size = 1275562, upload-time = "2026-01-30T04:26:17.235Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/8a/f7dc6351e154fc50ca6f27c5a983f7b42b6ec991a2b328f3dcbb9e5b5d56/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5d1ad4e2adbe6e8c744e67ee497e1cd4b149e9b1ccfea1d99e1697224d49d3d0", size = 1301079, upload-time = "2026-01-30T04:26:26.852Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/fc/c6f965abc719c722c57ed04ea6d63ec6c0178d8118cf1b71952e43c29d1d/sublime_search-0.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:9822ba29d5f5e72d7f52fa8408cc3e8179be63476483aa6a97aa21a202b62f05", size = 819477, upload-time = "2026-01-30T04:26:35.295Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/98/6a5ce632354d0788f3d4b11c909f9c378431c2809bc4720e20b6d6271fcb/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98724b9e6b0c64dcde9affa49bcce521970abb492e2f8a76d0a177d8b93236b", size = 1044689, upload-time = "2026-01-30T04:25:00.239Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/e3/8b26728a1047c5aac97c0b337f4eed16098f95425bb055af40f22c0d21c3/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4dd8dcfd68a966265202c8aa62be6ec61b51579b095eab728f84be23063c6b0", size = 997831, upload-time = "2026-01-30T04:25:09.614Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/61/d11af20984ef94d50d28b36a1fb017c0c7c5bc1868ff10a400f8c693cc41/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:67f3329c45152e712fcdefc74d2e0f2d7382b72c62ff4b0e5750c25cc5edde61", size = 1220861, upload-time = "2026-01-30T04:25:19.918Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/ec/ad95b318462bfcb43ea1117c7129b84cb8afedb83eea62522a9d5f0a767b/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48120a3b1f4c8383fbe43dadcea6a50e974ed162febbe407091dd028a5d228dc", size = 1108905, upload-time = "2026-01-30T04:25:29.601Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/97/263582e7135c552d562fbd69a7ad216892331e445fc1c2acce3c2dde4af9/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a2ab2477cf307d301c133a0350426bb22a5727fd060e3fe201852910ccb26e13", size = 1220691, upload-time = "2026-01-30T04:26:01.145Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/93/3f88bbb6821332194e729af5add9eabc90c0a7e24359546e06313961c5a8/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c7670d48b647fda37c279013db03400dbcfd44cc508a69ae690fe9a9e00d7ee0", size = 1264224, upload-time = "2026-01-30T04:26:10.226Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/b7/48c07d3c293328d18f70b1ecba1ff781e9dc7bde9fc4a8b97aba2411c587/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3dc9511d4a47fc616656c3b82322a20ffaf283d29560134169673206c4133b1d", size = 1274424, upload-time = "2026-01-30T04:26:18.85Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/78/17b7947310115e9c006eb05e1b4f1458fedd12864245f675454dd7be9ce6/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:052bc85ba46b34dbb462d3e7a1c2b982fdccb88bf225ba95952ed2cef3ad898c", size = 1302705, upload-time = "2026-01-30T04:26:27.966Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c9/134ea2f0569f432439e8d5d4b834a9445064720bec13f9304aaa836cdb34/sublime_search-0.5.0.tar.gz", hash = "sha256:c316df5cc5481e602205a5abff5e669b1bb960afb25e6578abfc15a23bdd1ce1", size = 44314 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/c8/848124cf507fb58956ea4058045e654cbfac9fb684a85435e67d9d62c0e9/sublime_search-0.5.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6f7edd7dd33aac1036af793f0bf8fc0d65233803280a9ac9d7fb01c54a19cb9f", size = 970575 }, + { url = "https://files.pythonhosted.org/packages/60/5b/360045d55180358b242a913d11528745e5fa001575606dc7a88c8346c168/sublime_search-0.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dda52fc8c1cfd23764813a70f767f62fcd78fb703759e73fd69d5902c6a8df9", size = 923303 }, + { url = "https://files.pythonhosted.org/packages/90/36/e3d4542b8bc3ab244f0405e2da9e89f4fe60a07d7f6a6b427d5a9e815e63/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5eba812aaa5335b8d18f741ba6a8f8a9ce34fb277021e809738991ac88a34b5", size = 1044339 }, + { url = "https://files.pythonhosted.org/packages/d0/63/8b77d4bdf761abc41edd616dfda907b2bd0d82b6b43ad1c28958a9474150/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5393bc2abe371aa1c49c99ddcb170a2a2526f6e99cd724aeb2621e63e776d9ab", size = 999821 }, + { url = "https://files.pythonhosted.org/packages/e0/89/4c32e535172269899f1b92ec75b80e8b21a6f3f806831e46baa982ece336/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8586c501e25c8f148643052b3441729190e390115e480051da6c05f00848fa71", size = 1220622 }, + { url = "https://files.pythonhosted.org/packages/93/e6/e72fca8916a6e149bcd7f388b2487f4fecca0c2d888ffa5f53d75bb988f2/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d02882d07a246092dd544ca56d52493dbf4333a006b4ba41a56c7dde22412c", size = 1108664 }, + { url = "https://files.pythonhosted.org/packages/08/94/0eaa0e2f7e1d664b0a538d2292fd11776d471322055970b778f4bfa622de/sublime_search-0.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4439832149ad6cac4cdd051feb584be31eccf3fa5fcf8a2259c4e0dd5113887d", size = 1065522 }, + { url = "https://files.pythonhosted.org/packages/5c/37/fc178eabbd15e7fd26cf3672ab10b45f657ec32b793a98e8d7e1ab73be66/sublime_search-0.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:203c79a7b1b1989acf215c850dd7b9070e3db8742cd782dc9f7186bc07f10592", size = 1093571 }, + { url = "https://files.pythonhosted.org/packages/0e/64/68f01cdcf27ee3b6da7a7829ade6fb6002f51ae4f1081ebda473c5fa3e99/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5c8d5798549bd60f11f99dc4a999ded9d572c6f26a4febe46f8ff11a2ca32936", size = 1220745 }, + { url = "https://files.pythonhosted.org/packages/2a/22/e417f582863e96ab8953dc026243ca5cb2b5b026ef5f85adfca0b8abd9b7/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bd07603402f0078d225513154bfbfde50d28566004f15b0097bb995767273e85", size = 1265335 }, + { url = "https://files.pythonhosted.org/packages/f1/cb/40df16111219449ef63a543371c5782e43a3a75dd844c7ff2056320495e4/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9fbb0abdae6f4241c91c041f23977a7836a3632da2efee76e170857393b813c6", size = 1275562 }, + { url = "https://files.pythonhosted.org/packages/e4/8a/f7dc6351e154fc50ca6f27c5a983f7b42b6ec991a2b328f3dcbb9e5b5d56/sublime_search-0.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5d1ad4e2adbe6e8c744e67ee497e1cd4b149e9b1ccfea1d99e1697224d49d3d0", size = 1301079 }, + { url = "https://files.pythonhosted.org/packages/a8/fc/c6f965abc719c722c57ed04ea6d63ec6c0178d8118cf1b71952e43c29d1d/sublime_search-0.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:9822ba29d5f5e72d7f52fa8408cc3e8179be63476483aa6a97aa21a202b62f05", size = 819477 }, + { url = "https://files.pythonhosted.org/packages/31/98/6a5ce632354d0788f3d4b11c909f9c378431c2809bc4720e20b6d6271fcb/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98724b9e6b0c64dcde9affa49bcce521970abb492e2f8a76d0a177d8b93236b", size = 1044689 }, + { url = "https://files.pythonhosted.org/packages/b1/e3/8b26728a1047c5aac97c0b337f4eed16098f95425bb055af40f22c0d21c3/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4dd8dcfd68a966265202c8aa62be6ec61b51579b095eab728f84be23063c6b0", size = 997831 }, + { url = "https://files.pythonhosted.org/packages/1e/61/d11af20984ef94d50d28b36a1fb017c0c7c5bc1868ff10a400f8c693cc41/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:67f3329c45152e712fcdefc74d2e0f2d7382b72c62ff4b0e5750c25cc5edde61", size = 1220861 }, + { url = "https://files.pythonhosted.org/packages/f6/ec/ad95b318462bfcb43ea1117c7129b84cb8afedb83eea62522a9d5f0a767b/sublime_search-0.5.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48120a3b1f4c8383fbe43dadcea6a50e974ed162febbe407091dd028a5d228dc", size = 1108905 }, + { url = "https://files.pythonhosted.org/packages/b1/97/263582e7135c552d562fbd69a7ad216892331e445fc1c2acce3c2dde4af9/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a2ab2477cf307d301c133a0350426bb22a5727fd060e3fe201852910ccb26e13", size = 1220691 }, + { url = "https://files.pythonhosted.org/packages/39/93/3f88bbb6821332194e729af5add9eabc90c0a7e24359546e06313961c5a8/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c7670d48b647fda37c279013db03400dbcfd44cc508a69ae690fe9a9e00d7ee0", size = 1264224 }, + { url = "https://files.pythonhosted.org/packages/73/b7/48c07d3c293328d18f70b1ecba1ff781e9dc7bde9fc4a8b97aba2411c587/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3dc9511d4a47fc616656c3b82322a20ffaf283d29560134169673206c4133b1d", size = 1274424 }, + { url = "https://files.pythonhosted.org/packages/74/78/17b7947310115e9c006eb05e1b4f1458fedd12864245f675454dd7be9ce6/sublime_search-0.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:052bc85ba46b34dbb462d3e7a1c2b982fdccb88bf225ba95952ed2cef3ad898c", size = 1302705 }, ] [[package]] name = "syrupy" version = "5.5.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/39/17dde9f0c76cc5abcdeef1b2243791bb850d498f784147b8e460dd23abe8/syrupy-5.5.3.tar.gz", hash = "sha256:fa21e4ae77c89ec5abfca513338d8a7eb916da6618ca0f8db301476e0768e57a", size = 91614, upload-time = "2026-07-11T15:54:31.46Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/39/17dde9f0c76cc5abcdeef1b2243791bb850d498f784147b8e460dd23abe8/syrupy-5.5.3.tar.gz", hash = "sha256:fa21e4ae77c89ec5abfca513338d8a7eb916da6618ca0f8db301476e0768e57a", size = 91614 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/f9/617a194c1a4203279998e1859426cf2635042533a99a93d63d3ee1fb967e/syrupy-5.5.3-py3-none-any.whl", hash = "sha256:0b260a0c9dad55e1fb83818973dc36fbc1aea3fd5381592f442a986686c4171a", size = 54959, upload-time = "2026-07-11T15:54:29.836Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f9/617a194c1a4203279998e1859426cf2635042533a99a93d63d3ee1fb967e/syrupy-5.5.3-py3-none-any.whl", hash = "sha256:0b260a0c9dad55e1fb83818973dc36fbc1aea3fd5381592f442a986686c4171a", size = 54959 }, ] [[package]] name = "tabulate" version = "0.10.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" }, + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814 }, ] [[package]] name = "tenacity" version = "9.1.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926 }, ] [[package]] name = "text-unidecode" version = "1.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885, upload-time = "2019-08-30T21:36:45.405Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" }, + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154 }, ] [[package]] name = "tiktoken" version = "0.13.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/e5/5f3cb2159769d0f4324c0e9e87f9de3c4b1cd45848a96b2eb3566ad5ca77/tiktoken-0.13.0.tar.gz", hash = "sha256:c9435714c3a84c2319499de9a300c0e604449dd0799ff246458b3bb6a7f433c1", size = 38986, upload-time = "2026-05-15T04:51:27.153Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/e5/5f3cb2159769d0f4324c0e9e87f9de3c4b1cd45848a96b2eb3566ad5ca77/tiktoken-0.13.0.tar.gz", hash = "sha256:c9435714c3a84c2319499de9a300c0e604449dd0799ff246458b3bb6a7f433c1", size = 38986 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/83/b096c859c2a47c11731bf2f5885f4028b809dfe2396582883eed9cae372f/tiktoken-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5df5d1507bd245f1ccad4a074698240021239e455eb0bb4ced4e3d7181872154", size = 1034228, upload-time = "2026-05-15T04:50:40.988Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/61/c68e123b6d753e3fc2751e9b18e732c9d8bf1e1926762e736eee935d931c/tiktoken-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fe806a50664e83a6ffd56cbd1e4f5dcc6cd32a3e7538f70dc38b1a271384545", size = 982978, upload-time = "2026-05-15T04:50:42.195Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/8b/96cc178cc584e65d363134500f297790b06cd48cdeb1e8fcf7bbe60f4715/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:125bc05005e747f993a83dc67934249932d6e4209854452cd4c0b1d53fba3ba2", size = 1116355, upload-time = "2026-05-15T04:50:43.564Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/f5/bab735d2c72ea55404b295d02d092644eb5f7cc6205e34d35eb9abfb9ab2/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5e6358911cab4adee6712da27d65573496a4f68cf8a2b5fca6a4ad10fc5748cf", size = 1135772, upload-time = "2026-05-15T04:50:44.782Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/b9/6de04ebdf904edfaad87788011b3735087a0c9ea671b9027e1e4e965e8c8/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:975cbd78d085d75d26b59660e262736dcaed1e35f8f142cd6291025c01d25486", size = 1182415, upload-time = "2026-05-15T04:50:46.422Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/9c/470a05f3b1caf038f44880e334d47ab674e0c80d514c66b375d14d5afa10/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ab9bc99fa020a4c283424590ecd7f3afd70c1c281cb3fa3192a6c3af9f9615", size = 1239879, upload-time = "2026-05-15T04:50:48.052Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/a6/c1936d16055436cb32e6c6128d68629622e00f4768562f55653752d34768/tiktoken-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:6b1615f0ff71953d19729ceb18865429c185b0a23c5353f1bbca34a394bf60f7", size = 874829, upload-time = "2026-05-15T04:50:49.202Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/07/acb5992c3772b5a36284f742cfb7a5895aa4471d1848ac31464ad50d7fdf/tiktoken-0.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6eb4a5bfbc6426938026b1a334e898ac53541360d62d8c689870160cc80abd67", size = 1033600, upload-time = "2026-05-15T04:50:50.4Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/e9/742e9aec30f59b9f161f7ff7cd072e02ea836c9e1c0854a8076dfcd40d5c/tiktoken-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:43cee3e5400573b2046fbf092cc7a5bc30164f9e4c95ce20714da929df48737a", size = 982516, upload-time = "2026-05-15T04:50:52.03Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/74/ca1541b053e7648254d2e4b42a253e1bb4359f2c91a0a8d49228c794e1a0/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7de52e3f566d19b3b11bd37eea552c6c305ad74081f736882bd44d148ed4c48d", size = 1115518, upload-time = "2026-05-15T04:50:53.543Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/e3/93825eaf5a4a504795b787e5d5dea07fbeb3dabf97aa7b450be8bde59c89/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:51384448aa508e4df84c0f7c1dc3211c7f7b8096325660ee5fc82f3e11b381ce", size = 1136867, upload-time = "2026-05-15T04:50:55.191Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/46/002b68de6827091d5ae90b048f326e8aad8d953520950e5ce1508879414f/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e28157350f7ebf35008dd8e9e0fdb621f976e4230c881099c85e8cf07eaa50e2", size = 1181826, upload-time = "2026-05-15T04:50:56.296Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/c6/d393e3185a276505182f7abd93fe714f3c444a2be9180798fa052347504e/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:165cf1820ea4a354985c2490a5205d4cc74661c934aca79dd0368232fff94e0f", size = 1239489, upload-time = "2026-05-15T04:50:57.918Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/4d/bc07d1f1635d4897a202acc0ae11c2886eaa7325c359ba4741b47bf8e225/tiktoken-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6c43a675ca14f6f2749ba7f12075d37456015a24b859f2517b9beb4ef30807ec", size = 873820, upload-time = "2026-05-15T04:50:59.528Z" }, + { url = "https://files.pythonhosted.org/packages/9c/83/b096c859c2a47c11731bf2f5885f4028b809dfe2396582883eed9cae372f/tiktoken-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5df5d1507bd245f1ccad4a074698240021239e455eb0bb4ced4e3d7181872154", size = 1034228 }, + { url = "https://files.pythonhosted.org/packages/53/61/c68e123b6d753e3fc2751e9b18e732c9d8bf1e1926762e736eee935d931c/tiktoken-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fe806a50664e83a6ffd56cbd1e4f5dcc6cd32a3e7538f70dc38b1a271384545", size = 982978 }, + { url = "https://files.pythonhosted.org/packages/ef/8b/96cc178cc584e65d363134500f297790b06cd48cdeb1e8fcf7bbe60f4715/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:125bc05005e747f993a83dc67934249932d6e4209854452cd4c0b1d53fba3ba2", size = 1116355 }, + { url = "https://files.pythonhosted.org/packages/86/f5/bab735d2c72ea55404b295d02d092644eb5f7cc6205e34d35eb9abfb9ab2/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5e6358911cab4adee6712da27d65573496a4f68cf8a2b5fca6a4ad10fc5748cf", size = 1135772 }, + { url = "https://files.pythonhosted.org/packages/4e/b9/6de04ebdf904edfaad87788011b3735087a0c9ea671b9027e1e4e965e8c8/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:975cbd78d085d75d26b59660e262736dcaed1e35f8f142cd6291025c01d25486", size = 1182415 }, + { url = "https://files.pythonhosted.org/packages/0d/9c/470a05f3b1caf038f44880e334d47ab674e0c80d514c66b375d14d5afa10/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ab9bc99fa020a4c283424590ecd7f3afd70c1c281cb3fa3192a6c3af9f9615", size = 1239879 }, + { url = "https://files.pythonhosted.org/packages/42/a6/c1936d16055436cb32e6c6128d68629622e00f4768562f55653752d34768/tiktoken-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:6b1615f0ff71953d19729ceb18865429c185b0a23c5353f1bbca34a394bf60f7", size = 874829 }, + { url = "https://files.pythonhosted.org/packages/d6/07/acb5992c3772b5a36284f742cfb7a5895aa4471d1848ac31464ad50d7fdf/tiktoken-0.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6eb4a5bfbc6426938026b1a334e898ac53541360d62d8c689870160cc80abd67", size = 1033600 }, + { url = "https://files.pythonhosted.org/packages/14/e9/742e9aec30f59b9f161f7ff7cd072e02ea836c9e1c0854a8076dfcd40d5c/tiktoken-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:43cee3e5400573b2046fbf092cc7a5bc30164f9e4c95ce20714da929df48737a", size = 982516 }, + { url = "https://files.pythonhosted.org/packages/72/74/ca1541b053e7648254d2e4b42a253e1bb4359f2c91a0a8d49228c794e1a0/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7de52e3f566d19b3b11bd37eea552c6c305ad74081f736882bd44d148ed4c48d", size = 1115518 }, + { url = "https://files.pythonhosted.org/packages/46/e3/93825eaf5a4a504795b787e5d5dea07fbeb3dabf97aa7b450be8bde59c89/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:51384448aa508e4df84c0f7c1dc3211c7f7b8096325660ee5fc82f3e11b381ce", size = 1136867 }, + { url = "https://files.pythonhosted.org/packages/8c/46/002b68de6827091d5ae90b048f326e8aad8d953520950e5ce1508879414f/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e28157350f7ebf35008dd8e9e0fdb621f976e4230c881099c85e8cf07eaa50e2", size = 1181826 }, + { url = "https://files.pythonhosted.org/packages/db/c6/d393e3185a276505182f7abd93fe714f3c444a2be9180798fa052347504e/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:165cf1820ea4a354985c2490a5205d4cc74661c934aca79dd0368232fff94e0f", size = 1239489 }, + { url = "https://files.pythonhosted.org/packages/b7/4d/bc07d1f1635d4897a202acc0ae11c2886eaa7325c359ba4741b47bf8e225/tiktoken-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6c43a675ca14f6f2749ba7f12075d37456015a24b859f2517b9beb4ef30807ec", size = 873820 }, ] [[package]] name = "tokenizers" version = "0.23.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/60/21f715d9faba5f5407ff759472ade058ec4a507ad62bcea47cb847239a73/tokenizers-0.23.1.tar.gz", hash = "sha256:1feeeadf865a7915adc25445dea30e9933e593c31bb96c277cee36de227c8bfa", size = 365748, upload-time = "2026-04-27T14:43:25.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/60/21f715d9faba5f5407ff759472ade058ec4a507ad62bcea47cb847239a73/tokenizers-0.23.1.tar.gz", hash = "sha256:1feeeadf865a7915adc25445dea30e9933e593c31bb96c277cee36de227c8bfa", size = 365748 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/39/b87a87d5bb9470610b80a2d31df42fcffeaf35118b8b97952b2aff598cc7/tokenizers-0.23.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e03d6ffcbe0d56ee9c1ccd070e70a13fa750727c0277e138152acbc0252c2224", size = 3146732, upload-time = "2026-04-27T14:43:15.427Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/6a/068ed9f6e444c9d7e9d55ce134181325700f3d7f30410721bdc8f848d727/tokenizers-0.23.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0948bbb1ac1d7cdfc9fb6d62c596e3b7550036ad60ecd654a66ad273326324e", size = 3054954, upload-time = "2026-04-27T14:43:13.745Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/36/e006edf031154cba92b8416057d92c3abe3635e4c4b0aa0b5b9bb39dde70/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bf13402aff9bc533c89cb849ec3b412dc3fbeacc9744840e423d7bf3f7dc0e3", size = 3374081, upload-time = "2026-04-27T14:43:01.241Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/ef/7735d226f9c7f874a6bee5e3f27fb25ecabdf207d37b8cf45286d0795893/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f836ca703b89ae07919a309f9651f7a88fd5a33d5f718ba5ad0870ec0256bad6", size = 3247641, upload-time = "2026-04-27T14:43:03.856Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/d9/24827036f6e21297bfffda0768e58eb6096a4f411e932964a01707857931/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae848657742035523fdf261773630cb819a26995fcd3d9ecae0c1daf6e5a4959", size = 3585624, upload-time = "2026-04-27T14:43:10.664Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/9a/22f3582b3a4f49358293a5206e25317621ee4526bfe9cdaa0f07a12e770e/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53b09e85775d5187941e7bab30e941b4134ab4a7dd8c68e783d231fb7ca27c51", size = 3844062, upload-time = "2026-04-27T14:43:05.643Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/65/b8f8814eef95800f20721384136d9a1d22241d50b2874357cb70542c392f/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea5a0ce170074329faaa8ea3f6400ecde604b6678192688533af80980daae71a", size = 3460098, upload-time = "2026-04-27T14:43:08.854Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/d5/1353e5f677ec27c2494fb6a6725e82d56c985f53e90ec511369e7e4f02c6/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b405006415ea148a992d093699c66eb01952bf59f4d5727089a98bda45a4", size = 3346235, upload-time = "2026-04-27T14:43:12.377Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/89/39b6b8fc073fb6d413d0147aa333dc7eff7be65639ac9d19930a0b21bf33/tokenizers-0.23.1-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:56f3a77de629917652f876294dc9fe6bad4a0c43bc229dc72e59bb23a0f4729a", size = 3426398, upload-time = "2026-04-27T14:43:07.264Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/80/127c854da64827e5b79264ce524993a90dddcb320e5cd42412c5c02f9e8a/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d10a6d957ef01896dc274e890eee27d41bd0e74ef31e60616f0fc311345184e", size = 9823279, upload-time = "2026-04-27T14:43:17.222Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/ba/44c2502feb1a058f096ddfb4e0996ef3225a01a388e1a9b094e91689fe93/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1974288a609c343774f1b897c8b482c791ab17b75ab5c8c2b1737565c1d82288", size = 9644986, upload-time = "2026-04-27T14:43:19.45Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/c1/464019a9fb059870bfe4eebb4ba12208f3042035e258bf5e782906bd3847/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:120468fb4c24faf0543c835a4fabafa4deb3f20a035c9b6e83d0b553a97615d4", size = 9976181, upload-time = "2026-04-27T14:43:21.463Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/94/3ac1432bda31626071e9b6a12709b97ae05131c804b94c8f3ac622c5da32/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e3d8f40ea6268047de7046906326abed5134f27d4e8447b23763afe5808c8a96", size = 10113853, upload-time = "2026-04-27T14:43:23.617Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/dd/631b21433c771b1382535326f0eca80b9c9cee2e64961dd993bc9ac4669e/tokenizers-0.23.1-cp310-abi3-win32.whl", hash = "sha256:93120a930b919416da7cd10a2f606ac9919cc69cacae7980fa2140e277660948", size = 2536263, upload-time = "2026-04-27T14:43:29.888Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/c9/2553f72aaf65a2797d4229e37fa7fbe38ffbf3e32912d31bdd78b3323e59/tokenizers-0.23.1-cp310-abi3-win_amd64.whl", hash = "sha256:e7bfaf995c1bdbbd21d13539decb6650967013759318627d85daeb7881af16b7", size = 2798223, upload-time = "2026-04-27T14:43:28.51Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/2b/2be299bab55fc595e3d38567edb1a87f86e594842968fa9515a07bdcf422/tokenizers-0.23.1-cp310-abi3-win_arm64.whl", hash = "sha256:a26197957d8e4425dfba746315f3c425ea00cfa8367c5fbc4ec73447893dcea9", size = 2664127, upload-time = "2026-04-27T14:43:26.949Z" }, + { url = "https://files.pythonhosted.org/packages/87/39/b87a87d5bb9470610b80a2d31df42fcffeaf35118b8b97952b2aff598cc7/tokenizers-0.23.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e03d6ffcbe0d56ee9c1ccd070e70a13fa750727c0277e138152acbc0252c2224", size = 3146732 }, + { url = "https://files.pythonhosted.org/packages/e2/6a/068ed9f6e444c9d7e9d55ce134181325700f3d7f30410721bdc8f848d727/tokenizers-0.23.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0948bbb1ac1d7cdfc9fb6d62c596e3b7550036ad60ecd654a66ad273326324e", size = 3054954 }, + { url = "https://files.pythonhosted.org/packages/6c/36/e006edf031154cba92b8416057d92c3abe3635e4c4b0aa0b5b9bb39dde70/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bf13402aff9bc533c89cb849ec3b412dc3fbeacc9744840e423d7bf3f7dc0e3", size = 3374081 }, + { url = "https://files.pythonhosted.org/packages/a2/ef/7735d226f9c7f874a6bee5e3f27fb25ecabdf207d37b8cf45286d0795893/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f836ca703b89ae07919a309f9651f7a88fd5a33d5f718ba5ad0870ec0256bad6", size = 3247641 }, + { url = "https://files.pythonhosted.org/packages/b9/d9/24827036f6e21297bfffda0768e58eb6096a4f411e932964a01707857931/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae848657742035523fdf261773630cb819a26995fcd3d9ecae0c1daf6e5a4959", size = 3585624 }, + { url = "https://files.pythonhosted.org/packages/0c/9a/22f3582b3a4f49358293a5206e25317621ee4526bfe9cdaa0f07a12e770e/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53b09e85775d5187941e7bab30e941b4134ab4a7dd8c68e783d231fb7ca27c51", size = 3844062 }, + { url = "https://files.pythonhosted.org/packages/7e/65/b8f8814eef95800f20721384136d9a1d22241d50b2874357cb70542c392f/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea5a0ce170074329faaa8ea3f6400ecde604b6678192688533af80980daae71a", size = 3460098 }, + { url = "https://files.pythonhosted.org/packages/0d/d5/1353e5f677ec27c2494fb6a6725e82d56c985f53e90ec511369e7e4f02c6/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b405006415ea148a992d093699c66eb01952bf59f4d5727089a98bda45a4", size = 3346235 }, + { url = "https://files.pythonhosted.org/packages/71/89/39b6b8fc073fb6d413d0147aa333dc7eff7be65639ac9d19930a0b21bf33/tokenizers-0.23.1-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:56f3a77de629917652f876294dc9fe6bad4a0c43bc229dc72e59bb23a0f4729a", size = 3426398 }, + { url = "https://files.pythonhosted.org/packages/0f/80/127c854da64827e5b79264ce524993a90dddcb320e5cd42412c5c02f9e8a/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d10a6d957ef01896dc274e890eee27d41bd0e74ef31e60616f0fc311345184e", size = 9823279 }, + { url = "https://files.pythonhosted.org/packages/fe/ba/44c2502feb1a058f096ddfb4e0996ef3225a01a388e1a9b094e91689fe93/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1974288a609c343774f1b897c8b482c791ab17b75ab5c8c2b1737565c1d82288", size = 9644986 }, + { url = "https://files.pythonhosted.org/packages/9e/c1/464019a9fb059870bfe4eebb4ba12208f3042035e258bf5e782906bd3847/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:120468fb4c24faf0543c835a4fabafa4deb3f20a035c9b6e83d0b553a97615d4", size = 9976181 }, + { url = "https://files.pythonhosted.org/packages/79/94/3ac1432bda31626071e9b6a12709b97ae05131c804b94c8f3ac622c5da32/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e3d8f40ea6268047de7046906326abed5134f27d4e8447b23763afe5808c8a96", size = 10113853 }, + { url = "https://files.pythonhosted.org/packages/6a/dd/631b21433c771b1382535326f0eca80b9c9cee2e64961dd993bc9ac4669e/tokenizers-0.23.1-cp310-abi3-win32.whl", hash = "sha256:93120a930b919416da7cd10a2f606ac9919cc69cacae7980fa2140e277660948", size = 2536263 }, + { url = "https://files.pythonhosted.org/packages/97/c9/2553f72aaf65a2797d4229e37fa7fbe38ffbf3e32912d31bdd78b3323e59/tokenizers-0.23.1-cp310-abi3-win_amd64.whl", hash = "sha256:e7bfaf995c1bdbbd21d13539decb6650967013759318627d85daeb7881af16b7", size = 2798223 }, + { url = "https://files.pythonhosted.org/packages/cd/2b/2be299bab55fc595e3d38567edb1a87f86e594842968fa9515a07bdcf422/tokenizers-0.23.1-cp310-abi3-win_arm64.whl", hash = "sha256:a26197957d8e4425dfba746315f3c425ea00cfa8367c5fbc4ec73447893dcea9", size = 2664127 }, ] [[package]] name = "tokonomics" version = "1.2.18" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv", extra = ["httpx"] }, { name = "httpx" }, @@ -5078,42 +4864,42 @@ dependencies = [ { name = "pydantic" }, { name = "schemez" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/0a/c8093f6836f898a9f587aa30786bba25f08e92c9c11f13f9e638b0b97688/tokonomics-1.2.18.tar.gz", hash = "sha256:6b40461c8c8ce71f4bc378c61a4743a55b20b5ddee50d27b9398c85c29279c17", size = 65636, upload-time = "2026-02-25T21:23:44.719Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/0a/c8093f6836f898a9f587aa30786bba25f08e92c9c11f13f9e638b0b97688/tokonomics-1.2.18.tar.gz", hash = "sha256:6b40461c8c8ce71f4bc378c61a4743a55b20b5ddee50d27b9398c85c29279c17", size = 65636 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/c4/fc9b68acb9d10a44b2c7121c8e02094c46244f9e7b07fa86ef47ee2d1073/tokonomics-1.2.18-py3-none-any.whl", hash = "sha256:8ff42a59c16be309a0d8d29e66a079f0b1ed72dce4380a545d8f20c32e4c9ab8", size = 103907, upload-time = "2026-02-25T21:23:43.281Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c4/fc9b68acb9d10a44b2c7121c8e02094c46244f9e7b07fa86ef47ee2d1073/tokonomics-1.2.18-py3-none-any.whl", hash = "sha256:8ff42a59c16be309a0d8d29e66a079f0b1ed72dce4380a545d8f20c32e4c9ab8", size = 103907 }, ] [[package]] name = "tomli" version = "2.4.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866 }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887 }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704 }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628 }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180 }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674 }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976 }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755 }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265 }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583 }, ] [[package]] name = "tomli-w" version = "1.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675 }, ] [[package]] name = "toprompt" version = "1.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "clinspector" }, { name = "fieldz" }, @@ -5121,378 +4907,378 @@ dependencies = [ { name = "pydantic" }, { name = "sqlmodel" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/18/2a0be0481f39259e51781db49c5bb9b7fdb15883ae4794578f8668ecea5b/toprompt-1.0.0.tar.gz", hash = "sha256:9303c46a466c02152473d815e549ee507337b3b5c550fe1b5e5b65420f5464fa", size = 11336, upload-time = "2025-10-07T20:15:20.097Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/18/2a0be0481f39259e51781db49c5bb9b7fdb15883ae4794578f8668ecea5b/toprompt-1.0.0.tar.gz", hash = "sha256:9303c46a466c02152473d815e549ee507337b3b5c550fe1b5e5b65420f5464fa", size = 11336 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/9b/92fb91f937412b7fc7fd1121bafcfe247fe01c4117984c084227419a664f/toprompt-1.0.0-py3-none-any.whl", hash = "sha256:97edd8dfb941dc44374fd84959cd5f9dae9111e58202da714346bae3e18138f1", size = 12109, upload-time = "2025-10-07T20:15:19.077Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9b/92fb91f937412b7fc7fd1121bafcfe247fe01c4117984c084227419a664f/toprompt-1.0.0-py3-none-any.whl", hash = "sha256:97edd8dfb941dc44374fd84959cd5f9dae9111e58202da714346bae3e18138f1", size = 12109 }, ] [[package]] name = "tqdm" version = "4.69.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/84/da0e5038228fa34dfd77c5026b173ed035d2a3ba31f1077590c013de2bff/tqdm-4.69.1.tar.gz", hash = "sha256:2be21080a0ce17e902c2f1baeb6a74bf551b67bbdfa4bc0109fad471d0b4cb0d", size = 793046, upload-time = "2026-07-24T14:22:02.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/84/da0e5038228fa34dfd77c5026b173ed035d2a3ba31f1077590c013de2bff/tqdm-4.69.1.tar.gz", hash = "sha256:2be21080a0ce17e902c2f1baeb6a74bf551b67bbdfa4bc0109fad471d0b4cb0d", size = 793046 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/50/5817619a0fca56aff06383dbfde7ae017b3ca383915b3f1e4713164273cf/tqdm-4.69.1-py3-none-any.whl", hash = "sha256:0a654b96f7a2660cceb615b56f307ec2bef96c515409014a429a561981ab52b4", size = 675452, upload-time = "2026-07-24T14:22:00.048Z" }, + { url = "https://files.pythonhosted.org/packages/01/50/5817619a0fca56aff06383dbfde7ae017b3ca383915b3f1e4713164273cf/tqdm-4.69.1-py3-none-any.whl", hash = "sha256:0a654b96f7a2660cceb615b56f307ec2bef96c515409014a429a561981ab52b4", size = 675452 }, ] [[package]] name = "tree-sitter" version = "0.26.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/03/5600b84aff2e6c4fe80cfebb4063fe2f50299521befe5f6092ab8c082f4a/tree_sitter-0.26.0.tar.gz", hash = "sha256:b40c219edccc4564530c96f8f1556f6202b37cda964d1cbd7bd2b7e68b40a245", size = 191423, upload-time = "2026-06-30T12:14:27.933Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/03/5600b84aff2e6c4fe80cfebb4063fe2f50299521befe5f6092ab8c082f4a/tree_sitter-0.26.0.tar.gz", hash = "sha256:b40c219edccc4564530c96f8f1556f6202b37cda964d1cbd7bd2b7e68b40a245", size = 191423 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/b0/465257cf8f972ad9f9812ec1cbaa8ec210ebebb601ade9a15881aa2436b4/tree_sitter-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed0889dbed843ce45ede9f5169c0b2dea2222f12685844a03fadb81f12705867", size = 148893, upload-time = "2026-06-30T12:14:10.541Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ec/19d093e854b45e807fecfdd26105c266f43aeecc39c4dc97992a7074ad5a/tree_sitter-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6189c6c340c7384357711e3d92645e96bfb79f7a502f86de1ebdb23eb43f7dab", size = 140829, upload-time = "2026-06-30T12:14:11.626Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/ee/87e74671ed63a837e7a1f17ab94aa3913871e033b27523d8e7b83d6f7ad0/tree_sitter-0.26.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ff2e0750b7daa722302838356d7b65e303829b7eb73c915df127ddba115e1d1", size = 639334, upload-time = "2026-06-30T12:14:12.836Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/e7/f7e04cd9dff6b6ac0adf23922796fbc76accd4cf4bcda50542748d485679/tree_sitter-0.26.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7075ef857ef86f327dbb72d1e2574dda78db5754b3a1fca6506acd7fe5d561a7", size = 668102, upload-time = "2026-06-30T12:14:14.035Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/90/0bfb16b7894fea728c774a89d5af421a9368a2f913bbd4e8dcab7caaecfb/tree_sitter-0.26.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:26c996c1edfee86e977bb3f5462e74fcec0d0b0db1e85a3c475875763caa03be", size = 648560, upload-time = "2026-06-30T12:14:15.302Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/e6/0fe05ba396e9623b0ae40ccf34171336b8701ec8d7bd0ee9f5224d638665/tree_sitter-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00289bfe7978f3e0dc0ce69813a20fa9f44ea4c100b3ec62043e5eb74ccfc3a2", size = 665121, upload-time = "2026-06-30T12:14:16.403Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/d2/a944b1ca35bed6068dc84a9967aaf3049d8cc0b7a36179eea8787270a6ab/tree_sitter-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:93e220cab7e6a823efeb2046c49171427de92ef71c7c681c01820d14d8d3721f", size = 129615, upload-time = "2026-06-30T12:14:17.463Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/ef/c7ca48293580d2249f36940c4eed5b4ddeb9ce75baf9a4ef30621987e0c7/tree_sitter-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:b31a8195d2f224224c530ac814632d98c1dcc123d227442c07c736e86b70d564", size = 116525, upload-time = "2026-06-30T12:14:18.53Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b0/465257cf8f972ad9f9812ec1cbaa8ec210ebebb601ade9a15881aa2436b4/tree_sitter-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed0889dbed843ce45ede9f5169c0b2dea2222f12685844a03fadb81f12705867", size = 148893 }, + { url = "https://files.pythonhosted.org/packages/a1/ec/19d093e854b45e807fecfdd26105c266f43aeecc39c4dc97992a7074ad5a/tree_sitter-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6189c6c340c7384357711e3d92645e96bfb79f7a502f86de1ebdb23eb43f7dab", size = 140829 }, + { url = "https://files.pythonhosted.org/packages/9b/ee/87e74671ed63a837e7a1f17ab94aa3913871e033b27523d8e7b83d6f7ad0/tree_sitter-0.26.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ff2e0750b7daa722302838356d7b65e303829b7eb73c915df127ddba115e1d1", size = 639334 }, + { url = "https://files.pythonhosted.org/packages/66/e7/f7e04cd9dff6b6ac0adf23922796fbc76accd4cf4bcda50542748d485679/tree_sitter-0.26.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7075ef857ef86f327dbb72d1e2574dda78db5754b3a1fca6506acd7fe5d561a7", size = 668102 }, + { url = "https://files.pythonhosted.org/packages/d3/90/0bfb16b7894fea728c774a89d5af421a9368a2f913bbd4e8dcab7caaecfb/tree_sitter-0.26.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:26c996c1edfee86e977bb3f5462e74fcec0d0b0db1e85a3c475875763caa03be", size = 648560 }, + { url = "https://files.pythonhosted.org/packages/cd/e6/0fe05ba396e9623b0ae40ccf34171336b8701ec8d7bd0ee9f5224d638665/tree_sitter-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00289bfe7978f3e0dc0ce69813a20fa9f44ea4c100b3ec62043e5eb74ccfc3a2", size = 665121 }, + { url = "https://files.pythonhosted.org/packages/eb/d2/a944b1ca35bed6068dc84a9967aaf3049d8cc0b7a36179eea8787270a6ab/tree_sitter-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:93e220cab7e6a823efeb2046c49171427de92ef71c7c681c01820d14d8d3721f", size = 129615 }, + { url = "https://files.pythonhosted.org/packages/09/ef/c7ca48293580d2249f36940c4eed5b4ddeb9ce75baf9a4ef30621987e0c7/tree_sitter-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:b31a8195d2f224224c530ac814632d98c1dcc123d227442c07c736e86b70d564", size = 116525 }, ] [[package]] name = "tree-sitter-c" version = "0.24.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/c9/3834f3d9278251aea7312274971bc4c45b17aec2490fd4b884d93bd7019a/tree_sitter_c-0.24.2.tar.gz", hash = "sha256:1628584df0299b5a340aa63f8e67b6c97c91517f52fa7e7a4c557e40adb330a9", size = 228397, upload-time = "2026-04-22T08:06:14.491Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/c9/3834f3d9278251aea7312274971bc4c45b17aec2490fd4b884d93bd7019a/tree_sitter_c-0.24.2.tar.gz", hash = "sha256:1628584df0299b5a340aa63f8e67b6c97c91517f52fa7e7a4c557e40adb330a9", size = 228397 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/c1/26ed17730ec2c17bedc1b673349e5e0a466c578e3eb0327c3b73cf52bf97/tree_sitter_c-0.24.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:4d4579a8b54f0a442f903d88d3304cab77cd5c2031d4015baa4f2f8e15d6dcb7", size = 81016, upload-time = "2026-04-22T08:06:07.208Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/1c/1140db75e7e375cda3c68792a33826c4fd40b5b98c3259d93c75f6c8368f/tree_sitter_c-0.24.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:97bc80a224d48215d4e6e6376bf30d114f4c317b8145ff1b02afe785d4ba7bdd", size = 86213, upload-time = "2026-04-22T08:06:08.136Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/8c/0dfb88d726f8821d1c4c36042f092be974a800afd734307a595b8604190c/tree_sitter_c-0.24.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5041ef67eb68ce6bc8bb0b1f8ef3a5585ce523dae0c7eec109ab0627dd75aede", size = 94264, upload-time = "2026-04-22T08:06:08.918Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/78/47dc570e7aee6b0a1ecc2520b30639cc2b06003154c9ab0672d86bf720d5/tree_sitter_c-0.24.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c098bedcd5ac86ff93fa734d51d1dd86aed40fd5ed7d634c7af11380a0469969", size = 94560, upload-time = "2026-04-22T08:06:09.852Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/37/75d59d3f74f4cfc00f04472917e933d8a9c9fdc6eff980ef9552e010e6aa/tree_sitter_c-0.24.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:82842c5a5f2acd93f4de10038c33ac179c8979defc39376f990348d6289e933b", size = 94023, upload-time = "2026-04-22T08:06:10.682Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/57/8fc655d5a446a70a637e92b98bd2fdaab88bf5bb5b36076ac4add544808d/tree_sitter_c-0.24.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e2b42e8e22202c251f8629306f9321233542e07a6e01611b5fe83489272143eb", size = 94160, upload-time = "2026-04-22T08:06:11.497Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/f7/72a1d6b42dd31fd37e03ff67e7dc5ee572301499e6b216002b8dd42a1714/tree_sitter_c-0.24.2-cp310-abi3-win_amd64.whl", hash = "sha256:abb549225091f7b25df2dd3a0143ece6e208f7055d8bcb4700b41ee79b9ef1e1", size = 84669, upload-time = "2026-04-22T08:06:12.347Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/9d/7475d9ae8ef679aa36c7dfe6c903ab78e573651c68b6ef9862d6a3f994db/tree_sitter_c-0.24.2-cp310-abi3-win_arm64.whl", hash = "sha256:4a2f4371cd816cc3153458f69062135ebb2ea5f275ddd90494e5c823d778204a", size = 82956, upload-time = "2026-04-22T08:06:13.364Z" }, + { url = "https://files.pythonhosted.org/packages/28/c1/26ed17730ec2c17bedc1b673349e5e0a466c578e3eb0327c3b73cf52bf97/tree_sitter_c-0.24.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:4d4579a8b54f0a442f903d88d3304cab77cd5c2031d4015baa4f2f8e15d6dcb7", size = 81016 }, + { url = "https://files.pythonhosted.org/packages/c1/1c/1140db75e7e375cda3c68792a33826c4fd40b5b98c3259d93c75f6c8368f/tree_sitter_c-0.24.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:97bc80a224d48215d4e6e6376bf30d114f4c317b8145ff1b02afe785d4ba7bdd", size = 86213 }, + { url = "https://files.pythonhosted.org/packages/e9/8c/0dfb88d726f8821d1c4c36042f092be974a800afd734307a595b8604190c/tree_sitter_c-0.24.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5041ef67eb68ce6bc8bb0b1f8ef3a5585ce523dae0c7eec109ab0627dd75aede", size = 94264 }, + { url = "https://files.pythonhosted.org/packages/87/78/47dc570e7aee6b0a1ecc2520b30639cc2b06003154c9ab0672d86bf720d5/tree_sitter_c-0.24.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c098bedcd5ac86ff93fa734d51d1dd86aed40fd5ed7d634c7af11380a0469969", size = 94560 }, + { url = "https://files.pythonhosted.org/packages/29/37/75d59d3f74f4cfc00f04472917e933d8a9c9fdc6eff980ef9552e010e6aa/tree_sitter_c-0.24.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:82842c5a5f2acd93f4de10038c33ac179c8979defc39376f990348d6289e933b", size = 94023 }, + { url = "https://files.pythonhosted.org/packages/64/57/8fc655d5a446a70a637e92b98bd2fdaab88bf5bb5b36076ac4add544808d/tree_sitter_c-0.24.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e2b42e8e22202c251f8629306f9321233542e07a6e01611b5fe83489272143eb", size = 94160 }, + { url = "https://files.pythonhosted.org/packages/c1/f7/72a1d6b42dd31fd37e03ff67e7dc5ee572301499e6b216002b8dd42a1714/tree_sitter_c-0.24.2-cp310-abi3-win_amd64.whl", hash = "sha256:abb549225091f7b25df2dd3a0143ece6e208f7055d8bcb4700b41ee79b9ef1e1", size = 84669 }, + { url = "https://files.pythonhosted.org/packages/e2/9d/7475d9ae8ef679aa36c7dfe6c903ab78e573651c68b6ef9862d6a3f994db/tree_sitter_c-0.24.2-cp310-abi3-win_arm64.whl", hash = "sha256:4a2f4371cd816cc3153458f69062135ebb2ea5f275ddd90494e5c823d778204a", size = 82956 }, ] [[package]] name = "tree-sitter-cpp" version = "0.23.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/2c/4dd63d705a8933543cad9b92ff31be849b164fec91a6eb63475ebc9ce668/tree_sitter_cpp-0.23.4.tar.gz", hash = "sha256:6a59c4cebb1ad1dc2e8d586cf8a72b39d21b8108b7b139d089719e81a339e41d", size = 940358, upload-time = "2024-11-11T06:59:24.934Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/2c/4dd63d705a8933543cad9b92ff31be849b164fec91a6eb63475ebc9ce668/tree_sitter_cpp-0.23.4.tar.gz", hash = "sha256:6a59c4cebb1ad1dc2e8d586cf8a72b39d21b8108b7b139d089719e81a339e41d", size = 940358 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/ac/11d56670f7b048362db872ca866fd00ba2002a322ab179f047b7c0fb2910/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aacb1759f0efd9dbc25bd8ee88184a340483018869f75412d9c3bc32c039a520", size = 287861, upload-time = "2024-11-11T06:59:15.005Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/1c/0337c016bdc00a77a3326d12f10ee836401dd28f27db6fd5b7734bfb21ed/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc3c404d9f0cbd87951213a85440afbf4c31e718f8d907fa9ee12bea4b8d276f", size = 315513, upload-time = "2024-11-11T06:59:16.679Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/7b/dd38c049b10ed7fda118b903a1d28a8b55a36b98c30606ef90e8f374c6de/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc43ddf1279d5d5a4ef190373f4cb16522801bec4492bcd4754edf2aeba2b7b", size = 334813, upload-time = "2024-11-11T06:59:18.253Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/4d/23e390234d2acd351f5563b1079c515d7c1fe13ddb7392cee543be74dda3/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:773d2cafc08bbc0f998687fa33f42f378c1a371cdb582870c4d13abb06092706", size = 316110, upload-time = "2024-11-11T06:59:19.823Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/c7/b94a7e0e803af9d3bd4608fb4f0cfb2e9e233abaf0a38c928bfb0b1a025d/tree_sitter_cpp-0.23.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:247d127f0eb6574b0f6b30c0151e0bd0774e2e7acf9c558bdf9fbb8adc2e80c0", size = 308242, upload-time = "2024-11-11T06:59:21.466Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/7e/909e52b3dec09c475140b0e175511e275d0d00ba2dbd7c68102d377ae0f6/tree_sitter_cpp-0.23.4-cp39-abi3-win_amd64.whl", hash = "sha256:68606a45bea92669d155399e1239f771a7767d8683cd8f8e30e7d813107030ca", size = 290997, upload-time = "2024-11-11T06:59:22.432Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/6a/65435d4d1f4c735be7ffe52d7c2e7b8a7f7c2790343a2719c60c548611c8/tree_sitter_cpp-0.23.4-cp39-abi3-win_arm64.whl", hash = "sha256:712f84f18be94cbe2a148fa4fdf40fcf4a8c25a8f7670efb9f8a47ddec2fc281", size = 288203, upload-time = "2024-11-11T06:59:23.404Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ac/11d56670f7b048362db872ca866fd00ba2002a322ab179f047b7c0fb2910/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aacb1759f0efd9dbc25bd8ee88184a340483018869f75412d9c3bc32c039a520", size = 287861 }, + { url = "https://files.pythonhosted.org/packages/12/1c/0337c016bdc00a77a3326d12f10ee836401dd28f27db6fd5b7734bfb21ed/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc3c404d9f0cbd87951213a85440afbf4c31e718f8d907fa9ee12bea4b8d276f", size = 315513 }, + { url = "https://files.pythonhosted.org/packages/b3/7b/dd38c049b10ed7fda118b903a1d28a8b55a36b98c30606ef90e8f374c6de/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc43ddf1279d5d5a4ef190373f4cb16522801bec4492bcd4754edf2aeba2b7b", size = 334813 }, + { url = "https://files.pythonhosted.org/packages/6a/4d/23e390234d2acd351f5563b1079c515d7c1fe13ddb7392cee543be74dda3/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:773d2cafc08bbc0f998687fa33f42f378c1a371cdb582870c4d13abb06092706", size = 316110 }, + { url = "https://files.pythonhosted.org/packages/32/c7/b94a7e0e803af9d3bd4608fb4f0cfb2e9e233abaf0a38c928bfb0b1a025d/tree_sitter_cpp-0.23.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:247d127f0eb6574b0f6b30c0151e0bd0774e2e7acf9c558bdf9fbb8adc2e80c0", size = 308242 }, + { url = "https://files.pythonhosted.org/packages/37/7e/909e52b3dec09c475140b0e175511e275d0d00ba2dbd7c68102d377ae0f6/tree_sitter_cpp-0.23.4-cp39-abi3-win_amd64.whl", hash = "sha256:68606a45bea92669d155399e1239f771a7767d8683cd8f8e30e7d813107030ca", size = 290997 }, + { url = "https://files.pythonhosted.org/packages/d4/6a/65435d4d1f4c735be7ffe52d7c2e7b8a7f7c2790343a2719c60c548611c8/tree_sitter_cpp-0.23.4-cp39-abi3-win_arm64.whl", hash = "sha256:712f84f18be94cbe2a148fa4fdf40fcf4a8c25a8f7670efb9f8a47ddec2fc281", size = 288203 }, ] [[package]] name = "tree-sitter-go" version = "0.25.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/05/727308adbbc79bcb1c92fc0ea10556a735f9d0f0a5435a18f59d40f7fd77/tree_sitter_go-0.25.0.tar.gz", hash = "sha256:a7466e9b8d94dda94cae8d91629f26edb2d26166fd454d4831c3bf6dfa2e8d68", size = 93890, upload-time = "2025-08-29T06:20:25.044Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/05/727308adbbc79bcb1c92fc0ea10556a735f9d0f0a5435a18f59d40f7fd77/tree_sitter_go-0.25.0.tar.gz", hash = "sha256:a7466e9b8d94dda94cae8d91629f26edb2d26166fd454d4831c3bf6dfa2e8d68", size = 93890 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/aa/0984707acc2b9bb461fe4a41e7e0fc5b2b1e245c32820f0c83b3c602957c/tree_sitter_go-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b852993063a3429a443e7bd0aa376dd7dd329d595819fabf56ac4cf9d7257b54", size = 47117, upload-time = "2025-08-29T06:20:14.286Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/16/dd4cb124b35e99239ab3624225da07d4cb8da4d8564ed81d03fcb3a6ba9f/tree_sitter_go-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:503b81a2b4c31e302869a1de3a352ad0912ccab3df9ac9950197b0a9ceeabd8f", size = 48674, upload-time = "2025-08-29T06:20:17.557Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/fb/b30d63a08044115d8b8bd196c6c2ab4325fb8db5757249a4ef0563966e2e/tree_sitter_go-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04b3b3cb4aff18e74e28d49b716c6f24cb71ddfdd66768987e26e4d0fa812f74", size = 66418, upload-time = "2025-08-29T06:20:18.345Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/21/d3d88a30ad007419b2c97b3baeeef7431407faf9f686195b6f1cad0aedf9/tree_sitter_go-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:148255aca2f54b90d48c48a9dbb4c7faad6cad310a980b2c5a5a9822057ed145", size = 72006, upload-time = "2025-08-29T06:20:19.14Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/d0/0dd6442353ced8a88bbda9e546f4ea29e381b59b5a40b122e5abb586bb6c/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4d338116cdf8a6c6ff990d2441929b41323ef17c710407abe0993c13417d6aad", size = 70603, upload-time = "2025-08-29T06:20:21.544Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/e2/ee5e09f63504fc286539535d374d2eaa0e7d489b80f8f744bb3962aff22a/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5608e089d2a29fa8d2b327abeb2ad1cdb8e223c440a6b0ceab0d3fa80bdeebae", size = 66088, upload-time = "2025-08-29T06:20:22.336Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/b6/d9142583374720e79aca9ccb394b3795149a54c012e1dfd80738df2d984e/tree_sitter_go-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:30d4ada57a223dfc2c32d942f44d284d40f3d1215ddcf108f96807fd36d53022", size = 48152, upload-time = "2025-08-29T06:20:23.089Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/00/9a2638e7339236f5b01622952a4d71c1474dd3783d1982a89555fc1f03b1/tree_sitter_go-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:d5d62362059bf79997340773d47cc7e7e002883b527a05cca829c46e40b70ded", size = 46752, upload-time = "2025-08-29T06:20:24.235Z" }, + { url = "https://files.pythonhosted.org/packages/ca/aa/0984707acc2b9bb461fe4a41e7e0fc5b2b1e245c32820f0c83b3c602957c/tree_sitter_go-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b852993063a3429a443e7bd0aa376dd7dd329d595819fabf56ac4cf9d7257b54", size = 47117 }, + { url = "https://files.pythonhosted.org/packages/32/16/dd4cb124b35e99239ab3624225da07d4cb8da4d8564ed81d03fcb3a6ba9f/tree_sitter_go-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:503b81a2b4c31e302869a1de3a352ad0912ccab3df9ac9950197b0a9ceeabd8f", size = 48674 }, + { url = "https://files.pythonhosted.org/packages/86/fb/b30d63a08044115d8b8bd196c6c2ab4325fb8db5757249a4ef0563966e2e/tree_sitter_go-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04b3b3cb4aff18e74e28d49b716c6f24cb71ddfdd66768987e26e4d0fa812f74", size = 66418 }, + { url = "https://files.pythonhosted.org/packages/26/21/d3d88a30ad007419b2c97b3baeeef7431407faf9f686195b6f1cad0aedf9/tree_sitter_go-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:148255aca2f54b90d48c48a9dbb4c7faad6cad310a980b2c5a5a9822057ed145", size = 72006 }, + { url = "https://files.pythonhosted.org/packages/cd/d0/0dd6442353ced8a88bbda9e546f4ea29e381b59b5a40b122e5abb586bb6c/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4d338116cdf8a6c6ff990d2441929b41323ef17c710407abe0993c13417d6aad", size = 70603 }, + { url = "https://files.pythonhosted.org/packages/01/e2/ee5e09f63504fc286539535d374d2eaa0e7d489b80f8f744bb3962aff22a/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5608e089d2a29fa8d2b327abeb2ad1cdb8e223c440a6b0ceab0d3fa80bdeebae", size = 66088 }, + { url = "https://files.pythonhosted.org/packages/6e/b6/d9142583374720e79aca9ccb394b3795149a54c012e1dfd80738df2d984e/tree_sitter_go-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:30d4ada57a223dfc2c32d942f44d284d40f3d1215ddcf108f96807fd36d53022", size = 48152 }, + { url = "https://files.pythonhosted.org/packages/9e/00/9a2638e7339236f5b01622952a4d71c1474dd3783d1982a89555fc1f03b1/tree_sitter_go-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:d5d62362059bf79997340773d47cc7e7e002883b527a05cca829c46e40b70ded", size = 46752 }, ] [[package]] name = "tree-sitter-javascript" version = "0.25.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/e0/e63103c72a9d3dfd89a31e02e660263ad84b7438e5f44ee82e443e65bbde/tree_sitter_javascript-0.25.0.tar.gz", hash = "sha256:329b5414874f0588a98f1c291f1b28138286617aa907746ffe55adfdcf963f38", size = 132338, upload-time = "2025-09-01T07:13:44.792Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/e0/e63103c72a9d3dfd89a31e02e660263ad84b7438e5f44ee82e443e65bbde/tree_sitter_javascript-0.25.0.tar.gz", hash = "sha256:329b5414874f0588a98f1c291f1b28138286617aa907746ffe55adfdcf963f38", size = 132338 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/df/5106ac250cd03661ebc3cc75da6b3d9f6800a3606393a0122eca58038104/tree_sitter_javascript-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b70f887fb269d6e58c349d683f59fa647140c410cfe2bee44a883b20ec92e3dc", size = 64052, upload-time = "2025-09-01T07:13:36.865Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/8f/6b4b2bc90d8ab3955856ce852cc9d1e82c81d7ab9646385f0e75ffd5b5d3/tree_sitter_javascript-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:8264a996b8845cfce06965152a013b5d9cbb7d199bc3503e12b5682e62bb1de1", size = 66440, upload-time = "2025-09-01T07:13:37.962Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/c4/7da74ecdcd8a398f88bd003a87c65403b5fe0e958cdd43fbd5fd4a398fcf/tree_sitter_javascript-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9dc04ba91fc8583344e57c1f1ed5b2c97ecaaf47480011b92fbeab8dda96db75", size = 99728, upload-time = "2025-09-01T07:13:38.755Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/c8/97da3af4796495e46421e9344738addb3602fa6426ea695be3fcbadbee37/tree_sitter_javascript-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:199d09985190852e0912da2b8d26c932159be314bc04952cf917ed0e4c633e6b", size = 106072, upload-time = "2025-09-01T07:13:39.798Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/be/c964e8130be08cc9bd6627d845f0e4460945b158429d39510953bbcb8fcc/tree_sitter_javascript-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dfcf789064c58dc13c0a4edb550acacfc6f0f280577f1e7a00de3e89fc7f8ddc", size = 104388, upload-time = "2025-09-01T07:13:40.866Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/89/9b773dee0f8961d1bb8d7baf0a204ab587618df19897c1ef260916f318ec/tree_sitter_javascript-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b852d3aee8a36186dbcc32c798b11b4869f9b5041743b63b65c2ef793db7a54", size = 98377, upload-time = "2025-09-01T07:13:41.838Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/dc/d90cb1790f8cec9b4878d278ad9faf7c8f893189ce0f855304fd704fc274/tree_sitter_javascript-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:e5ed840f5bd4a3f0272e441d19429b26eedc257abe5574c8546da6b556865e3c", size = 62975, upload-time = "2025-09-01T07:13:42.828Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/1f/f9eba1038b7d4394410f3c0a6ec2122b590cd7acb03f196e52fa57ebbe72/tree_sitter_javascript-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:622a69d677aa7f6ee2931d8c77c981a33f0ebb6d275aa9d43d3397c879a9bb0b", size = 61668, upload-time = "2025-09-01T07:13:43.803Z" }, + { url = "https://files.pythonhosted.org/packages/2c/df/5106ac250cd03661ebc3cc75da6b3d9f6800a3606393a0122eca58038104/tree_sitter_javascript-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b70f887fb269d6e58c349d683f59fa647140c410cfe2bee44a883b20ec92e3dc", size = 64052 }, + { url = "https://files.pythonhosted.org/packages/b1/8f/6b4b2bc90d8ab3955856ce852cc9d1e82c81d7ab9646385f0e75ffd5b5d3/tree_sitter_javascript-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:8264a996b8845cfce06965152a013b5d9cbb7d199bc3503e12b5682e62bb1de1", size = 66440 }, + { url = "https://files.pythonhosted.org/packages/5f/c4/7da74ecdcd8a398f88bd003a87c65403b5fe0e958cdd43fbd5fd4a398fcf/tree_sitter_javascript-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9dc04ba91fc8583344e57c1f1ed5b2c97ecaaf47480011b92fbeab8dda96db75", size = 99728 }, + { url = "https://files.pythonhosted.org/packages/96/c8/97da3af4796495e46421e9344738addb3602fa6426ea695be3fcbadbee37/tree_sitter_javascript-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:199d09985190852e0912da2b8d26c932159be314bc04952cf917ed0e4c633e6b", size = 106072 }, + { url = "https://files.pythonhosted.org/packages/13/be/c964e8130be08cc9bd6627d845f0e4460945b158429d39510953bbcb8fcc/tree_sitter_javascript-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dfcf789064c58dc13c0a4edb550acacfc6f0f280577f1e7a00de3e89fc7f8ddc", size = 104388 }, + { url = "https://files.pythonhosted.org/packages/ee/89/9b773dee0f8961d1bb8d7baf0a204ab587618df19897c1ef260916f318ec/tree_sitter_javascript-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b852d3aee8a36186dbcc32c798b11b4869f9b5041743b63b65c2ef793db7a54", size = 98377 }, + { url = "https://files.pythonhosted.org/packages/3b/dc/d90cb1790f8cec9b4878d278ad9faf7c8f893189ce0f855304fd704fc274/tree_sitter_javascript-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:e5ed840f5bd4a3f0272e441d19429b26eedc257abe5574c8546da6b556865e3c", size = 62975 }, + { url = "https://files.pythonhosted.org/packages/2e/1f/f9eba1038b7d4394410f3c0a6ec2122b590cd7acb03f196e52fa57ebbe72/tree_sitter_javascript-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:622a69d677aa7f6ee2931d8c77c981a33f0ebb6d275aa9d43d3397c879a9bb0b", size = 61668 }, ] [[package]] name = "tree-sitter-json" version = "0.24.8" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/29/e92df6dca3a6b2ab1c179978be398059817e1173fbacd47e832aaff3446b/tree_sitter_json-0.24.8.tar.gz", hash = "sha256:ca8486e52e2d261819311d35cf98656123d59008c3b7dcf91e61d2c0c6f3120e", size = 8155, upload-time = "2024-11-11T06:05:00.667Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/29/e92df6dca3a6b2ab1c179978be398059817e1173fbacd47e832aaff3446b/tree_sitter_json-0.24.8.tar.gz", hash = "sha256:ca8486e52e2d261819311d35cf98656123d59008c3b7dcf91e61d2c0c6f3120e", size = 8155 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/41/84866232980fb3cf0cff46f5af2dbb9bfa3324b32614c6a9af3d08926b72/tree_sitter_json-0.24.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:59ac06c6db1877d0e2076bce54a5fddcdd2fc38ca778905662e80fa9ffcea2ab", size = 8718, upload-time = "2024-11-11T06:04:49.779Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/31/102c15948d97b135611d6a995c97a3933c0e9745f25737723977f58e142c/tree_sitter_json-0.24.8-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:62b4c45b561db31436a81a3f037f71ec29049f4fc9bf5269b6ec3ebaaa35a1cd", size = 9163, upload-time = "2024-11-11T06:04:51.275Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/64/aa44ea2f3d2e76ec086ce83902eb26b2ed0a92d3fd5e2714c9cb007e90d1/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8627f7d375fda9fc193ebee368c453f374f65c2f25c58b6fea4e6b49a7fccbc", size = 17726, upload-time = "2024-11-11T06:04:52.732Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/08/10001992526670e0d6f24c571b179f0ece90e5e014a4b98a3ce076884f32/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85cca779872f7278f3a74eb38533d34b9c4de4fd548615e3361fa64fe350ad0a", size = 17236, upload-time = "2024-11-11T06:04:54.189Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/64/908e9e0bd84fe3c81c564115d3bbe0e49b0e152784bbaf153d749d00bbe6/tree_sitter_json-0.24.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:deeb45850dcc52990fbb52c80196492a099e3fa3512d928a390a91cf061068cc", size = 16071, upload-time = "2024-11-11T06:04:55.628Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/df/31daab1eedb445bef208a04fc35428de3afe2b37075fec84d7737e1c69de/tree_sitter_json-0.24.8-cp39-abi3-win_amd64.whl", hash = "sha256:e4849a03cd7197267b2688a4506a90a13568a8e0e8588080bd0212fcb38974e3", size = 11457, upload-time = "2024-11-11T06:04:57.698Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/3d/902d2f3125b6b90cebf404b63ca775bc6d82071ccc76c0d10fabfeb2febe/tree_sitter_json-0.24.8-cp39-abi3-win_arm64.whl", hash = "sha256:591e0096c882d12668b88f30d3ca6f85b9db3406910eaaab6afb6b17d65367dd", size = 10174, upload-time = "2024-11-11T06:04:59.309Z" }, + { url = "https://files.pythonhosted.org/packages/42/41/84866232980fb3cf0cff46f5af2dbb9bfa3324b32614c6a9af3d08926b72/tree_sitter_json-0.24.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:59ac06c6db1877d0e2076bce54a5fddcdd2fc38ca778905662e80fa9ffcea2ab", size = 8718 }, + { url = "https://files.pythonhosted.org/packages/5c/31/102c15948d97b135611d6a995c97a3933c0e9745f25737723977f58e142c/tree_sitter_json-0.24.8-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:62b4c45b561db31436a81a3f037f71ec29049f4fc9bf5269b6ec3ebaaa35a1cd", size = 9163 }, + { url = "https://files.pythonhosted.org/packages/28/64/aa44ea2f3d2e76ec086ce83902eb26b2ed0a92d3fd5e2714c9cb007e90d1/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8627f7d375fda9fc193ebee368c453f374f65c2f25c58b6fea4e6b49a7fccbc", size = 17726 }, + { url = "https://files.pythonhosted.org/packages/77/08/10001992526670e0d6f24c571b179f0ece90e5e014a4b98a3ce076884f32/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85cca779872f7278f3a74eb38533d34b9c4de4fd548615e3361fa64fe350ad0a", size = 17236 }, + { url = "https://files.pythonhosted.org/packages/92/64/908e9e0bd84fe3c81c564115d3bbe0e49b0e152784bbaf153d749d00bbe6/tree_sitter_json-0.24.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:deeb45850dcc52990fbb52c80196492a099e3fa3512d928a390a91cf061068cc", size = 16071 }, + { url = "https://files.pythonhosted.org/packages/53/df/31daab1eedb445bef208a04fc35428de3afe2b37075fec84d7737e1c69de/tree_sitter_json-0.24.8-cp39-abi3-win_amd64.whl", hash = "sha256:e4849a03cd7197267b2688a4506a90a13568a8e0e8588080bd0212fcb38974e3", size = 11457 }, + { url = "https://files.pythonhosted.org/packages/6c/3d/902d2f3125b6b90cebf404b63ca775bc6d82071ccc76c0d10fabfeb2febe/tree_sitter_json-0.24.8-cp39-abi3-win_arm64.whl", hash = "sha256:591e0096c882d12668b88f30d3ca6f85b9db3406910eaaab6afb6b17d65367dd", size = 10174 }, ] [[package]] name = "tree-sitter-language-pack" version = "1.13.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tree-sitter" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/9e/01be7a94fc69204350dc99707730aa4f32906cdb8b0b5856ff43fd0c60d5/tree_sitter_language_pack-1.13.3.tar.gz", hash = "sha256:e0e0287bf5a2f3d8a382136b1565206988417ece502b796f4b6f7fefb832963c", size = 82100, upload-time = "2026-07-21T14:10:20.469Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/9e/01be7a94fc69204350dc99707730aa4f32906cdb8b0b5856ff43fd0c60d5/tree_sitter_language_pack-1.13.3.tar.gz", hash = "sha256:e0e0287bf5a2f3d8a382136b1565206988417ece502b796f4b6f7fefb832963c", size = 82100 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/a0/69ac382a8e610a2e521fd1c661a59127b5387fad8789355c9fa60b27765a/tree_sitter_language_pack-1.13.3-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9229a14185583a0acfd9946154d491908078cc7a54150bf03268aed5a67cf3c3", size = 2141847, upload-time = "2026-07-21T14:10:11.626Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/57/24ea7493d5704fcce545707732c652d7fd1cbe82c6f824cdc26554124069/tree_sitter_language_pack-1.13.3-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:8435aabc1e5537e97e0a5893b6bf8d501ee7e55f93ee813e98711c5787fdbf78", size = 2033141, upload-time = "2026-07-21T14:10:13.131Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/02/b476c812681922ac2763100aeb98040b880e6fae9bf5960068fb5652356d/tree_sitter_language_pack-1.13.3-cp310-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3096595f83c3ad1c8cdace1db737e074f4ef3c798534deb2fe758acda0d7e211", size = 2190490, upload-time = "2026-07-21T14:10:14.66Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/f8/bfb863ffd53eb888308ffebeb9f73ed24bda0eac10393b9ec1b6774abe06/tree_sitter_language_pack-1.13.3-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ebcd8fa9435ff956bd82eeea21f492fb2ccfefd9be909fe5cdebabb892c4b034", size = 2301379, upload-time = "2026-07-21T14:10:16.147Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/32/5a395972eee620b763493f6abf527dd76dea0cef91010ab348137cb51a2d/tree_sitter_language_pack-1.13.3-cp310-abi3-win_amd64.whl", hash = "sha256:c67c322370c30fa802e01532933709d0c29de8337d911eb693692a996993e55d", size = 2076066, upload-time = "2026-07-21T14:10:17.614Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/56/b2cbaaaadbcadc90355f3bc7ecf9bb9f842ffd328ee9fb6b33518c93c360/tree_sitter_language_pack-1.13.3-cp310-abi3-win_arm64.whl", hash = "sha256:49eb4d6f532556076a885b36aab51919ae6ab396401144e4f73e4a0c9ead0219", size = 1987435, upload-time = "2026-07-21T14:10:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/75/a0/69ac382a8e610a2e521fd1c661a59127b5387fad8789355c9fa60b27765a/tree_sitter_language_pack-1.13.3-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9229a14185583a0acfd9946154d491908078cc7a54150bf03268aed5a67cf3c3", size = 2141847 }, + { url = "https://files.pythonhosted.org/packages/bb/57/24ea7493d5704fcce545707732c652d7fd1cbe82c6f824cdc26554124069/tree_sitter_language_pack-1.13.3-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:8435aabc1e5537e97e0a5893b6bf8d501ee7e55f93ee813e98711c5787fdbf78", size = 2033141 }, + { url = "https://files.pythonhosted.org/packages/4e/02/b476c812681922ac2763100aeb98040b880e6fae9bf5960068fb5652356d/tree_sitter_language_pack-1.13.3-cp310-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3096595f83c3ad1c8cdace1db737e074f4ef3c798534deb2fe758acda0d7e211", size = 2190490 }, + { url = "https://files.pythonhosted.org/packages/6e/f8/bfb863ffd53eb888308ffebeb9f73ed24bda0eac10393b9ec1b6774abe06/tree_sitter_language_pack-1.13.3-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ebcd8fa9435ff956bd82eeea21f492fb2ccfefd9be909fe5cdebabb892c4b034", size = 2301379 }, + { url = "https://files.pythonhosted.org/packages/5b/32/5a395972eee620b763493f6abf527dd76dea0cef91010ab348137cb51a2d/tree_sitter_language_pack-1.13.3-cp310-abi3-win_amd64.whl", hash = "sha256:c67c322370c30fa802e01532933709d0c29de8337d911eb693692a996993e55d", size = 2076066 }, + { url = "https://files.pythonhosted.org/packages/ff/56/b2cbaaaadbcadc90355f3bc7ecf9bb9f842ffd328ee9fb6b33518c93c360/tree_sitter_language_pack-1.13.3-cp310-abi3-win_arm64.whl", hash = "sha256:49eb4d6f532556076a885b36aab51919ae6ab396401144e4f73e4a0c9ead0219", size = 1987435 }, ] [[package]] name = "tree-sitter-python" version = "0.25.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/8b/c992ff0e768cb6768d5c96234579bf8842b3a633db641455d86dd30d5dac/tree_sitter_python-0.25.0.tar.gz", hash = "sha256:b13e090f725f5b9c86aa455a268553c65cadf325471ad5b65cd29cac8a1a68ac", size = 159845, upload-time = "2025-09-11T06:47:58.159Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/8b/c992ff0e768cb6768d5c96234579bf8842b3a633db641455d86dd30d5dac/tree_sitter_python-0.25.0.tar.gz", hash = "sha256:b13e090f725f5b9c86aa455a268553c65cadf325471ad5b65cd29cac8a1a68ac", size = 159845 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/64/a4e503c78a4eb3ac46d8e72a29c1b1237fa85238d8e972b063e0751f5a94/tree_sitter_python-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:14a79a47ddef72f987d5a2c122d148a812169d7484ff5c75a3db9609d419f361", size = 73790, upload-time = "2025-09-11T06:47:47.652Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/1d/60d8c2a0cc63d6ec4ba4e99ce61b802d2e39ef9db799bdf2a8f932a6cd4b/tree_sitter_python-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:480c21dbd995b7fe44813e741d71fed10ba695e7caab627fb034e3828469d762", size = 76691, upload-time = "2025-09-11T06:47:49.038Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/cb/d9b0b67d037922d60cbe0359e0c86457c2da721bc714381a63e2c8e35eba/tree_sitter_python-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:86f118e5eecad616ecdb81d171a36dde9bef5a0b21ed71ea9c3e390813c3baf5", size = 108133, upload-time = "2025-09-11T06:47:50.499Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/bd/bf4787f57e6b2860f3f1c8c62f045b39fb32d6bac4b53d7a9e66de968440/tree_sitter_python-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be71650ca2b93b6e9649e5d65c6811aad87a7614c8c1003246b303f6b150f61b", size = 110603, upload-time = "2025-09-11T06:47:51.985Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/25/feff09f5c2f32484fbce15db8b49455c7572346ce61a699a41972dea7318/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6d5b5799628cc0f24691ab2a172a8e676f668fe90dc60468bee14084a35c16d", size = 108998, upload-time = "2025-09-11T06:47:53.046Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/69/4946da3d6c0df316ccb938316ce007fb565d08f89d02d854f2d308f0309f/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:71959832fc5d9642e52c11f2f7d79ae520b461e63334927e93ca46cd61cd9683", size = 107268, upload-time = "2025-09-11T06:47:54.388Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/a2/996fc2dfa1076dc460d3e2f3c75974ea4b8f02f6bc925383aaae519920e8/tree_sitter_python-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:9bcde33f18792de54ee579b00e1b4fe186b7926825444766f849bf7181793a76", size = 76073, upload-time = "2025-09-11T06:47:55.773Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/19/4b5569d9b1ebebb5907d11554a96ef3fa09364a30fcfabeff587495b512f/tree_sitter_python-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:0fbf6a3774ad7e89ee891851204c2e2c47e12b63a5edbe2e9156997731c128bb", size = 74169, upload-time = "2025-09-11T06:47:56.747Z" }, + { url = "https://files.pythonhosted.org/packages/cf/64/a4e503c78a4eb3ac46d8e72a29c1b1237fa85238d8e972b063e0751f5a94/tree_sitter_python-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:14a79a47ddef72f987d5a2c122d148a812169d7484ff5c75a3db9609d419f361", size = 73790 }, + { url = "https://files.pythonhosted.org/packages/e6/1d/60d8c2a0cc63d6ec4ba4e99ce61b802d2e39ef9db799bdf2a8f932a6cd4b/tree_sitter_python-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:480c21dbd995b7fe44813e741d71fed10ba695e7caab627fb034e3828469d762", size = 76691 }, + { url = "https://files.pythonhosted.org/packages/aa/cb/d9b0b67d037922d60cbe0359e0c86457c2da721bc714381a63e2c8e35eba/tree_sitter_python-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:86f118e5eecad616ecdb81d171a36dde9bef5a0b21ed71ea9c3e390813c3baf5", size = 108133 }, + { url = "https://files.pythonhosted.org/packages/40/bd/bf4787f57e6b2860f3f1c8c62f045b39fb32d6bac4b53d7a9e66de968440/tree_sitter_python-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be71650ca2b93b6e9649e5d65c6811aad87a7614c8c1003246b303f6b150f61b", size = 110603 }, + { url = "https://files.pythonhosted.org/packages/5d/25/feff09f5c2f32484fbce15db8b49455c7572346ce61a699a41972dea7318/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6d5b5799628cc0f24691ab2a172a8e676f668fe90dc60468bee14084a35c16d", size = 108998 }, + { url = "https://files.pythonhosted.org/packages/75/69/4946da3d6c0df316ccb938316ce007fb565d08f89d02d854f2d308f0309f/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:71959832fc5d9642e52c11f2f7d79ae520b461e63334927e93ca46cd61cd9683", size = 107268 }, + { url = "https://files.pythonhosted.org/packages/ed/a2/996fc2dfa1076dc460d3e2f3c75974ea4b8f02f6bc925383aaae519920e8/tree_sitter_python-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:9bcde33f18792de54ee579b00e1b4fe186b7926825444766f849bf7181793a76", size = 76073 }, + { url = "https://files.pythonhosted.org/packages/07/19/4b5569d9b1ebebb5907d11554a96ef3fa09364a30fcfabeff587495b512f/tree_sitter_python-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:0fbf6a3774ad7e89ee891851204c2e2c47e12b63a5edbe2e9156997731c128bb", size = 74169 }, ] [[package]] name = "tree-sitter-rust" version = "0.24.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/87/75cbd22b927267d310f76cca1ab3c1d9d41035dfa3eb9cc95f96ee199440/tree_sitter_rust-0.24.2.tar.gz", hash = "sha256:54fb02a5911e345308b405174465112479f56dc39e3f1e7744d7568595f00db9", size = 339341, upload-time = "2026-03-27T21:08:55.629Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/87/75cbd22b927267d310f76cca1ab3c1d9d41035dfa3eb9cc95f96ee199440/tree_sitter_rust-0.24.2.tar.gz", hash = "sha256:54fb02a5911e345308b405174465112479f56dc39e3f1e7744d7568595f00db9", size = 339341 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/24/2b2d33af5e27c84a4fde4e8cd2594bb4ab1e1cf48756a9f40dadc84956cc/tree_sitter_rust-0.24.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3620cfd12340efa43082d45df76349ff511893a9c361da2f8d6d51e307020a59", size = 129507, upload-time = "2026-03-27T21:08:47.585Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/2a/cf39f881a545360b5a86bb1accba1f4acc713daab01fb9edd35b6e84f473/tree_sitter_rust-0.24.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:01a46622735498493f29f3e628a90de95c96a07bfbeb88996243eb986b1cee36", size = 136812, upload-time = "2026-03-27T21:08:48.761Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/45/a051bbd3045a61182dde25b93ae9a33d2677c935b16952283e12eaf46051/tree_sitter_rust-0.24.2-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e033c5a93b57c88e0a835880de39fc802909ff69f57aaff6000211c196ea5190", size = 164706, upload-time = "2026-03-27T21:08:49.605Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/f6/a5a146df5c0a5daea3ffcd5d7245775fe7f084357770d5a313dd6245ae78/tree_sitter_rust-0.24.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d76d1208c3638b871236090759dfc13d478921320653a6c9da5336e7c58f65a", size = 170310, upload-time = "2026-03-27T21:08:50.424Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/a8/f85b1ca75e01361ca5f92d226593ca4857cea49551b9f6c8fa6fc08ea917/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87930163a462408c49ab62c667e74029bc26b4cc7123dd1bdc7352215786c64a", size = 168668, upload-time = "2026-03-27T21:08:51.404Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/e1/3519f866a4679ca36acd9f5a06a779ecb8a92b18887c5546458d521df557/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da2b86099028fd42c6cd32878b7b16b01f8aac0f7b0e98742b7fa6bc3cf09b89", size = 162403, upload-time = "2026-03-27T21:08:52.588Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/71/7ef609894dbfe5699eb16f7471f9b8af1d958d8ba3e29c238d7607e8cb47/tree_sitter_rust-0.24.2-cp39-abi3-win_amd64.whl", hash = "sha256:4529c125d928882ddfb879fdc6bc0704913261ecc078b6fa7902559e0daf200d", size = 129422, upload-time = "2026-03-27T21:08:54.031Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/d8/050a781172745bc345f98abb7c56e72022ea0790f8e793de981c83c2ef15/tree_sitter_rust-0.24.2-cp39-abi3-win_arm64.whl", hash = "sha256:66ba90f61bd54f4c4f5d30434957daf64507c16b0313df76becb37d63f70a227", size = 128245, upload-time = "2026-03-27T21:08:54.803Z" }, + { url = "https://files.pythonhosted.org/packages/d0/24/2b2d33af5e27c84a4fde4e8cd2594bb4ab1e1cf48756a9f40dadc84956cc/tree_sitter_rust-0.24.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3620cfd12340efa43082d45df76349ff511893a9c361da2f8d6d51e307020a59", size = 129507 }, + { url = "https://files.pythonhosted.org/packages/78/2a/cf39f881a545360b5a86bb1accba1f4acc713daab01fb9edd35b6e84f473/tree_sitter_rust-0.24.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:01a46622735498493f29f3e628a90de95c96a07bfbeb88996243eb986b1cee36", size = 136812 }, + { url = "https://files.pythonhosted.org/packages/ca/45/a051bbd3045a61182dde25b93ae9a33d2677c935b16952283e12eaf46051/tree_sitter_rust-0.24.2-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e033c5a93b57c88e0a835880de39fc802909ff69f57aaff6000211c196ea5190", size = 164706 }, + { url = "https://files.pythonhosted.org/packages/b5/f6/a5a146df5c0a5daea3ffcd5d7245775fe7f084357770d5a313dd6245ae78/tree_sitter_rust-0.24.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d76d1208c3638b871236090759dfc13d478921320653a6c9da5336e7c58f65a", size = 170310 }, + { url = "https://files.pythonhosted.org/packages/95/a8/f85b1ca75e01361ca5f92d226593ca4857cea49551b9f6c8fa6fc08ea917/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87930163a462408c49ab62c667e74029bc26b4cc7123dd1bdc7352215786c64a", size = 168668 }, + { url = "https://files.pythonhosted.org/packages/a2/e1/3519f866a4679ca36acd9f5a06a779ecb8a92b18887c5546458d521df557/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da2b86099028fd42c6cd32878b7b16b01f8aac0f7b0e98742b7fa6bc3cf09b89", size = 162403 }, + { url = "https://files.pythonhosted.org/packages/34/71/7ef609894dbfe5699eb16f7471f9b8af1d958d8ba3e29c238d7607e8cb47/tree_sitter_rust-0.24.2-cp39-abi3-win_amd64.whl", hash = "sha256:4529c125d928882ddfb879fdc6bc0704913261ecc078b6fa7902559e0daf200d", size = 129422 }, + { url = "https://files.pythonhosted.org/packages/b9/d8/050a781172745bc345f98abb7c56e72022ea0790f8e793de981c83c2ef15/tree_sitter_rust-0.24.2-cp39-abi3-win_arm64.whl", hash = "sha256:66ba90f61bd54f4c4f5d30434957daf64507c16b0313df76becb37d63f70a227", size = 128245 }, ] [[package]] name = "tree-sitter-typescript" version = "0.23.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/fc/bb52958f7e399250aee093751e9373a6311cadbe76b6e0d109b853757f35/tree_sitter_typescript-0.23.2.tar.gz", hash = "sha256:7b167b5827c882261cb7a50dfa0fb567975f9b315e87ed87ad0a0a3aedb3834d", size = 773053, upload-time = "2024-11-11T02:36:11.396Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/fc/bb52958f7e399250aee093751e9373a6311cadbe76b6e0d109b853757f35/tree_sitter_typescript-0.23.2.tar.gz", hash = "sha256:7b167b5827c882261cb7a50dfa0fb567975f9b315e87ed87ad0a0a3aedb3834d", size = 773053 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/95/4c00680866280e008e81dd621fd4d3f54aa3dad1b76b857a19da1b2cc426/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478", size = 286677, upload-time = "2024-11-11T02:35:58.839Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/2f/1f36fda564518d84593f2740d5905ac127d590baf5c5753cef2a88a89c15/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8", size = 302008, upload-time = "2024-11-11T02:36:00.733Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/2d/975c2dad292aa9994f982eb0b69cc6fda0223e4b6c4ea714550477d8ec3a/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b1eed5b0b3a8134e86126b00b743d667ec27c63fc9de1b7bb23168803879e31", size = 351987, upload-time = "2024-11-11T02:36:02.669Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/d1/a71c36da6e2b8a4ed5e2970819b86ef13ba77ac40d9e333cb17df6a2c5db/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e96d36b85bcacdeb8ff5c2618d75593ef12ebaf1b4eace3477e2bdb2abb1752c", size = 344960, upload-time = "2024-11-11T02:36:04.443Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/cb/f57b149d7beed1a85b8266d0c60ebe4c46e79c9ba56bc17b898e17daf88e/tree_sitter_typescript-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8d4f0f9bcb61ad7b7509d49a1565ff2cc363863644a234e1e0fe10960e55aea0", size = 340245, upload-time = "2024-11-11T02:36:06.473Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/ab/dd84f0e2337296a5f09749f7b5483215d75c8fa9e33738522e5ed81f7254/tree_sitter_typescript-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:3f730b66396bc3e11811e4465c41ee45d9e9edd6de355a58bbbc49fa770da8f9", size = 278015, upload-time = "2024-11-11T02:36:07.631Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/e4/81f9a935789233cf412a0ed5fe04c883841d2c8fb0b7e075958a35c65032/tree_sitter_typescript-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:05db58f70b95ef0ea126db5560f3775692f609589ed6f8dd0af84b7f19f1cbb7", size = 274052, upload-time = "2024-11-11T02:36:09.514Z" }, + { url = "https://files.pythonhosted.org/packages/28/95/4c00680866280e008e81dd621fd4d3f54aa3dad1b76b857a19da1b2cc426/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478", size = 286677 }, + { url = "https://files.pythonhosted.org/packages/8f/2f/1f36fda564518d84593f2740d5905ac127d590baf5c5753cef2a88a89c15/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8", size = 302008 }, + { url = "https://files.pythonhosted.org/packages/96/2d/975c2dad292aa9994f982eb0b69cc6fda0223e4b6c4ea714550477d8ec3a/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b1eed5b0b3a8134e86126b00b743d667ec27c63fc9de1b7bb23168803879e31", size = 351987 }, + { url = "https://files.pythonhosted.org/packages/49/d1/a71c36da6e2b8a4ed5e2970819b86ef13ba77ac40d9e333cb17df6a2c5db/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e96d36b85bcacdeb8ff5c2618d75593ef12ebaf1b4eace3477e2bdb2abb1752c", size = 344960 }, + { url = "https://files.pythonhosted.org/packages/7f/cb/f57b149d7beed1a85b8266d0c60ebe4c46e79c9ba56bc17b898e17daf88e/tree_sitter_typescript-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8d4f0f9bcb61ad7b7509d49a1565ff2cc363863644a234e1e0fe10960e55aea0", size = 340245 }, + { url = "https://files.pythonhosted.org/packages/8b/ab/dd84f0e2337296a5f09749f7b5483215d75c8fa9e33738522e5ed81f7254/tree_sitter_typescript-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:3f730b66396bc3e11811e4465c41ee45d9e9edd6de355a58bbbc49fa770da8f9", size = 278015 }, + { url = "https://files.pythonhosted.org/packages/9f/e4/81f9a935789233cf412a0ed5fe04c883841d2c8fb0b7e075958a35c65032/tree_sitter_typescript-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:05db58f70b95ef0ea126db5560f3775692f609589ed6f8dd0af84b7f19f1cbb7", size = 274052 }, ] [[package]] name = "tree-sitter-yaml" version = "0.7.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882, upload-time = "2025-10-07T14:40:36.071Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898, upload-time = "2025-10-07T14:40:29.486Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691, upload-time = "2025-10-07T14:40:30.369Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430, upload-time = "2025-10-07T14:40:31.16Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428, upload-time = "2025-10-07T14:40:31.954Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580, upload-time = "2025-10-07T14:40:33.037Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455, upload-time = "2025-10-07T14:40:33.778Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514, upload-time = "2025-10-07T14:40:34.562Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065, upload-time = "2025-10-07T14:40:35.337Z" }, + { url = "https://files.pythonhosted.org/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898 }, + { url = "https://files.pythonhosted.org/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691 }, + { url = "https://files.pythonhosted.org/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430 }, + { url = "https://files.pythonhosted.org/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428 }, + { url = "https://files.pythonhosted.org/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580 }, + { url = "https://files.pythonhosted.org/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455 }, + { url = "https://files.pythonhosted.org/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514 }, + { url = "https://files.pythonhosted.org/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065 }, ] [[package]] name = "truststore" version = "0.10.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" }, + { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660 }, ] [[package]] name = "ty" version = "0.0.63" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/ce/cbeaa5c7576fec643609dfbf200d59493523b1cc0481d4e7a5effcbf0630/ty-0.0.63.tar.gz", hash = "sha256:c2f66439393b3acac69306c117d4ae44638ce5fffa4a20c21046e85bd473359f", size = 6280695, upload-time = "2026-07-23T11:41:39.845Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/e4/d17a8e113ab15c692fe6fb9c422112d4bee7e829d80ced35826b45d98d98/ty-0.0.63-py3-none-linux_armv6l.whl", hash = "sha256:9a4ef7782e3af314fb63d006bec5ac3025bd70fee774b4dcfb5e44e8564f1994", size = 12056302, upload-time = "2026-07-23T11:41:03.5Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/0b/357234c815dc4bfcc88c3f860aa0983fe4228c8aa2a5bb16c35ee08a94af/ty-0.0.63-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:01eab0ab70d51ad10298aa2d4b058b387a1fe93e5ee52d2a1ee23e9c69ba8354", size = 11737674, upload-time = "2026-07-23T11:41:05.862Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/13/193d9aeeb6774690351cff9fafabd3ae9b54cc225d125e07fa004ce23bdc/ty-0.0.63-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a671b61eaad16178389e05b9c108c9cb75ae8d84968fe55906484f55d6268338", size = 11264191, upload-time = "2026-07-23T11:41:07.963Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/b7/c5843e16759a2b25fc8a7197473474038e585ac9fdaa6fa16aeb6384bf6a/ty-0.0.63-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b255cc83d95c51bb9ee4931303fdbece8cb1d6d7c655eb77a3f2c8f349fb64d6", size = 11818890, upload-time = "2026-07-23T11:41:09.885Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/20/adf83ae1fc570d9bb449605e1eeeaa523ad2329ca838a636698b5c135d11/ty-0.0.63-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0da1e8183aa4f893421a173904478021498f542ee41273660538da6649a9631c", size = 11853141, upload-time = "2026-07-23T11:41:12.151Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/90/f8effd846e3ee13486ea08257c13094d58b9f188c5641bf609d6a7d5c09f/ty-0.0.63-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128f38eb5a67199e3811426386f7ec96f41251ddf45e04ddc0bcbb29d4853245", size = 12545184, upload-time = "2026-07-23T11:41:14.4Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/76/aac3a30d40431eb1d329acea016b248f5b6255e30ef3d28e19447e344be2/ty-0.0.63-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bedf34aff8b0557f2a7119b314a68a9c9e58c1d21554d0e2748f2c5fe6a1f638", size = 13062375, upload-time = "2026-07-23T11:41:16.441Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/3d/0158733932893e17f6008dadd74c8d7aed422fefc66e47fe77888014fe8c/ty-0.0.63-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7328d63c34587606dce02935a25404f54cd0161bfe413b8f7fc21d174aead612", size = 12619461, upload-time = "2026-07-23T11:41:18.538Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/be/e280ad095b050778f16f493d49a073fa5f6d8f301d3e2e59be6a672ba05c/ty-0.0.63-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504c4457f3a62afe836c1f26a2c9a12549299095f9cc4146778558df51a7515c", size = 12376402, upload-time = "2026-07-23T11:41:20.482Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/57/619788bf335cb86b090470b557ae1eed33613dc24e12142505d32b462e58/ty-0.0.63-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7394ed39424b027c89d5f0c818871c791e33958b88f5bb13e14bd4a12a3ca631", size = 12671377, upload-time = "2026-07-23T11:41:22.489Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/a2/0aff2cdd3c98e2c4729337542b8da0ce1980790e1600e0c4a717a1f46293/ty-0.0.63-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:94c3f155230490f8fb505911655e940c630c25cc0c35a76690cb413254fb4437", size = 11768090, upload-time = "2026-07-23T11:41:24.558Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/4a/cdb5f1d26154144dfee08e1bd671daf827238170f7cee9dad43990bb2016/ty-0.0.63-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c16e1d8b4f0ae99106d5f13a894034a202baf6cdab61e2bb1a239de82904f839", size = 11867747, upload-time = "2026-07-23T11:41:26.794Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/26/ecc09ecb70bc9fbfdf42fa57ff29568f173e8200df575a0d72dc2b8486f9/ty-0.0.63-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b66293dad89eaed4b9fbf3b661614dbeb85b59ba037178a3f9a0adedf264da5c", size = 12120000, upload-time = "2026-07-23T11:41:28.731Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/07/862822f9c2c397785b69b25cf79b4dfc3c0d55684b9adf11ac194450f8e1/ty-0.0.63-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8b29cc832e2ec73502c97dd7325d6ae0e085b34a33529a0f785192317d9862fc", size = 12477862, upload-time = "2026-07-23T11:41:30.847Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/c2/50b08b641578d6f48e8aa28a91d0de32c91aa24dd926ed199e8afd2d37ea/ty-0.0.63-py3-none-win32.whl", hash = "sha256:f0a8fbfd1f990c0c5d85cce018d438969ac79e49247e2192c9745394bb7d17ab", size = 11433155, upload-time = "2026-07-23T11:41:33.28Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/2d/d422a5568f0d1f317186be22241288dc6e08b71dc24aca223f22038ac2d2/ty-0.0.63-py3-none-win_amd64.whl", hash = "sha256:2aa2370bdd6f42e9f37518c812379bef70b9162f9e5aad511495229b0b71cb93", size = 12450036, upload-time = "2026-07-23T11:41:35.559Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/97/2c9748e28ead0650c7ad3e5f74f178832ceabd7cb5c272a882f29eb32ee4/ty-0.0.63-py3-none-win_arm64.whl", hash = "sha256:95ac1a62162c3c7ac204731e95ebf766d62a46a7bfa238a6acbe923fcb772cb1", size = 11826243, upload-time = "2026-07-23T11:41:37.749Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/ce/cbeaa5c7576fec643609dfbf200d59493523b1cc0481d4e7a5effcbf0630/ty-0.0.63.tar.gz", hash = "sha256:c2f66439393b3acac69306c117d4ae44638ce5fffa4a20c21046e85bd473359f", size = 6280695 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/e4/d17a8e113ab15c692fe6fb9c422112d4bee7e829d80ced35826b45d98d98/ty-0.0.63-py3-none-linux_armv6l.whl", hash = "sha256:9a4ef7782e3af314fb63d006bec5ac3025bd70fee774b4dcfb5e44e8564f1994", size = 12056302 }, + { url = "https://files.pythonhosted.org/packages/be/0b/357234c815dc4bfcc88c3f860aa0983fe4228c8aa2a5bb16c35ee08a94af/ty-0.0.63-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:01eab0ab70d51ad10298aa2d4b058b387a1fe93e5ee52d2a1ee23e9c69ba8354", size = 11737674 }, + { url = "https://files.pythonhosted.org/packages/1c/13/193d9aeeb6774690351cff9fafabd3ae9b54cc225d125e07fa004ce23bdc/ty-0.0.63-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a671b61eaad16178389e05b9c108c9cb75ae8d84968fe55906484f55d6268338", size = 11264191 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/c5843e16759a2b25fc8a7197473474038e585ac9fdaa6fa16aeb6384bf6a/ty-0.0.63-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b255cc83d95c51bb9ee4931303fdbece8cb1d6d7c655eb77a3f2c8f349fb64d6", size = 11818890 }, + { url = "https://files.pythonhosted.org/packages/06/20/adf83ae1fc570d9bb449605e1eeeaa523ad2329ca838a636698b5c135d11/ty-0.0.63-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0da1e8183aa4f893421a173904478021498f542ee41273660538da6649a9631c", size = 11853141 }, + { url = "https://files.pythonhosted.org/packages/ea/90/f8effd846e3ee13486ea08257c13094d58b9f188c5641bf609d6a7d5c09f/ty-0.0.63-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128f38eb5a67199e3811426386f7ec96f41251ddf45e04ddc0bcbb29d4853245", size = 12545184 }, + { url = "https://files.pythonhosted.org/packages/b8/76/aac3a30d40431eb1d329acea016b248f5b6255e30ef3d28e19447e344be2/ty-0.0.63-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bedf34aff8b0557f2a7119b314a68a9c9e58c1d21554d0e2748f2c5fe6a1f638", size = 13062375 }, + { url = "https://files.pythonhosted.org/packages/62/3d/0158733932893e17f6008dadd74c8d7aed422fefc66e47fe77888014fe8c/ty-0.0.63-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7328d63c34587606dce02935a25404f54cd0161bfe413b8f7fc21d174aead612", size = 12619461 }, + { url = "https://files.pythonhosted.org/packages/f5/be/e280ad095b050778f16f493d49a073fa5f6d8f301d3e2e59be6a672ba05c/ty-0.0.63-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504c4457f3a62afe836c1f26a2c9a12549299095f9cc4146778558df51a7515c", size = 12376402 }, + { url = "https://files.pythonhosted.org/packages/57/57/619788bf335cb86b090470b557ae1eed33613dc24e12142505d32b462e58/ty-0.0.63-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7394ed39424b027c89d5f0c818871c791e33958b88f5bb13e14bd4a12a3ca631", size = 12671377 }, + { url = "https://files.pythonhosted.org/packages/07/a2/0aff2cdd3c98e2c4729337542b8da0ce1980790e1600e0c4a717a1f46293/ty-0.0.63-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:94c3f155230490f8fb505911655e940c630c25cc0c35a76690cb413254fb4437", size = 11768090 }, + { url = "https://files.pythonhosted.org/packages/43/4a/cdb5f1d26154144dfee08e1bd671daf827238170f7cee9dad43990bb2016/ty-0.0.63-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c16e1d8b4f0ae99106d5f13a894034a202baf6cdab61e2bb1a239de82904f839", size = 11867747 }, + { url = "https://files.pythonhosted.org/packages/7e/26/ecc09ecb70bc9fbfdf42fa57ff29568f173e8200df575a0d72dc2b8486f9/ty-0.0.63-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b66293dad89eaed4b9fbf3b661614dbeb85b59ba037178a3f9a0adedf264da5c", size = 12120000 }, + { url = "https://files.pythonhosted.org/packages/14/07/862822f9c2c397785b69b25cf79b4dfc3c0d55684b9adf11ac194450f8e1/ty-0.0.63-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8b29cc832e2ec73502c97dd7325d6ae0e085b34a33529a0f785192317d9862fc", size = 12477862 }, + { url = "https://files.pythonhosted.org/packages/d0/c2/50b08b641578d6f48e8aa28a91d0de32c91aa24dd926ed199e8afd2d37ea/ty-0.0.63-py3-none-win32.whl", hash = "sha256:f0a8fbfd1f990c0c5d85cce018d438969ac79e49247e2192c9745394bb7d17ab", size = 11433155 }, + { url = "https://files.pythonhosted.org/packages/92/2d/d422a5568f0d1f317186be22241288dc6e08b71dc24aca223f22038ac2d2/ty-0.0.63-py3-none-win_amd64.whl", hash = "sha256:2aa2370bdd6f42e9f37518c812379bef70b9162f9e5aad511495229b0b71cb93", size = 12450036 }, + { url = "https://files.pythonhosted.org/packages/35/97/2c9748e28ead0650c7ad3e5f74f178832ceabd7cb5c272a882f29eb32ee4/ty-0.0.63-py3-none-win_arm64.whl", hash = "sha256:95ac1a62162c3c7ac204731e95ebf766d62a46a7bfa238a6acbe923fcb772cb1", size = 11826243 }, ] [[package]] name = "typeguard" version = "4.5.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/1c/dfba5c4633cafc4c701f237d2ba63b416805047fd6d96aab4cfc40969f98/typeguard-4.5.2.tar.gz", hash = "sha256:5a16dcac23502039299c97c8941651bc33d7ea8cc4b2f7d6bbb1b528f6eea423", size = 80240, upload-time = "2026-05-14T12:59:40.857Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/1c/dfba5c4633cafc4c701f237d2ba63b416805047fd6d96aab4cfc40969f98/typeguard-4.5.2.tar.gz", hash = "sha256:5a16dcac23502039299c97c8941651bc33d7ea8cc4b2f7d6bbb1b528f6eea423", size = 80240 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/29/74eeb4d3f3ae61ca096b018ad486b3b3c74b17bec09ab4edab721cbefec3/typeguard-4.5.2-py3-none-any.whl", hash = "sha256:fcf9de18bd945cdb4c7b996e12b4c51ce83f92f191314a6d7cf1739586ec98cf", size = 36748, upload-time = "2026-05-14T12:59:39.473Z" }, + { url = "https://files.pythonhosted.org/packages/5b/29/74eeb4d3f3ae61ca096b018ad486b3b3c74b17bec09ab4edab721cbefec3/typeguard-4.5.2-py3-none-any.whl", hash = "sha256:fcf9de18bd945cdb4c7b996e12b4c51ce83f92f191314a6d7cf1739586ec98cf", size = 36748 }, ] [[package]] name = "typer" version = "0.27.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/78/fda3361b56efc27944f24225f6ecd13d96d6fcfe37bd0eb34e2f4c63f9fc/typer-0.27.0.tar.gz", hash = "sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5", size = 203430, upload-time = "2026-07-15T19:21:07.007Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/78/fda3361b56efc27944f24225f6ecd13d96d6fcfe37bd0eb34e2f4c63f9fc/typer-0.27.0.tar.gz", hash = "sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5", size = 203430 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/03/26a383c9e58c213199d1aad1c3d353cfc22d4444ec6d2c0bf8ad02523843/typer-0.27.0-py3-none-any.whl", hash = "sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1", size = 122716, upload-time = "2026-07-15T19:21:05.553Z" }, + { url = "https://files.pythonhosted.org/packages/40/03/26a383c9e58c213199d1aad1c3d353cfc22d4444ec6d2c0bf8ad02523843/typer-0.27.0-py3-none-any.whl", hash = "sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1", size = 122716 }, ] [[package]] name = "types-croniter" version = "6.2.4.20260711" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/e1/ef8900515a103efa41b29ce81e59efb18e72bc9642139c5a3a17f6022641/types_croniter-6.2.4.20260711.tar.gz", hash = "sha256:807793e109f4e0d51ec18f6277a1a5f1d133e425ac6bee3a7c3254ab1793e0ea", size = 12164, upload-time = "2026-07-11T04:51:18.624Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/e1/ef8900515a103efa41b29ce81e59efb18e72bc9642139c5a3a17f6022641/types_croniter-6.2.4.20260711.tar.gz", hash = "sha256:807793e109f4e0d51ec18f6277a1a5f1d133e425ac6bee3a7c3254ab1793e0ea", size = 12164 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/3f/896a98180a55f855bd068b97e8c99fd4ce1885a4b43f68fd7b29641084f6/types_croniter-6.2.4.20260711-py3-none-any.whl", hash = "sha256:601b68139a47fe1b2177f433132cc81fd91155f3059a49c5cb63404c389dff01", size = 9746, upload-time = "2026-07-11T04:51:17.662Z" }, + { url = "https://files.pythonhosted.org/packages/c8/3f/896a98180a55f855bd068b97e8c99fd4ce1885a4b43f68fd7b29641084f6/types_croniter-6.2.4.20260711-py3-none-any.whl", hash = "sha256:601b68139a47fe1b2177f433132cc81fd91155f3059a49c5cb63404c389dff01", size = 9746 }, ] [[package]] name = "types-docutils" version = "0.22.3.20260724" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/8f/30e02b59a9aad81eacd3d02fab5365257e89fde56e7acf44a4128f5f80aa/types_docutils-0.22.3.20260724.tar.gz", hash = "sha256:0223ce87f9b8331a5a3a7c7832e828033d289da6b878a0dcfbc74c30ec58850e", size = 57883, upload-time = "2026-07-24T04:58:36.388Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/8f/30e02b59a9aad81eacd3d02fab5365257e89fde56e7acf44a4128f5f80aa/types_docutils-0.22.3.20260724.tar.gz", hash = "sha256:0223ce87f9b8331a5a3a7c7832e828033d289da6b878a0dcfbc74c30ec58850e", size = 57883 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/1a/898fbb98680dbd5eb7ac8e9cbaf39cf234d329a45d89d04faaf33d7d8008/types_docutils-0.22.3.20260724-py3-none-any.whl", hash = "sha256:45e2fe584608671aa648784c4f805d08a36cd69eb2bd542e60522140c46dc89c", size = 91986, upload-time = "2026-07-24T04:58:35.368Z" }, + { url = "https://files.pythonhosted.org/packages/13/1a/898fbb98680dbd5eb7ac8e9cbaf39cf234d329a45d89d04faaf33d7d8008/types_docutils-0.22.3.20260724-py3-none-any.whl", hash = "sha256:45e2fe584608671aa648784c4f805d08a36cd69eb2bd542e60522140c46dc89c", size = 91986 }, ] [[package]] name = "types-jsonschema" version = "4.26.0.20260518" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/46/73b6a5d61a61015c4248030a8cb07e5bdddb4041430fae9e585a68692578/types_jsonschema-4.26.0.20260518.tar.gz", hash = "sha256:e1dd53dc97a64f5eccdd6fa9839666e09bb500a8ebba2db6fdaf1789faea81a6", size = 16638, upload-time = "2026-05-18T06:06:44.106Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/46/73b6a5d61a61015c4248030a8cb07e5bdddb4041430fae9e585a68692578/types_jsonschema-4.26.0.20260518.tar.gz", hash = "sha256:e1dd53dc97a64f5eccdd6fa9839666e09bb500a8ebba2db6fdaf1789faea81a6", size = 16638 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/d5/134f8a147dcecda10db7f60cfc6af0578a25a5c53c87b3907a64385e0184/types_jsonschema-4.26.0.20260518-py3-none-any.whl", hash = "sha256:30b30a518c7fe335df85c919fcbcc631b69c03d4a4b5b632fa916bea03065307", size = 16072, upload-time = "2026-05-18T06:06:43.264Z" }, + { url = "https://files.pythonhosted.org/packages/07/d5/134f8a147dcecda10db7f60cfc6af0578a25a5c53c87b3907a64385e0184/types_jsonschema-4.26.0.20260518-py3-none-any.whl", hash = "sha256:30b30a518c7fe335df85c919fcbcc631b69c03d4a4b5b632fa916bea03065307", size = 16072 }, ] [[package]] name = "types-pygments" version = "2.20.0.20260518" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "types-docutils" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/66/3e27e8dbe72947d51355d1fcba222a55bf5f0770ee660e9cc9df0d1ce5d7/types_pygments-2.20.0.20260518.tar.gz", hash = "sha256:bcab233d0389cb0a91146eb860e7bdcbceaa0f30bee73cefc7b367129cc4a330", size = 21152, upload-time = "2026-05-18T06:07:26.927Z" } +sdist = { url = "https://files.pythonhosted.org/packages/db/66/3e27e8dbe72947d51355d1fcba222a55bf5f0770ee660e9cc9df0d1ce5d7/types_pygments-2.20.0.20260518.tar.gz", hash = "sha256:bcab233d0389cb0a91146eb860e7bdcbceaa0f30bee73cefc7b367129cc4a330", size = 21152 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/d6/5f19f5b633af6a7666c6096fdd06b70f0d4a8f585af5e18a3ef58ad47ec1/types_pygments-2.20.0.20260518-py3-none-any.whl", hash = "sha256:e40728efd00c9da5936366648d5b3c55e72ed52bdd80495a96577b4d717c4fcb", size = 29002, upload-time = "2026-05-18T06:07:26.054Z" }, + { url = "https://files.pythonhosted.org/packages/66/d6/5f19f5b633af6a7666c6096fdd06b70f0d4a8f585af5e18a3ef58ad47ec1/types_pygments-2.20.0.20260518-py3-none-any.whl", hash = "sha256:e40728efd00c9da5936366648d5b3c55e72ed52bdd80495a96577b4d717c4fcb", size = 29002 }, ] [[package]] name = "types-pyyaml" version = "6.0.12.20260724" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/6f/a28f44bcd56bebed42b028a2894c79853e2f5e6b5279e633cb3f287a05e7/types_pyyaml-6.0.12.20260724.tar.gz", hash = "sha256:3c1ce1bb73cd5ec02e90390c2b1f00e810d241d8825fd73ff359696839271b6b", size = 17893, upload-time = "2026-07-24T04:58:43.453Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/6f/a28f44bcd56bebed42b028a2894c79853e2f5e6b5279e633cb3f287a05e7/types_pyyaml-6.0.12.20260724.tar.gz", hash = "sha256:3c1ce1bb73cd5ec02e90390c2b1f00e810d241d8825fd73ff359696839271b6b", size = 17893 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/42/0337fefc615e20ee55d1c8f71b774a9b2b734a04669139c20753b27a2a3a/types_pyyaml-6.0.12.20260724-py3-none-any.whl", hash = "sha256:d57db930a4b2efbc57cf430ec8882765d246929432fa253092f383902329a453", size = 20312, upload-time = "2026-07-24T04:58:42.486Z" }, + { url = "https://files.pythonhosted.org/packages/8b/42/0337fefc615e20ee55d1c8f71b774a9b2b734a04669139c20753b27a2a3a/types_pyyaml-6.0.12.20260724-py3-none-any.whl", hash = "sha256:d57db930a4b2efbc57cf430ec8882765d246929432fa253092f383902329a453", size = 20312 }, ] [[package]] name = "typing-extensions" version = "4.16.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571 }, ] [[package]] name = "typing-inspect" version = "0.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f" }, + { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827 }, ] [[package]] name = "typing-inspection" version = "0.4.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, ] [[package]] name = "tzdata" version = "2026.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168 }, ] [[package]] name = "uncalled-for" version = "0.3.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/82/345cc927f7fbdae6065e7768759932fcc827fc20b29b45dfbafa2f1f7da4/uncalled_for-0.3.2.tar.gz", hash = "sha256:89f5dbcd71e2b8f47c030b1fa302e6cce2ec795d1ac565eeb6525c5fe55cb8a2", size = 50032, upload-time = "2026-05-06T13:38:25.204Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/82/345cc927f7fbdae6065e7768759932fcc827fc20b29b45dfbafa2f1f7da4/uncalled_for-0.3.2.tar.gz", hash = "sha256:89f5dbcd71e2b8f47c030b1fa302e6cce2ec795d1ac565eeb6525c5fe55cb8a2", size = 50032 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/25/2c87754f3a9e692315f7b811244090e68f362979fc8886b3fbd2985a1d8c/uncalled_for-0.3.2-py3-none-any.whl", hash = "sha256:0ff60b142c7d1f8070bde9d42afaa70aedc77dcc10998c227687e9c15713418e", size = 11444, upload-time = "2026-05-06T13:38:24.025Z" }, + { url = "https://files.pythonhosted.org/packages/3b/25/2c87754f3a9e692315f7b811244090e68f362979fc8886b3fbd2985a1d8c/uncalled_for-0.3.2-py3-none-any.whl", hash = "sha256:0ff60b142c7d1f8070bde9d42afaa70aedc77dcc10998c227687e9c15713418e", size = 11444 }, ] [[package]] name = "universal-pathlib" version = "0.3.10" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fsspec" }, { name = "pathlib-abc" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/6e/d997a70ee8f4c61f9a7e2f4f8af721cf072a3326848fc881b05187e52558/universal_pathlib-0.3.10.tar.gz", hash = "sha256:4487cbc90730a48cfb64f811d99e14b6faed6d738420cd5f93f59f48e6930bfb", size = 261110, upload-time = "2026-02-22T14:40:58.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/6e/d997a70ee8f4c61f9a7e2f4f8af721cf072a3326848fc881b05187e52558/universal_pathlib-0.3.10.tar.gz", hash = "sha256:4487cbc90730a48cfb64f811d99e14b6faed6d738420cd5f93f59f48e6930bfb", size = 261110 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/1a/5d9a402b39ec892d856bbdd9db502ff73ce28cdf4aff72eb1ce1d6843506/universal_pathlib-0.3.10-py3-none-any.whl", hash = "sha256:dfaf2fb35683d2eb1287a3ed7b215e4d6016aa6eaf339c607023d22f90821c66", size = 83528, upload-time = "2026-02-22T14:40:57.316Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1a/5d9a402b39ec892d856bbdd9db502ff73ce28cdf4aff72eb1ce1d6843506/universal_pathlib-0.3.10-py3-none-any.whl", hash = "sha256:dfaf2fb35683d2eb1287a3ed7b215e4d6016aa6eaf339c607023d22f90821c66", size = 83528 }, ] [[package]] name = "upathtools" version = "1.20.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiofile" }, { name = "fsspec" }, @@ -5500,9 +5286,9 @@ dependencies = [ { name = "ripgrep-rs" }, { name = "universal-pathlib" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/36/dec97bc75c092f493492e023260d1994785365fdfe3a6705eb103fbc81ed/upathtools-1.20.4.tar.gz", hash = "sha256:d29173ea690299e4e3955d6900d666ef6226ead4df91849bcbd4c5a05fe8e179", size = 207060, upload-time = "2026-04-24T22:03:30.677Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/36/dec97bc75c092f493492e023260d1994785365fdfe3a6705eb103fbc81ed/upathtools-1.20.4.tar.gz", hash = "sha256:d29173ea690299e4e3955d6900d666ef6226ead4df91849bcbd4c5a05fe8e179", size = 207060 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/72/70943b53a6dad3a5a8c3ea4ed7c498344b6f8eafe5a36e7982d686c4bc7e/upathtools-1.20.4-py3-none-any.whl", hash = "sha256:45bf470cf529c2bd4c492427fac24c4a75863a603bbf061c91e1d526371926c5", size = 274153, upload-time = "2026-04-24T22:03:29.101Z" }, + { url = "https://files.pythonhosted.org/packages/81/72/70943b53a6dad3a5a8c3ea4ed7c498344b6f8eafe5a36e7982d686c4bc7e/upathtools-1.20.4-py3-none-any.whl", hash = "sha256:45bf470cf529c2bd4c492427fac24c4a75863a603bbf061c91e1d526371926c5", size = 274153 }, ] [package.optional-dependencies] @@ -5515,23 +5301,23 @@ httpx = [ [[package]] name = "urllib3" version = "2.7.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087 }, ] [[package]] name = "uvicorn" version = "0.51.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/65/b7c6c443ccc58678c91e1e973bbe2a878591538655d6e1d47f24ba1c51f3/uvicorn-0.51.0.tar.gz", hash = "sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0", size = 94412, upload-time = "2026-07-08T10:59:05.962Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/65/b7c6c443ccc58678c91e1e973bbe2a878591538655d6e1d47f24ba1c51f3/uvicorn-0.51.0.tar.gz", hash = "sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0", size = 94412 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/ec/dbb7e5a6b91f86bfb9eb7d2988a2730907b6a729875b949c7f022e8b88fa/uvicorn-0.51.0-py3-none-any.whl", hash = "sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b", size = 73219, upload-time = "2026-07-08T10:59:04.44Z" }, + { url = "https://files.pythonhosted.org/packages/45/ec/dbb7e5a6b91f86bfb9eb7d2988a2730907b6a729875b949c7f022e8b88fa/uvicorn-0.51.0-py3-none-any.whl", hash = "sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b", size = 73219 }, ] [package.optional-dependencies] @@ -5547,157 +5333,459 @@ standard = [ [[package]] name = "uvloop" version = "0.22.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611 }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811 }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562 }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890 }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472 }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051 }, ] [[package]] name = "vcrpy" version = "8.3.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, { name = "wrapt" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/d5/8a1f8eb603e2d35fbb0ecd1e309d0c5c18a0ecfc8c0a8f04088bbc8f833b/vcrpy-8.3.0.tar.gz", hash = "sha256:46d64e77e8d95e5c76c7d9a94ff05d8b38b2ae4e1d4869eb0235024b6fcb5212", size = 96117, upload-time = "2026-07-04T14:27:01.608Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/d5/8a1f8eb603e2d35fbb0ecd1e309d0c5c18a0ecfc8c0a8f04088bbc8f833b/vcrpy-8.3.0.tar.gz", hash = "sha256:46d64e77e8d95e5c76c7d9a94ff05d8b38b2ae4e1d4869eb0235024b6fcb5212", size = 96117 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/77/cb4219be91508399cbcb6143bad89462cfb16f6c638458f454a5d46ac95a/vcrpy-8.3.0-py3-none-any.whl", hash = "sha256:bd66e6143746778157f00e2a922527a8d96b2fdc350be8988a45a29c843815b9", size = 46530, upload-time = "2026-07-04T14:27:00.546Z" }, + { url = "https://files.pythonhosted.org/packages/34/77/cb4219be91508399cbcb6143bad89462cfb16f6c638458f454a5d46ac95a/vcrpy-8.3.0-py3-none-any.whl", hash = "sha256:bd66e6143746778157f00e2a922527a8d96b2fdc350be8988a45a29c843815b9", size = 46530 }, ] [[package]] name = "watchdog" version = "6.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, ] [[package]] name = "watchfiles" version = "1.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730 }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842 }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989 }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978 }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248 }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847 }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974 }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782 }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182 }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841 }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028 }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183 }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059 }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186 }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031 }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205 }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892 }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867 }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217 }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458 }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707 }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663 }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537 }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194 }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194 }, ] [[package]] name = "wcwidth" version = "0.8.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253, upload-time = "2026-06-29T18:11:11.601Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" }, + { url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166 }, ] [[package]] name = "websocket-client" version = "1.9.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, ] [[package]] name = "websockets" version = "15.0.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, ] [[package]] name = "win32-setctime" version = "1.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 }, +] + +[[package]] +name = "wolfharness" +version = "2.9.5" +source = { editable = "." } +dependencies = [ + { name = "ag-ui-protocol" }, + { name = "alembic" }, + { name = "anyenv", extra = ["httpx"] }, + { name = "anyio" }, + { name = "docler" }, + { name = "docstring-parser" }, + { name = "epregistry" }, + { name = "evented" }, + { name = "exxec" }, + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "httpx" }, + { name = "jinja2" }, + { name = "jinjarope" }, + { name = "keyring" }, + { name = "logfire", extra = ["fastapi"] }, + { name = "mcp" }, + { name = "pillow" }, + { name = "platformdirs" }, + { name = "promptantic" }, + { name = "psygnal" }, + { name = "py-key-value-aio", extra = ["disk"] }, + { name = "pydantic" }, + { name = "pydantic-ai-slim", extra = ["anthropic", "google", "mistral", "openai", "retries"] }, + { name = "pydantic-graph" }, + { name = "pydantic-xml" }, + { name = "pydocket" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "ripgrep-rs" }, + { name = "schemez", extra = ["codegen"] }, + { name = "searchly", extra = ["all"] }, + { name = "slashed" }, + { name = "sqlalchemy", extra = ["aiosqlite"] }, + { name = "sqlmodel" }, + { name = "starlette" }, + { name = "structlog" }, + { name = "sublime-search" }, + { name = "tokonomics" }, + { name = "toprompt" }, + { name = "typer" }, + { name = "upathtools", extra = ["httpx"] }, + { name = "uvicorn", extra = ["standard"] }, + { name = "watchfiles" }, + { name = "websockets" }, + { name = "yamling" }, +] + +[package.optional-dependencies] +a2a = [ + { name = "fasta2a" }, + { name = "starlette" }, +] +bot = [ + { name = "croniter" }, + { name = "python-telegram-bot", extra = ["socks"] }, + { name = "slack-sdk" }, + { name = "slackify-markdown" }, +] +braintrust = [ + { name = "autoevals" }, + { name = "braintrust" }, +] +clipboard = [ + { name = "copykitten" }, +] +coding = [ + { name = "ast-grep-py" }, + { name = "grep-ast" }, + { name = "rustworkx" }, + { name = "tree-sitter" }, + { name = "tree-sitter-c" }, + { name = "tree-sitter-cpp" }, + { name = "tree-sitter-go" }, + { name = "tree-sitter-javascript" }, + { name = "tree-sitter-json" }, + { name = "tree-sitter-python" }, + { name = "tree-sitter-rust" }, + { name = "tree-sitter-typescript" }, + { name = "tree-sitter-yaml" }, +] +composio = [ + { name = "composio" }, +] +events = [ + { name = "evented", extra = ["all"] }, +] +langfuse = [ + { name = "langfuse" }, +] +markitdown = [ + { name = "markitdown" }, +] +mcp-discovery = [ + { name = "fastembed" }, + { name = "lancedb" }, + { name = "pyarrow" }, +] +mcp-run = [ + { name = "mcpx-py" }, +] +notifications = [ + { name = "apprise" }, +] +promptlayer = [ + { name = "promptlayer" }, +] +tiktoken = [ + { name = "tiktoken" }, +] +tts = [ + { name = "anyvoice", extra = ["openai", "tts-edge"] }, +] +viking = [ + { name = "openviking-sdk" }, +] +watchdog = [ + { name = "watchdog" }, +] +zed = [ + { name = "zstandard" }, +] + +[package.dev-dependencies] +benchmark = [ + { name = "pyinstrument" }, +] +dev = [ + { name = "check-jsonschema" }, + { name = "devtools" }, + { name = "dirty-equals" }, + { name = "fastembed" }, + { name = "import-linter" }, + { name = "inline-snapshot" }, + { name = "lancedb" }, + { name = "openapi-spec-validator" }, + { name = "pyinstaller" }, + { name = "pyreadline3" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-recording" }, + { name = "pytest-rerunfailures" }, + { name = "pytest-timeout" }, + { name = "pytest-xdist" }, + { name = "syrupy" }, + { name = "vcrpy" }, +] +docs = [ + { name = "beautifulsoup4" }, + { name = "json-schema-for-humans" }, + { name = "mkdocs-material" }, + { name = "mknodes" }, + { name = "zensical" }, +] +lint = [ + { name = "mypy", extra = ["faster-cache"] }, + { name = "ruff" }, + { name = "ty" }, + { name = "types-croniter" }, + { name = "types-jsonschema" }, + { name = "types-pygments" }, + { name = "types-pyyaml" }, +] + +[package.metadata] +requires-dist = [ + { name = "ag-ui-protocol", specifier = ">=0.1.10" }, + { name = "alembic", specifier = ">=1.16.5" }, + { name = "anyenv", extras = ["httpx"], specifier = ">=0.3.0" }, + { name = "anyio", specifier = ">=4.0" }, + { name = "anyvoice", extras = ["tts-edge", "openai"], marker = "extra == 'tts'", specifier = ">=0.0.2" }, + { name = "apprise", marker = "extra == 'notifications'", specifier = ">=1.9.5" }, + { name = "ast-grep-py", marker = "extra == 'coding'", specifier = ">=0.40.0" }, + { name = "autoevals", marker = "extra == 'braintrust'" }, + { name = "braintrust", marker = "extra == 'braintrust'" }, + { name = "composio", marker = "extra == 'composio'" }, + { name = "copykitten", marker = "extra == 'clipboard'" }, + { name = "croniter", marker = "extra == 'bot'", specifier = ">=2.0.0" }, + { name = "docler", specifier = ">=1.0.3" }, + { name = "docstring-parser", specifier = ">=0.17.0" }, + { name = "epregistry" }, + { name = "evented", specifier = ">=1.0.5" }, + { name = "evented", extras = ["all"], marker = "extra == 'events'" }, + { name = "exxec", specifier = ">=0.1.0" }, + { name = "fasta2a", marker = "extra == 'a2a'" }, + { name = "fastapi" }, + { name = "fastembed", marker = "python_full_version < '3.14' and extra == 'mcp-discovery'", specifier = ">=0.7.4" }, + { name = "fastmcp", specifier = ">=2.12.4" }, + { name = "filelock", specifier = ">=3.13" }, + { name = "fsspec" }, + { name = "grep-ast", marker = "extra == 'coding'" }, + { name = "httpx" }, + { name = "jinja2" }, + { name = "jinjarope" }, + { name = "keyring", specifier = ">=25.6.0" }, + { name = "lancedb", marker = "python_full_version < '3.14' and extra == 'mcp-discovery'", specifier = ">=0.26.0" }, + { name = "langfuse", marker = "extra == 'langfuse'" }, + { name = "logfire", extras = ["fastapi"] }, + { name = "markitdown", marker = "python_full_version < '3.14' and extra == 'markitdown'" }, + { name = "mcp", specifier = ">=1.2.0" }, + { name = "mcpx-py", marker = "extra == 'mcp-run'", specifier = ">=0.7.0" }, + { name = "openviking-sdk", marker = "extra == 'viking'", specifier = ">=0.1.0" }, + { name = "pillow", specifier = ">=11.3.0" }, + { name = "platformdirs" }, + { name = "promptantic", specifier = ">=0.4.5" }, + { name = "promptlayer", marker = "extra == 'promptlayer'" }, + { name = "psygnal", specifier = ">=0.11.1" }, + { name = "py-key-value-aio", extras = ["disk"] }, + { name = "pyarrow", marker = "python_full_version < '3.14' and extra == 'mcp-discovery'", specifier = ">=19.0.0" }, + { name = "pydantic", specifier = ">=2.10.0" }, + { name = "pydantic-ai-slim", extras = ["openai", "google", "anthropic", "mistral", "retries"], specifier = ">=2.12.0" }, + { name = "pydantic-graph", specifier = ">=2.12.0" }, + { name = "pydantic-xml", specifier = ">=2.6.0" }, + { name = "pydocket", specifier = ">=0.16.1" }, + { name = "python-dotenv", specifier = ">=1.0.1" }, + { name = "python-telegram-bot", extras = ["socks"], marker = "extra == 'bot'", specifier = ">=21.0" }, + { name = "rich" }, + { name = "ripgrep-rs", specifier = ">=0.3.0" }, + { name = "rustworkx", marker = "extra == 'coding'", specifier = ">=0.17.1" }, + { name = "schemez", extras = ["codegen"] }, + { name = "searchly", extras = ["all"], specifier = ">=2.0.1" }, + { name = "slack-sdk", marker = "extra == 'bot'", specifier = ">=3.26.0" }, + { name = "slackify-markdown", marker = "extra == 'bot'", specifier = ">=0.2.0" }, + { name = "slashed", specifier = ">=0.1.0" }, + { name = "sqlalchemy", extras = ["aiosqlite"] }, + { name = "sqlmodel", specifier = ">=0.0.22" }, + { name = "starlette", specifier = ">=0.40" }, + { name = "starlette", marker = "extra == 'a2a'" }, + { name = "structlog", specifier = ">=25.5.0" }, + { name = "sublime-search", specifier = ">=0.3.1" }, + { name = "tiktoken", marker = "extra == 'tiktoken'" }, + { name = "tokonomics", specifier = ">=0.1.2" }, + { name = "toprompt", specifier = ">=0.0.1" }, + { name = "tree-sitter", marker = "extra == 'coding'", specifier = ">=0.25.2" }, + { name = "tree-sitter-c", marker = "extra == 'coding'", specifier = ">=0.24.1" }, + { name = "tree-sitter-cpp", marker = "extra == 'coding'", specifier = ">=0.23.0" }, + { name = "tree-sitter-go", marker = "extra == 'coding'", specifier = ">=0.23.0" }, + { name = "tree-sitter-javascript", marker = "extra == 'coding'", specifier = ">=0.25.0" }, + { name = "tree-sitter-json", marker = "extra == 'coding'", specifier = ">=0.24.0" }, + { name = "tree-sitter-python", marker = "extra == 'coding'", specifier = ">=0.25.0" }, + { name = "tree-sitter-rust", marker = "extra == 'coding'", specifier = ">=0.23.0" }, + { name = "tree-sitter-typescript", marker = "extra == 'coding'", specifier = ">=0.23.0" }, + { name = "tree-sitter-yaml", marker = "extra == 'coding'", specifier = ">=0.6.0" }, + { name = "typer" }, + { name = "upathtools", extras = ["httpx"], specifier = ">=0.1.0" }, + { name = "uvicorn", extras = ["standard"] }, + { name = "watchdog", marker = "extra == 'watchdog'", specifier = ">=4.0.0" }, + { name = "watchfiles", specifier = ">=1.1.1" }, + { name = "websockets", specifier = ">=15.0" }, + { name = "yamling", specifier = ">=2.0.2" }, + { name = "zstandard", marker = "extra == 'zed'", specifier = ">=0.23.0" }, +] +provides-extras = ["a2a", "bot", "braintrust", "clipboard", "coding", "composio", "events", "langfuse", "markitdown", "mcp-discovery", "mcp-run", "notifications", "promptlayer", "tiktoken", "tts", "viking", "watchdog", "zed"] + +[package.metadata.requires-dev] +benchmark = [{ name = "pyinstrument" }] +dev = [ + { name = "check-jsonschema", specifier = ">=0.35.0" }, + { name = "devtools" }, + { name = "dirty-equals", specifier = ">=0.9.0" }, + { name = "fastembed", marker = "python_full_version < '3.14'", specifier = ">=0.7.4" }, + { name = "import-linter", specifier = ">=2.12" }, + { name = "inline-snapshot", specifier = ">=0.13.0" }, + { name = "lancedb", marker = "python_full_version < '3.14'", specifier = ">=0.26.0" }, + { name = "openapi-spec-validator" }, + { name = "pyinstaller", specifier = ">=6.17.0" }, + { name = "pyreadline3" }, + { name = "pytest" }, + { name = "pytest-asyncio", specifier = ">=0.24.0" }, + { name = "pytest-cov" }, + { name = "pytest-recording", specifier = ">=0.13.0" }, + { name = "pytest-rerunfailures", specifier = ">=16.1" }, + { name = "pytest-timeout", specifier = ">=2.4.0" }, + { name = "pytest-xdist" }, + { name = "syrupy", specifier = ">=4.0.0" }, + { name = "vcrpy", specifier = ">=7.0.0" }, +] +docs = [ + { name = "beautifulsoup4", specifier = ">=4.14.3" }, + { name = "json-schema-for-humans", specifier = ">=1.0.0" }, + { name = "mkdocs-material" }, + { name = "mknodes" }, + { name = "zensical", specifier = ">=0.0.51" }, +] +lint = [ + { name = "mypy", extras = ["faster-cache"] }, + { name = "ruff" }, + { name = "ty" }, + { name = "types-croniter" }, + { name = "types-jsonschema", specifier = ">=4.25.1.20251009" }, + { name = "types-pygments" }, + { name = "types-pyyaml" }, ] [[package]] name = "wrapt" version = "1.17.3" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003 }, + { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025 }, + { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108 }, + { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072 }, + { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214 }, + { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105 }, + { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766 }, + { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711 }, + { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885 }, + { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896 }, + { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591 }, ] [[package]] name = "yamling" version = "2.1.7" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyenv" }, { name = "fsspec" }, @@ -5707,46 +5795,46 @@ dependencies = [ { name = "universal-pathlib" }, { name = "upathtools" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/77/16d795d8c89ab018f8aeac0b075adb0491dbe2f3e95410f89ac93ff46803/yamling-2.1.7.tar.gz", hash = "sha256:b75e5b37dc3d2a874286604ec7179a3a149541c3ebfeb7f50ee7e66e1bacabf9", size = 24561, upload-time = "2025-12-10T10:47:50.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/77/16d795d8c89ab018f8aeac0b075adb0491dbe2f3e95410f89ac93ff46803/yamling-2.1.7.tar.gz", hash = "sha256:b75e5b37dc3d2a874286604ec7179a3a149541c3ebfeb7f50ee7e66e1bacabf9", size = 24561 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/3f/d981b71fd7332a3f70a17e4111e01f36ec38b7333eda8ae508cc2959b7de/yamling-2.1.7-py3-none-any.whl", hash = "sha256:730c4be2c5efe44660e1b77db82a87c305cdfcc669052742925b026c1078081a", size = 30465, upload-time = "2025-12-10T10:47:47.611Z" }, + { url = "https://files.pythonhosted.org/packages/2a/3f/d981b71fd7332a3f70a17e4111e01f36ec38b7333eda8ae508cc2959b7de/yamling-2.1.7-py3-none-any.whl", hash = "sha256:730c4be2c5efe44660e1b77db82a87c305cdfcc669052742925b026c1078081a", size = 30465 }, ] [[package]] name = "yarl" version = "1.24.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/33/ebe9e3d1f86c7a0b51094c0a146392045ca1631d2664889539dec8088a33/yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f", size = 228679, upload-time = "2026-07-20T02:07:45.435Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/63/64ef361967cc983573149dc1515d531db5da8a4c92d22bb833d59e01b313/yarl-1.24.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:79af890482fc94648e8cde4c68620378f7fef60932710fa17a66abc039244da2", size = 135075, upload-time = "2026-07-20T02:05:59.671Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/89/55920fd853ce43e608adbc3962456f0d649d6bb15250dc2988321da0fe1c/yarl-1.24.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46c2f213e23a04b93a392942d782eb9e413e6ef6bf7c8c53884e599a5c174dcb", size = 97225, upload-time = "2026-07-20T02:06:01.769Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/f0/7688d3f2cfff7590df2af38ec46d969f4281a4dddb08a9ad2eafbcdddf98/yarl-1.24.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92ab3e11448f2ff7bf53c5a26eff0edc086898ec8b21fb154b85839ce1d88075", size = 96751, upload-time = "2026-07-20T02:06:03.676Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/1a/a851a0f94aaaf379dd4f901bfc80f634280bec51eb260b47363e2a4cd62e/yarl-1.24.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebb0ec7f17803063d5aeb982f3b1bd2b2f4e4fae6751226cbd6ba1fcfe9e63ff", size = 107960, upload-time = "2026-07-20T02:06:05.699Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/a8/faea066c12f9c77ca0de90641f1655f9dd7b412477bf28c76d692f3aecff/yarl-1.24.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:82632daed195dcc8ea664e8556dc9bdbd671960fb3776bd92806ce05792c2448", size = 103500, upload-time = "2026-07-20T02:06:07.556Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/9c/1e67084c2a6e2f2db0e3be798328cb3be42c0119b621d25461479a224d21/yarl-1.24.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:53e549287ef628fecba270045c9701b0c564563a9b0577d24a4ec75b8ab8040f", size = 115780, upload-time = "2026-07-20T02:06:09.599Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/86/1f94664e147474337e3359f52012cf3d02f825f694317b178bfba1078c62/yarl-1.24.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fcd3b77e2f17bbe4ca56ec7bcb07992647d19d0b9c05d84886dcd6f9eb810afd", size = 115308, upload-time = "2026-07-20T02:06:11.352Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/43/8e55ae7538ba5f28ccb3c845c6dd4549cf7016d5992e5326512519107cdd/yarl-1.24.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d46b86567dd4e248c6c159fcbcdcce01e0a5c8a7cd2334a0fff759d0fa075b16", size = 110574, upload-time = "2026-07-20T02:06:13.129Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/ba/a889ec8765cedcf2ac44dcb02d6a21e4861399b243b263c5f2dde27ee740/yarl-1.24.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f72c74aa99359e27a2ee8d6613fefa28b5f76a983c083074dfc2aaa4ab46213", size = 109914, upload-time = "2026-07-20T02:06:15.243Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/c3/e45f821af67b791c2dbbe4a9f4137a1d33f8d386654a05a0c3f47bdfa25d/yarl-1.24.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f45789ce415a7ec0820dc4f82925f9b5f7732070be1dec1f5f23ec381435a24", size = 107712, upload-time = "2026-07-20T02:06:17.443Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/00/2ab0f42c9857fcb490bfaa6647b14540b53d241ab209f23220b958cc5832/yarl-1.24.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6e73e7fe93f17a7b191f52ec9da9dd8c06a8fe735a1ecbd13b97d1c723bff385", size = 104251, upload-time = "2026-07-20T02:06:19.259Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/70/709d9a286e98af2c7fd8e4e6cada658b5c0e30d87dd7e2a63c2fb5767217/yarl-1.24.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4a36f9becdd4c5c52a20c3e9484128b070b1dcfc8944c006f3a528295a359a9c", size = 115319, upload-time = "2026-07-20T02:06:21.207Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/6c/3eaa515142991fe84cfc483ff986492211f1978f90161ccefdbec919d09b/yarl-1.24.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:7bcbe0fcf850eae67b6b01749815a4f7161c560a844c769ad7b48fcd99f791c4", size = 109163, upload-time = "2026-07-20T02:06:23.006Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/64/711dafce66c323a3144d470547a71c5384c57623308ac8bb5e4b903ac148/yarl-1.24.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:24e861e9630e0daddcb9191fb187f60f034e17a4426f8101279f0c475cd74144", size = 115435, upload-time = "2026-07-20T02:06:24.923Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/f3/9b9d0e6d84bea851eb1ba99e4bdc755b86fd813e49ec86dfe42f26befdef/yarl-1.24.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9335a099ad87287c37fe5d1a982ff392fa5efe5d14b40a730b1ec1d6a41382b4", size = 110691, upload-time = "2026-07-20T02:06:26.973Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/e4/62a06b7e87c4246ac76b7c2da136f972eb4a3a1fc94abb07e7022d6fdb0a/yarl-1.24.5-cp313-cp313-win_amd64.whl", hash = "sha256:2dbe06fc16bc91502bca713704022182e5729861ae00277c3a23354b40929740", size = 97454, upload-time = "2026-07-20T02:06:29.163Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/c9/5fc8025b318ab10db413b61056bd0d95c557a70e8df4210c7511f866329c/yarl-1.24.5-cp313-cp313-win_arm64.whl", hash = "sha256:6b8536851f9f65e7f00c7a1d49ba7f2be0ffe2c11555367fc9f50d9f842410a1", size = 92813, upload-time = "2026-07-20T02:06:31.113Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/02/962c1cbfc401a30c1d034dc67ff395f64b52302c6d62de556c1fca99acc0/yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7", size = 58612, upload-time = "2026-07-20T02:07:43.461Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/31/33/ebe9e3d1f86c7a0b51094c0a146392045ca1631d2664889539dec8088a33/yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f", size = 228679 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/63/64ef361967cc983573149dc1515d531db5da8a4c92d22bb833d59e01b313/yarl-1.24.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:79af890482fc94648e8cde4c68620378f7fef60932710fa17a66abc039244da2", size = 135075 }, + { url = "https://files.pythonhosted.org/packages/bb/89/55920fd853ce43e608adbc3962456f0d649d6bb15250dc2988321da0fe1c/yarl-1.24.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46c2f213e23a04b93a392942d782eb9e413e6ef6bf7c8c53884e599a5c174dcb", size = 97225 }, + { url = "https://files.pythonhosted.org/packages/15/f0/7688d3f2cfff7590df2af38ec46d969f4281a4dddb08a9ad2eafbcdddf98/yarl-1.24.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92ab3e11448f2ff7bf53c5a26eff0edc086898ec8b21fb154b85839ce1d88075", size = 96751 }, + { url = "https://files.pythonhosted.org/packages/05/1a/a851a0f94aaaf379dd4f901bfc80f634280bec51eb260b47363e2a4cd62e/yarl-1.24.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebb0ec7f17803063d5aeb982f3b1bd2b2f4e4fae6751226cbd6ba1fcfe9e63ff", size = 107960 }, + { url = "https://files.pythonhosted.org/packages/6c/a8/faea066c12f9c77ca0de90641f1655f9dd7b412477bf28c76d692f3aecff/yarl-1.24.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:82632daed195dcc8ea664e8556dc9bdbd671960fb3776bd92806ce05792c2448", size = 103500 }, + { url = "https://files.pythonhosted.org/packages/fb/9c/1e67084c2a6e2f2db0e3be798328cb3be42c0119b621d25461479a224d21/yarl-1.24.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:53e549287ef628fecba270045c9701b0c564563a9b0577d24a4ec75b8ab8040f", size = 115780 }, + { url = "https://files.pythonhosted.org/packages/58/86/1f94664e147474337e3359f52012cf3d02f825f694317b178bfba1078c62/yarl-1.24.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fcd3b77e2f17bbe4ca56ec7bcb07992647d19d0b9c05d84886dcd6f9eb810afd", size = 115308 }, + { url = "https://files.pythonhosted.org/packages/0a/43/8e55ae7538ba5f28ccb3c845c6dd4549cf7016d5992e5326512519107cdd/yarl-1.24.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d46b86567dd4e248c6c159fcbcdcce01e0a5c8a7cd2334a0fff759d0fa075b16", size = 110574 }, + { url = "https://files.pythonhosted.org/packages/ce/ba/a889ec8765cedcf2ac44dcb02d6a21e4861399b243b263c5f2dde27ee740/yarl-1.24.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f72c74aa99359e27a2ee8d6613fefa28b5f76a983c083074dfc2aaa4ab46213", size = 109914 }, + { url = "https://files.pythonhosted.org/packages/9c/c3/e45f821af67b791c2dbbe4a9f4137a1d33f8d386654a05a0c3f47bdfa25d/yarl-1.24.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f45789ce415a7ec0820dc4f82925f9b5f7732070be1dec1f5f23ec381435a24", size = 107712 }, + { url = "https://files.pythonhosted.org/packages/02/00/2ab0f42c9857fcb490bfaa6647b14540b53d241ab209f23220b958cc5832/yarl-1.24.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6e73e7fe93f17a7b191f52ec9da9dd8c06a8fe735a1ecbd13b97d1c723bff385", size = 104251 }, + { url = "https://files.pythonhosted.org/packages/7a/70/709d9a286e98af2c7fd8e4e6cada658b5c0e30d87dd7e2a63c2fb5767217/yarl-1.24.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4a36f9becdd4c5c52a20c3e9484128b070b1dcfc8944c006f3a528295a359a9c", size = 115319 }, + { url = "https://files.pythonhosted.org/packages/5c/6c/3eaa515142991fe84cfc483ff986492211f1978f90161ccefdbec919d09b/yarl-1.24.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:7bcbe0fcf850eae67b6b01749815a4f7161c560a844c769ad7b48fcd99f791c4", size = 109163 }, + { url = "https://files.pythonhosted.org/packages/bb/64/711dafce66c323a3144d470547a71c5384c57623308ac8bb5e4b903ac148/yarl-1.24.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:24e861e9630e0daddcb9191fb187f60f034e17a4426f8101279f0c475cd74144", size = 115435 }, + { url = "https://files.pythonhosted.org/packages/cf/f3/9b9d0e6d84bea851eb1ba99e4bdc755b86fd813e49ec86dfe42f26befdef/yarl-1.24.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9335a099ad87287c37fe5d1a982ff392fa5efe5d14b40a730b1ec1d6a41382b4", size = 110691 }, + { url = "https://files.pythonhosted.org/packages/86/e4/62a06b7e87c4246ac76b7c2da136f972eb4a3a1fc94abb07e7022d6fdb0a/yarl-1.24.5-cp313-cp313-win_amd64.whl", hash = "sha256:2dbe06fc16bc91502bca713704022182e5729861ae00277c3a23354b40929740", size = 97454 }, + { url = "https://files.pythonhosted.org/packages/9e/c9/5fc8025b318ab10db413b61056bd0d95c557a70e8df4210c7511f866329c/yarl-1.24.5-cp313-cp313-win_arm64.whl", hash = "sha256:6b8536851f9f65e7f00c7a1d49ba7f2be0ffe2c11555367fc9f50d9f842410a1", size = 92813 }, + { url = "https://files.pythonhosted.org/packages/61/02/962c1cbfc401a30c1d034dc67ff395f64b52302c6d62de556c1fca99acc0/yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7", size = 58612 }, ] [[package]] name = "zensical" version = "0.0.51" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "deepmerge" }, @@ -5757,52 +5845,52 @@ dependencies = [ { name = "pyyaml" }, { name = "tomli" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/f7/d07ffb268ca86afb26b7f32dbabe25dec03d3aa63ba4d876720c84681d33/zensical-0.0.51.tar.gz", hash = "sha256:de25de067bedfa18f916d7f366fd64a7fbf09bfcc615b44d1ddbe3b5fe02ab49", size = 3979640, upload-time = "2026-07-17T18:08:03.445Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/f7/d07ffb268ca86afb26b7f32dbabe25dec03d3aa63ba4d876720c84681d33/zensical-0.0.51.tar.gz", hash = "sha256:de25de067bedfa18f916d7f366fd64a7fbf09bfcc615b44d1ddbe3b5fe02ab49", size = 3979640 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/21/02db3e1fb3904016bfac310037c95b9f1eaaf0ffe7b4a84f14263a7d95df/zensical-0.0.51-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:134d776afa526098e05e34713e2f577c075e57a232e01b97842bb0206716afce", size = 12791154, upload-time = "2026-07-17T18:07:20.748Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/35/b0d96f58253514cb3d08f5779020ab01ee5472334fb984b92e3fc9e9c9ac/zensical-0.0.51-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e97ab39668ae3b452c550634e921a0336443743aae5e1fe031c7bb57d049e535", size = 12692190, upload-time = "2026-07-17T18:07:24.553Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/90/7a60e126a10c37c6b789938ff17e73fe76bba707fa029cb40ac659aeaa82/zensical-0.0.51-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c9579809f88608e7aa2cff516fff9d267d74a843cf6088a5f4227de2f092bb5", size = 13139337, upload-time = "2026-07-17T18:07:27.885Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/c3/9101c97b90d4713ef2816db03366a45ae4762efebffd296737a2dd2df325/zensical-0.0.51-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296dc7a14aa28b81a58eb57df2d5c9c9a4b0de7e90c11d99c943354287952925", size = 13069851, upload-time = "2026-07-17T18:07:31.814Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/79/0474df9e15a2c18f6281a786e10177c1b6e16feac1c568e7f36ad39b339c/zensical-0.0.51-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f779d2d87b4bf228cf2e279bc0ae6bcf3b36a9335ff283a317d01f7c15ae46b2", size = 13451083, upload-time = "2026-07-17T18:07:35.543Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/6f/91bbf78f704d5fd4c0c9be27d6bce3b6e4c2c339e4dcd6e7cf19ecda643c/zensical-0.0.51-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f813a1514a90890ca86248a8d54b81b2164bcbff11a6bcf11b01e1c01a1454", size = 13110446, upload-time = "2026-07-17T18:07:38.783Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/89/aa9a95f81771614c37bdc52b8ab21fcdef4c8de7c9cedf34e9bf62674281/zensical-0.0.51-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:186ef37e0eee0e969e2cfae47b1b97775e3164e2cba95c71faa4dd6ef47ed009", size = 13315871, upload-time = "2026-07-17T18:07:42.43Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/11/1bf6e9ded29d376f8c12644cc4de04676b010fee8caa17f682606b1f16d5/zensical-0.0.51-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5d91ce246ed930224603083cef02ae8947132fc7c52901d72015ea03526fa58", size = 13344382, upload-time = "2026-07-17T18:07:46.066Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/ec/663f16ff82d08b212e7c3236a88bd332f73331f94ddac1c91aaf882bbd1e/zensical-0.0.51-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:b1108eae82c6e8ffc33026f60b485c1512647a5333be4f547166b7c8877b98af", size = 13499628, upload-time = "2026-07-17T18:07:49.196Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/b4/7f1b6c3cf06d9f6ff5216523168a5d6ccc693444d5ceeb911eca97b30d98/zensical-0.0.51-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6fa0ecaf14f56841bfc595fa141396350c72aafbec73a016ebe3c824ed21ac72", size = 13451420, upload-time = "2026-07-17T18:07:52.563Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/44/be4bc09ec8f69e7be1b07b875887961c4e0e478b03a10d2cc624ef28fbe6/zensical-0.0.51-cp310-abi3-win32.whl", hash = "sha256:fb7ff4946b72168759c6af0a29cf5de4c38aebe633a83292e8cd4145b5213cc2", size = 12375639, upload-time = "2026-07-17T18:07:56.081Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/85/aa827c244ed4f404e99a91c3ecf5e5adb62eca806a9e9c8e3333bbad8660/zensical-0.0.51-cp310-abi3-win_amd64.whl", hash = "sha256:12529d3d3991b63820952111dc1d1edc29b2c9b3a3abb16c243bcb649631ebf2", size = 12628965, upload-time = "2026-07-17T18:07:59.741Z" }, + { url = "https://files.pythonhosted.org/packages/48/21/02db3e1fb3904016bfac310037c95b9f1eaaf0ffe7b4a84f14263a7d95df/zensical-0.0.51-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:134d776afa526098e05e34713e2f577c075e57a232e01b97842bb0206716afce", size = 12791154 }, + { url = "https://files.pythonhosted.org/packages/a2/35/b0d96f58253514cb3d08f5779020ab01ee5472334fb984b92e3fc9e9c9ac/zensical-0.0.51-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e97ab39668ae3b452c550634e921a0336443743aae5e1fe031c7bb57d049e535", size = 12692190 }, + { url = "https://files.pythonhosted.org/packages/2e/90/7a60e126a10c37c6b789938ff17e73fe76bba707fa029cb40ac659aeaa82/zensical-0.0.51-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c9579809f88608e7aa2cff516fff9d267d74a843cf6088a5f4227de2f092bb5", size = 13139337 }, + { url = "https://files.pythonhosted.org/packages/ae/c3/9101c97b90d4713ef2816db03366a45ae4762efebffd296737a2dd2df325/zensical-0.0.51-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296dc7a14aa28b81a58eb57df2d5c9c9a4b0de7e90c11d99c943354287952925", size = 13069851 }, + { url = "https://files.pythonhosted.org/packages/d2/79/0474df9e15a2c18f6281a786e10177c1b6e16feac1c568e7f36ad39b339c/zensical-0.0.51-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f779d2d87b4bf228cf2e279bc0ae6bcf3b36a9335ff283a317d01f7c15ae46b2", size = 13451083 }, + { url = "https://files.pythonhosted.org/packages/fe/6f/91bbf78f704d5fd4c0c9be27d6bce3b6e4c2c339e4dcd6e7cf19ecda643c/zensical-0.0.51-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f813a1514a90890ca86248a8d54b81b2164bcbff11a6bcf11b01e1c01a1454", size = 13110446 }, + { url = "https://files.pythonhosted.org/packages/d9/89/aa9a95f81771614c37bdc52b8ab21fcdef4c8de7c9cedf34e9bf62674281/zensical-0.0.51-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:186ef37e0eee0e969e2cfae47b1b97775e3164e2cba95c71faa4dd6ef47ed009", size = 13315871 }, + { url = "https://files.pythonhosted.org/packages/08/11/1bf6e9ded29d376f8c12644cc4de04676b010fee8caa17f682606b1f16d5/zensical-0.0.51-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5d91ce246ed930224603083cef02ae8947132fc7c52901d72015ea03526fa58", size = 13344382 }, + { url = "https://files.pythonhosted.org/packages/d6/ec/663f16ff82d08b212e7c3236a88bd332f73331f94ddac1c91aaf882bbd1e/zensical-0.0.51-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:b1108eae82c6e8ffc33026f60b485c1512647a5333be4f547166b7c8877b98af", size = 13499628 }, + { url = "https://files.pythonhosted.org/packages/60/b4/7f1b6c3cf06d9f6ff5216523168a5d6ccc693444d5ceeb911eca97b30d98/zensical-0.0.51-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6fa0ecaf14f56841bfc595fa141396350c72aafbec73a016ebe3c824ed21ac72", size = 13451420 }, + { url = "https://files.pythonhosted.org/packages/0a/44/be4bc09ec8f69e7be1b07b875887961c4e0e478b03a10d2cc624ef28fbe6/zensical-0.0.51-cp310-abi3-win32.whl", hash = "sha256:fb7ff4946b72168759c6af0a29cf5de4c38aebe633a83292e8cd4145b5213cc2", size = 12375639 }, + { url = "https://files.pythonhosted.org/packages/1f/85/aa827c244ed4f404e99a91c3ecf5e5adb62eca806a9e9c8e3333bbad8660/zensical-0.0.51-cp310-abi3-win_amd64.whl", hash = "sha256:12529d3d3991b63820952111dc1d1edc29b2c9b3a3abb16c243bcb649631ebf2", size = 12628965 }, ] [[package]] name = "zipp" version = "4.1.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214 } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238 }, ] [[package]] name = "zstandard" version = "0.25.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735 }, + { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440 }, + { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070 }, + { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001 }, + { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120 }, + { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230 }, + { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173 }, + { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736 }, + { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368 }, + { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022 }, + { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889 }, + { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952 }, + { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054 }, + { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113 }, + { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936 }, + { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232 }, + { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671 }, ] diff --git a/agentpool.spec b/wolfharness.spec similarity index 86% rename from agentpool.spec rename to wolfharness.spec index 2aa5a14f6..576e8ee2a 100644 --- a/agentpool.spec +++ b/wolfharness.spec @@ -6,7 +6,7 @@ from PyInstaller.utils.hooks import collect_data_files, copy_metadata datas = [] # Collect package metadata for packages that use importlib.metadata metadata_packages = [ - "agentpool", + "wolfharness", "llmling-models", "pydantic-ai-slim", "genai_prices", @@ -44,19 +44,19 @@ with contextlib.suppress(Exception): a = Analysis( # noqa: F821 # pyright: ignore[reportUndefinedVariable] # ty:ignore[unresolved-reference] - ["src/agentpool/__main__.py"], + ["src/wolfharness/__main__.py"], pathex=[], binaries=[], datas=datas, hiddenimports=[ - "agentpool", - "agentpool_cli", - "agentpool_config", - "agentpool_commands", - "agentpool_storage", - "agentpool_prompts", - "agentpool_server", - "agentpool_toolsets", + "wolfharness", + "wolfharness_cli", + "wolfharness_config", + "wolfharness_commands", + "wolfharness_storage", + "wolfharness_prompts", + "wolfharness_server", + "wolfharness_toolsets", "acp", "acp.bridge", ], @@ -75,7 +75,7 @@ exe = EXE( # noqa: F821 # pyright: ignore[reportUndefinedVariable] # ty:ignor a.binaries, a.datas, [], - name="agentpool", + name="wolfharness", debug=False, bootloader_ignore_signals=False, strip=False, From 3d645a80b2c650dc2f2f3fefca0c53fd548904c9 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Thu, 6 Aug 2026 18:06:17 +0800 Subject: [PATCH 02/23] fix: override mknodes dependency-metadata to prevent stale PyPI agentpool install --- pyproject.toml | 47 ++++++++++++++++++++++--- uv.lock | 95 ++++---------------------------------------------- 2 files changed, 48 insertions(+), 94 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b26d0dc52..96deab6b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,11 +85,11 @@ dependencies = [ ] [project.urls] -"Code coverage" = "https://app.codecov.io/gh/phil65/wolfharness" -Discussions = "https://github.com/phil65/wolfharness/discussions" -Documentation = "https://phil65.github.io/wolfharness/" -Issues = "https://github.com/phil65/wolfharness/issues" -Source = "https://github.com/phil65/wolfharness" +"Code coverage" = "https://app.codecov.io/gh/Leoyzen/wolfharness" +Discussions = "https://github.com/Leoyzen/wolfharness/discussions" +Documentation = "https://leoyzen.github.io/wolfharness/" +Issues = "https://github.com/Leoyzen/wolfharness/issues" +Source = "https://github.com/Leoyzen/wolfharness" [project.scripts] acp-bridge = "acp.bridge.__main__:main" @@ -518,6 +518,43 @@ asyncio_mode = "auto" default-groups = ["dev", "lint", "docs"] constraint-dependencies = ["extism-sys<1.13.0"] +# Override mknodes metadata: it declares a dependency on the legacy `agentpool` +# PyPI package, which pulls in outdated code that conflicts with the renamed +# `wolfharness` project. Redirect that dependency to `wolfharness` so uv resolves +# the local workspace instead of downloading the stale PyPI release. +[[tool.uv.dependency-metadata]] +name = "mknodes" +version = "2.2.14" +requires-dist = [ + "wolfharness", + "anybadge", + "anyenv[httpx]", + "clinspector", + "coloraide", + "epregistry", + "fieldz", + "fsspec", + "git-changelog", + "githarbor", + "gitpython", + "griffelib", + "jinja2", + "jinjarope[icons]", + "mkdocstrings[python]", + "natsort", + "pipdeptree", + "pydantic", + "pytest-asyncio", + "python-dotenv", + "setuptools", + "tomli-w", + "typer", + "universal-pathlib", + "upathtools[httpx]", + "yamling", + "zensical", +] + [tool.uv.build-backend] module-name = [ "wolfharness", diff --git a/uv.lock b/uv.lock index 2889b0667..3e09a8646 100644 --- a/uv.lock +++ b/uv.lock @@ -9,6 +9,11 @@ resolution-markers = [ [manifest] constraints = [{ name = "extism-sys", specifier = "<1.13.0" }] +[[manifest.dependency-metadata]] +name = "mknodes" +version = "2.2.14" +requires-dist = ["wolfharness", "anybadge", "anyenv[httpx]", "clinspector", "coloraide", "epregistry", "fieldz", "fsspec", "git-changelog", "githarbor", "gitpython", "griffelib", "jinja2", "jinjarope[icons]", "mkdocstrings[python]", "natsort", "pipdeptree", "pydantic", "pytest-asyncio", "python-dotenv", "setuptools", "tomli-w", "typer", "universal-pathlib", "upathtools[httpx]", "yamling", "zensical"] + [[package]] name = "ag-ui-protocol" version = "0.1.19" @@ -21,61 +26,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4c/0a/bcad8116eb058e4b4a305e3fc37ebd7efc879deeb86b854f1c5b8b6e97dd/ag_ui_protocol-0.1.19-py3-none-any.whl", hash = "sha256:898843b1410d378824da0c6a776486288b9c5828689d0bf563118868e37f390f", size = 13490 }, ] -[[package]] -name = "agentpool" -version = "2.9.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alembic" }, - { name = "anyenv", extra = ["httpx"] }, - { name = "clawd-code-sdk" }, - { name = "docler" }, - { name = "docstring-parser" }, - { name = "epregistry" }, - { name = "evented" }, - { name = "exxec" }, - { name = "fastapi" }, - { name = "fastmcp" }, - { name = "fsspec" }, - { name = "httpx" }, - { name = "jinja2" }, - { name = "jinjarope" }, - { name = "keyring" }, - { name = "llmling-models" }, - { name = "logfire", extra = ["fastapi"] }, - { name = "mcp" }, - { name = "pillow" }, - { name = "platformdirs" }, - { name = "promptantic" }, - { name = "psygnal" }, - { name = "py-key-value-aio", extra = ["disk"] }, - { name = "pydantic" }, - { name = "pydantic-ai-slim", extra = ["anthropic", "google", "mistral", "openai", "retries"] }, - { name = "pydocket" }, - { name = "python-dotenv" }, - { name = "rich" }, - { name = "ripgrep-rs" }, - { name = "schemez", extra = ["codegen"] }, - { name = "searchly", extra = ["all"] }, - { name = "slashed" }, - { name = "sqlalchemy", extra = ["aiosqlite"] }, - { name = "sqlmodel" }, - { name = "structlog" }, - { name = "sublime-search" }, - { name = "tokonomics" }, - { name = "toprompt" }, - { name = "typer" }, - { name = "upathtools", extra = ["httpx"] }, - { name = "uvicorn", extra = ["standard"] }, - { name = "watchfiles" }, - { name = "websockets" }, - { name = "yamling" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6c/5a/e858b0724d7637993d4a8c8a901d5c0e2611ee01d62222a55e97c96d79cf/agentpool-2.9.5.tar.gz", hash = "sha256:e3c60adbd77ea56c7639c533aba4ef3ec871730f807b55b5b693911c48f85121", size = 3858635 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/7c/690b675a468b10e09614ddc64944182c43f58828ae5adef1790e4dbfc14d/agentpool-2.9.5-py3-none-any.whl", hash = "sha256:81c5ae37e30e46e064b35ec73fb3233aaa0eeb547011c1a7f9a2914b48b27257", size = 4170375 }, -] - [[package]] name = "aiofile" version = "3.11.1" @@ -734,23 +684,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/93/342cc62a70ab727e093ed98e02a725d85b746345f05d2b5e5034649f4ec8/chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443", size = 11595 }, ] -[[package]] -name = "clawd-code-sdk" -version = "1.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anthropic" }, - { name = "anyenv" }, - { name = "anyio" }, - { name = "logfire" }, - { name = "mcp" }, - { name = "python-dotenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/b7/158079d9c76f2247eb934c4792a6fefbd10069326d4b435d69613a92044f/clawd_code_sdk-1.1.6.tar.gz", hash = "sha256:e85b834bba7efde466d8f82c873004fbd92debb6657c1ed49360c196d6dd4fea", size = 198815 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/23/2ad1586d809669f7161365b8aa639b4a7fbf3d4635f1e9fe5f95352b2cc5/clawd_code_sdk-1.1.6-py3-none-any.whl", hash = "sha256:b5dec10cbe8bd09475b05cbce985874e2f4ab5f3b614f3a46cf0c109cfc09721", size = 159138 }, -] - [[package]] name = "click" version = "8.4.2" @@ -2208,22 +2141,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/3a/d68cb2b334d53fd30fac81d3a489ce4ba0d9506f4df43fcf676b68352b19/librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566", size = 112127 }, ] -[[package]] -name = "llmling-models" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyenv", extra = ["httpx"] }, - { name = "pydantic" }, - { name = "pydantic-ai-slim" }, - { name = "schemez" }, - { name = "tokonomics" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/17/eb/0730ef45f5442da4ac1866e8969678ce2d96551474f8dc66fe85f47d703c/llmling_models-1.6.1.tar.gz", hash = "sha256:6f73ead1d1bdc518531c0af0756cd7e4862f176e6a15d1514a1ad299211d2908", size = 90738 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/54/90dc8ef416f24b138ebbe579548dd2940f6cdae9f93db0ce0f1c9e920524/llmling_models-1.6.1-py3-none-any.whl", hash = "sha256:94bfd7608b39b324ca2d40a1a07c1a623990ea6c81213deef327012c48dde147", size = 130926 }, -] - [[package]] name = "logfire" version = "4.38.0" @@ -2676,7 +2593,6 @@ name = "mknodes" version = "2.2.14" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "agentpool" }, { name = "anybadge" }, { name = "anyenv", extra = ["httpx"] }, { name = "clinspector" }, @@ -2701,6 +2617,7 @@ dependencies = [ { name = "typer" }, { name = "universal-pathlib" }, { name = "upathtools", extra = ["httpx"] }, + { name = "wolfharness" }, { name = "yamling" }, { name = "zensical" }, ] From e418207805a824c3f8499a07036b3b4c6d256485 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 10:07:44 +0800 Subject: [PATCH 03/23] fix(opencode): add /experimental/capabilities and workspace/status routes OpenCode attach clients call GET /experimental/capabilities and GET /experimental/workspace/status on startup. Both routes were missing, causing 404 (capabilities) and 500 (workspace/status, where the missing route was masked by an OpenTelemetry FastAPI middleware crash on Starlette 1.4 _IncludedRouter). Add: - OpenCodeCapabilities model serializing backgroundSubagents=false - WorkspaceEventConnectionStatus model for the empty status array - Routes returning the OpenCode-compatible shapes Verified: attach endpoint sequence is now all-200, ruff/mypy clean, 1012 opencode server tests pass. --- .../opencode_server/models/__init__.py | 4 +++ .../opencode_server/models/agent.py | 24 ++++++++++++++ .../opencode_server/routes/agent_routes.py | 32 +++++++++++++++++++ .../test_workspace_compat_routes.py | 28 ++++++++++++++++ 4 files changed, 88 insertions(+) diff --git a/src/wolfharness_server/opencode_server/models/__init__.py b/src/wolfharness_server/opencode_server/models/__init__.py index a0d8f0b0c..b8d9800be 100644 --- a/src/wolfharness_server/opencode_server/models/__init__.py +++ b/src/wolfharness_server/opencode_server/models/__init__.py @@ -130,6 +130,7 @@ Agent, AuthInfo, Command, + OpenCodeCapabilities, ProviderAuthAuthorization, ProviderAuthMethod, SkillInfo, @@ -138,6 +139,7 @@ WorktreeRemoveRequest, WorktreeResetRequest, WorkspaceCreateRequest, + WorkspaceEventConnectionStatus, WorkspaceInfo, ) from wolfharness_server.opencode_server.models.diagnostics import ( @@ -289,6 +291,7 @@ "ModelLimit", "ModelRef", "OpenCodeBaseModel", + "OpenCodeCapabilities", "OpenCodePendingPermission", "OpenCodePendingQuestion", "OutputFormat", @@ -409,6 +412,7 @@ "VcsBranchUpdatedEvent", "VcsInfo", "WorkspaceCreateRequest", + "WorkspaceEventConnectionStatus", "WorkspaceInfo", "WorktreeCreateRequest", "WorktreeInfo", diff --git a/src/wolfharness_server/opencode_server/models/agent.py b/src/wolfharness_server/opencode_server/models/agent.py index 102847d65..ab3dd7524 100644 --- a/src/wolfharness_server/opencode_server/models/agent.py +++ b/src/wolfharness_server/opencode_server/models/agent.py @@ -181,6 +181,30 @@ class WorkspaceCreateRequest(OpenCodeBaseModel): """Backend-specific metadata.""" +class WorkspaceEventConnectionStatus(OpenCodeBaseModel): + """Connection status for a workspace. + + Matches OpenCode SDK's ``WorkspaceEventConnectionStatus`` type used by + ``/experimental/workspace/status``. + """ + + workspace_id: str + """Workspace identifier (serialized as ``workspaceID``).""" + + status: Literal["connected", "connecting", "disconnected", "error"] + """Connection status of the workspace event stream.""" + + +class OpenCodeCapabilities(OpenCodeBaseModel): + """Experimental server capabilities for OpenCode TUI. + + Matches the body OpenCode expects from ``/experimental/capabilities``. + """ + + background_subagents: bool + """Whether the server supports background subagents.""" + + class AuthInfo(OpenCodeBaseModel): """Authentication credential info.""" diff --git a/src/wolfharness_server/opencode_server/routes/agent_routes.py b/src/wolfharness_server/opencode_server/routes/agent_routes.py index 07184542b..5f80afe19 100644 --- a/src/wolfharness_server/opencode_server/routes/agent_routes.py +++ b/src/wolfharness_server/opencode_server/routes/agent_routes.py @@ -30,11 +30,13 @@ McpAuthorizationResponse, McpResource, MCPStatus, + OpenCodeCapabilities, ProviderAuthAuthorization, ProviderAuthMethod, Session, SkillInfo, WorkspaceCreateRequest, + WorkspaceEventConnectionStatus, WorkspaceInfo, WorktreeCreateRequest, WorktreeInfo, @@ -636,6 +638,36 @@ async def list_workspaces(state: StateDep) -> list[WorkspaceInfo]: ] +@router.get("/experimental/workspace/status") +async def get_workspace_status( + state: StateDep, + directory: str | None = None, + workspace: str | None = None, +) -> list[WorkspaceEventConnectionStatus]: + """Return workspace event-stream connection statuses. + + OpenCode SDK expects ``Array``. We do not + maintain per-workspace event-stream connections, so the result is empty. + + Args: + state: Server state (kept for request context compatibility). + directory: Optional directory filter (unused, kept for contract). + workspace: Optional workspace filter (unused, kept for contract). + """ + _ = directory, workspace, state + return [] + + +@router.get("/experimental/capabilities") +async def get_capabilities() -> OpenCodeCapabilities: + """Return experimental server capabilities for the OpenCode client. + + OpenCode requires the ``backgroundSubagents`` field. We do not yet support + background subagents, so this is reported as ``false``. + """ + return OpenCodeCapabilities(background_subagents=False) + + @router.post("/experimental/workspace") async def create_workspace(request: WorkspaceCreateRequest, state: StateDep) -> WorkspaceInfo: """Create a worktree-backed workspace through OpenCode's workspace API.""" diff --git a/tests/servers/opencode_server/test_workspace_compat_routes.py b/tests/servers/opencode_server/test_workspace_compat_routes.py index 8cb7fbae5..62e108383 100644 --- a/tests/servers/opencode_server/test_workspace_compat_routes.py +++ b/tests/servers/opencode_server/test_workspace_compat_routes.py @@ -20,3 +20,31 @@ def test_list_workspaces_returns_local_response(client: TestClient) -> None: assert response.status_code == 200 assert response.json() == [] + + +def test_get_experimental_capabilities(client: TestClient) -> None: + """GET /experimental/capabilities reports no background-subagent support. + + OpenCode requires the response body to include the ``backgroundSubagents`` + field with a boolean value. + """ + response = client.get("/experimental/capabilities") + + assert response.status_code == 200 + assert response.json() == {"backgroundSubagents": False} + + +def test_get_workspace_status_returns_empty_array(client: TestClient) -> None: + """GET /experimental/workspace/status returns an empty status array. + + We do not maintain per-workspace event-stream connections, so the result + is always an empty list matching OpenCode SDK's + ``Array`` contract. + """ + response = client.get( + "/experimental/workspace/status", + params={"directory": "src", "workspace": "worktree-foo"}, + ) + + assert response.status_code == 200 + assert response.json() == [] From 5ea6cb649b354e4f4617de36c6c9d7e22521eb10 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 10:18:41 +0800 Subject: [PATCH 04/23] fix(opencode): guard OTel FastAPI route-details against _IncludedRouter FastAPI >= 0.136 wraps sub-routers added via include_router in _IncludedRouter nodes that carry no .path attribute. OTel's _get_route_details reads route.path in its Match.PARTIAL branch without guarding, so a partial match (e.g. POST against a GET route like /session/{id}/message) raises AttributeError mid-request and surfaces as a 500, masking the real endpoint result. Add otel_fastapi_patch module reproducing OTel's helper with a guarded PARTIAL branch (falls back to scope['path']), and apply it before logfire.instrument_fastapi in the OpenCode server. Verified: unpatched wrong-method POST crashes to 500, patched returns 405/200 correctly. --- .../opencode_server/otel_fastapi_patch.py | 70 +++++++++ .../opencode_server/server.py | 9 ++ .../test_otel_fastapi_patch.py | 143 ++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 src/wolfharness_server/opencode_server/otel_fastapi_patch.py create mode 100644 tests/servers/opencode_server/test_otel_fastapi_patch.py diff --git a/src/wolfharness_server/opencode_server/otel_fastapi_patch.py b/src/wolfharness_server/opencode_server/otel_fastapi_patch.py new file mode 100644 index 000000000..011c598f7 --- /dev/null +++ b/src/wolfharness_server/opencode_server/otel_fastapi_patch.py @@ -0,0 +1,70 @@ +"""Workaround for OpenTelemetry FastAPI instrumentation with FastAPI >= 0.136. + +FastAPI >= 0.136 wraps sub-routers added via ``include_router`` in an +``_IncludedRouter`` wrapper. These wrappers are matchable base routes that +intentionally carry no ``path`` attribute. + +``opentelemetry.instrumentation.fastapi._get_route_details`` walks +``app.routes`` and reads ``route.path``. Its ``Match.FULL`` branch already +guards the missing-``path`` case with try/except, but the ``Match.PARTIAL`` +branch does not — so any request that only *partially* matches a +lazily-included route (for example a dynamic path like ``/session/{id}/message`` +inside an included router) raises ``AttributeError`` and turns into a 500. + +This module replaces the module-level ``_get_route_details`` helper in +``opentelemetry.instrumentation.fastapi`` with a guarded copy. +``_get_default_span_details`` resolves it as a module global on every call, so +the reassignment takes effect without re-instrumenting the app. +""" + +from __future__ import annotations + +from typing import Any + +from starlette.routing import Match, Route + + +def _safe_get_route_details(scope: dict[str, Any]) -> str | None: + """Route-details resolver matching OTel's with a guarded PARTIAL branch. + + Identical to the OTel implementation except the ``Match.PARTIAL`` branch + falls back to ``scope["path"]`` when the matched route has no ``path`` + attribute (FastAPI >= 0.136 ``_IncludedRouter`` wrappers). + """ + app = scope["app"] + route: str | None = None + + for starlette_route in app.routes: + match, _ = ( + Route.matches(starlette_route, scope) + if isinstance(starlette_route, Route) + else starlette_route.matches(scope) + ) + if match == Match.FULL: + try: + route = starlette_route.path + except AttributeError: + # routes added via host routing won't have a path attribute + route = scope.get("path") + break + if match == Match.PARTIAL: + try: + route = starlette_route.path + except AttributeError: + # FastAPI >= 0.136 ``_IncludedRouter`` wrappers carry no path. + route = scope.get("path") + return route + + +def patch_otel_fastapi_route_details() -> None: + """Replace OTel's ``_get_route_details`` with the guarded version. + + Idempotent: re-applying ends with the OTel module pointing at our guarded + function either way. Safe to call before ``logfire.instrument_fastapi``. + """ + import opentelemetry.instrumentation.fastapi as otel_fastapi + + otel_fastapi.__dict__["_get_route_details"] = _safe_get_route_details + + +__all__ = ["patch_otel_fastapi_route_details"] diff --git a/src/wolfharness_server/opencode_server/server.py b/src/wolfharness_server/opencode_server/server.py index 3a25686e8..c1b18fd29 100644 --- a/src/wolfharness_server/opencode_server/server.py +++ b/src/wolfharness_server/opencode_server/server.py @@ -572,6 +572,15 @@ async def proxy_web_ui(request: Request, path: str) -> Response: if os.environ.get("LOGFIRE_DISABLE", "").lower() != "true": try: + from wolfharness_server.opencode_server.otel_fastapi_patch import ( + patch_otel_fastapi_route_details, + ) + + # FastAPI >= 0.136 wraps included routers in _IncludedRouter nodes + # without a `.path` attribute; OTel's _get_route_details crashes on + # these for partial matches (e.g. /session/{id}/message), turning + # valid requests into 500s. Guard before instrumenting. + patch_otel_fastapi_route_details() logfire.instrument_fastapi(app) except Exception: logger.warning("Failed to instrument FastAPI app with Logfire", exc_info=True) diff --git a/tests/servers/opencode_server/test_otel_fastapi_patch.py b/tests/servers/opencode_server/test_otel_fastapi_patch.py new file mode 100644 index 000000000..019b0963c --- /dev/null +++ b/tests/servers/opencode_server/test_otel_fastapi_patch.py @@ -0,0 +1,143 @@ +"""Tests for the OpenTelemetry FastAPI route-details patch. + +FastAPI >= 0.136 wraps ``include_router`` sub-routers in ``_IncludedRouter`` +nodes that carry no ``path`` attribute. OTel's ``_get_route_details`` reads +``route.path`` in its ``Match.PARTIAL`` branch without guarding, crashing on +dynamic routes (e.g. ``/session/{id}/message``) and turning valid requests into +500s. ``_safe_get_route_details`` guards that branch. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +import pytest +from starlette.routing import Match, Route + +from wolfharness_server.opencode_server.otel_fastapi_patch import ( + _safe_get_route_details, + patch_otel_fastapi_route_details, +) + + +pytestmark = pytest.mark.unit + + +if TYPE_CHECKING: + from fastapi import FastAPI + + +def _app_with_included_router() -> FastAPI: + """Build a FastAPI app with a dynamic route inside an included router.""" + from fastapi import APIRouter, FastAPI + + sub = APIRouter() + + @sub.get("/session/{session_id}/message") + async def session_message(session_id: str) -> dict[str, str]: + return {"ok": session_id} + + app = FastAPI() + app.include_router(sub) + return app + + +def _scope_for(app: FastAPI, path: str) -> dict[str, Any]: + """Build a minimal ASGI scope for an HTTP request to ``path``.""" + return { + "type": "http", + "app": app, + "method": "POST", + "path": path, + "raw_path": path.encode(), + "root_path": "", + "headers": [], + "query_string": b"", + "client": ("127.0.0.1", 12345), + "server": ("test", 80), + "scheme": "http", + } + + +def test_included_router_entries_have_no_path() -> None: + """Sanity: FastAPI >= 0.136 wraps include_router routes in _IncludedRouter.""" + app = _app_with_included_router() + + pathless = [r for r in app.routes if not hasattr(r, "path")] + assert pathless, "expected at least one _IncludedRouter entry without .path" + assert all(type(r).__name__ == "_IncludedRouter" for r in pathless), ( + "pathless routes should be _IncludedRouter wrappers" + ) + + +def test_original_otel_route_details_crashes_on_partial_match() -> None: + """The unpatched OTel helper crashes on a partial-match pathless route. + + Guards the fix: this proves the bug exists and the patch is necessary. + + A partial match against an ``_IncludedRouter`` wrapper happens when the + request method differs from the route's (e.g. POST against a GET route): + the path-prefix matches (``Match.PARTIAL``) but the wrapper has no ``path`` + attribute, so OTel's unguarded ``route.path`` access raises. + """ + from fastapi import APIRouter, FastAPI + + verify_app = FastAPI() + sub = APIRouter() + + @sub.get("/session/{session_id}/message") + async def sm(session_id: str) -> dict[str, str]: + return {"ok": session_id} + + verify_app.include_router(sub) + + # POST against a GET-only route inside an included router → PARTIAL match. + partial_routes = [] + for route in verify_app.routes: + if isinstance(route, Route): + continue # skip explicit Route objects (they carry .path) + match, _ = route.matches({ + "path": "/session/ses_x/message", + "path_params": {}, + "root_path": "", + "method": "POST", + "type": "http", + }) + if match == Match.PARTIAL: + partial_routes.append(route) + + assert partial_routes, "expected a partial match against _IncludedRouter" + for route in partial_routes: + with pytest.raises(AttributeError): + object.__getattribute__(route, "path") + + # The guarded helper must resolve the same POST without crashing. + resolved_route = _safe_get_route_details(_scope_for(verify_app, "/session/ses_x/message")) + assert resolved_route is not None + + +def test_safe_route_details_handles_pathless_included_router() -> None: + """The patched helper falls back to scope path, never crashing.""" + from fastapi import APIRouter, FastAPI + + app = FastAPI() + sub = APIRouter() + + @sub.post("/session/{session_id}/message") + async def sm(session_id: str) -> dict[str, str]: + return {"ok": session_id} + + app.include_router(sub) + + resolved_route = _safe_get_route_details(_scope_for(app, "/session/ses_x/message")) + assert resolved_route is not None + + +def test_patch_is_idempotent_and_effective() -> None: + """patch_otel_fastapi_route_details rewires the OTel module global.""" + import opentelemetry.instrumentation.fastapi as otel_fastapi + + patch_otel_fastapi_route_details() + patch_otel_fastapi_route_details() + + assert otel_fastapi._get_route_details is _safe_get_route_details From c1fb0c5971e24f3c720eccbcb016ed1f33cdd557 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 10:47:35 +0800 Subject: [PATCH 05/23] fix(opencode): resolve OTel _IncludedRouter crash via upstream fix Replace the runtime monkeypatch (otel_fastapi_patch) with the upstream OpenTelemetry fix: bump opentelemetry-instrumentation-fastapi to >=0.64b0, which flattens FastAPI >=0.136 _IncludedRouter wrappers so partial-match requests (e.g. POST against a GET route like /session/{id}/message) no longer crash with AttributeError in _get_route_details. mistralai pins opentelemetry-semantic-conventions <0.61, but OTel 0.64b0 requires ==0.64b0 (metadata-only; mistralai only imports stable attr constants). Override mistralai's metadata via [[tool.uv.dependency-metadata]] so the resolver is satisfiable. Verified: ruff/mypy clean, 1012 opencode server tests pass, partial-match 500 -> 405 with both worktree (0.64b0) and wolfharness-test (0.65b0). --- pyproject.toml | 21 +++ .../opencode_server/otel_fastapi_patch.py | 70 --------- .../opencode_server/server.py | 9 -- .../test_otel_fastapi_patch.py | 143 ------------------ uv.lock | 95 +++++------- 5 files changed, 61 insertions(+), 277 deletions(-) delete mode 100644 src/wolfharness_server/opencode_server/otel_fastapi_patch.py delete mode 100644 tests/servers/opencode_server/test_otel_fastapi_patch.py diff --git a/pyproject.toml b/pyproject.toml index 96deab6b2..072adca17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ dependencies = [ "keyring>=25.6.0", "logfire[fastapi]", "mcp>=1.2.0", + "opentelemetry-instrumentation-fastapi>=0.64b0", "pillow>=11.3.0", "platformdirs", "promptantic>=0.4.5", @@ -555,6 +556,26 @@ requires-dist = [ "zensical", ] +# Override mistralai's outdated opentelemetry-semantic-conventions pin +# (>=0.60b1,<0.61). It only imports stable attr constants from +# opentelemetry.semconv.attributes; the constraint blocks the OTel +# instrumentation upgrade (>=0.64b0) that fixes the FastAPI _IncludedRouter +# crash. The downgrade-name for the pin is metadata-only; the code is +# compatible across 0.60-0.65 betas. +[[tool.uv.dependency-metadata]] +name = "mistralai" +version = "2.7.2" +requires-dist = [ + "eval-type-backport", + "httpx", + "jsonpath-python", + "opentelemetry-api<2.0.0,>=1.33.1", + "opentelemetry-semantic-conventions>=0.60b1", + "pydantic", + "python-dateutil", + "typing-inspection", +] + [tool.uv.build-backend] module-name = [ "wolfharness", diff --git a/src/wolfharness_server/opencode_server/otel_fastapi_patch.py b/src/wolfharness_server/opencode_server/otel_fastapi_patch.py deleted file mode 100644 index 011c598f7..000000000 --- a/src/wolfharness_server/opencode_server/otel_fastapi_patch.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Workaround for OpenTelemetry FastAPI instrumentation with FastAPI >= 0.136. - -FastAPI >= 0.136 wraps sub-routers added via ``include_router`` in an -``_IncludedRouter`` wrapper. These wrappers are matchable base routes that -intentionally carry no ``path`` attribute. - -``opentelemetry.instrumentation.fastapi._get_route_details`` walks -``app.routes`` and reads ``route.path``. Its ``Match.FULL`` branch already -guards the missing-``path`` case with try/except, but the ``Match.PARTIAL`` -branch does not — so any request that only *partially* matches a -lazily-included route (for example a dynamic path like ``/session/{id}/message`` -inside an included router) raises ``AttributeError`` and turns into a 500. - -This module replaces the module-level ``_get_route_details`` helper in -``opentelemetry.instrumentation.fastapi`` with a guarded copy. -``_get_default_span_details`` resolves it as a module global on every call, so -the reassignment takes effect without re-instrumenting the app. -""" - -from __future__ import annotations - -from typing import Any - -from starlette.routing import Match, Route - - -def _safe_get_route_details(scope: dict[str, Any]) -> str | None: - """Route-details resolver matching OTel's with a guarded PARTIAL branch. - - Identical to the OTel implementation except the ``Match.PARTIAL`` branch - falls back to ``scope["path"]`` when the matched route has no ``path`` - attribute (FastAPI >= 0.136 ``_IncludedRouter`` wrappers). - """ - app = scope["app"] - route: str | None = None - - for starlette_route in app.routes: - match, _ = ( - Route.matches(starlette_route, scope) - if isinstance(starlette_route, Route) - else starlette_route.matches(scope) - ) - if match == Match.FULL: - try: - route = starlette_route.path - except AttributeError: - # routes added via host routing won't have a path attribute - route = scope.get("path") - break - if match == Match.PARTIAL: - try: - route = starlette_route.path - except AttributeError: - # FastAPI >= 0.136 ``_IncludedRouter`` wrappers carry no path. - route = scope.get("path") - return route - - -def patch_otel_fastapi_route_details() -> None: - """Replace OTel's ``_get_route_details`` with the guarded version. - - Idempotent: re-applying ends with the OTel module pointing at our guarded - function either way. Safe to call before ``logfire.instrument_fastapi``. - """ - import opentelemetry.instrumentation.fastapi as otel_fastapi - - otel_fastapi.__dict__["_get_route_details"] = _safe_get_route_details - - -__all__ = ["patch_otel_fastapi_route_details"] diff --git a/src/wolfharness_server/opencode_server/server.py b/src/wolfharness_server/opencode_server/server.py index c1b18fd29..3a25686e8 100644 --- a/src/wolfharness_server/opencode_server/server.py +++ b/src/wolfharness_server/opencode_server/server.py @@ -572,15 +572,6 @@ async def proxy_web_ui(request: Request, path: str) -> Response: if os.environ.get("LOGFIRE_DISABLE", "").lower() != "true": try: - from wolfharness_server.opencode_server.otel_fastapi_patch import ( - patch_otel_fastapi_route_details, - ) - - # FastAPI >= 0.136 wraps included routers in _IncludedRouter nodes - # without a `.path` attribute; OTel's _get_route_details crashes on - # these for partial matches (e.g. /session/{id}/message), turning - # valid requests into 500s. Guard before instrumenting. - patch_otel_fastapi_route_details() logfire.instrument_fastapi(app) except Exception: logger.warning("Failed to instrument FastAPI app with Logfire", exc_info=True) diff --git a/tests/servers/opencode_server/test_otel_fastapi_patch.py b/tests/servers/opencode_server/test_otel_fastapi_patch.py deleted file mode 100644 index 019b0963c..000000000 --- a/tests/servers/opencode_server/test_otel_fastapi_patch.py +++ /dev/null @@ -1,143 +0,0 @@ -"""Tests for the OpenTelemetry FastAPI route-details patch. - -FastAPI >= 0.136 wraps ``include_router`` sub-routers in ``_IncludedRouter`` -nodes that carry no ``path`` attribute. OTel's ``_get_route_details`` reads -``route.path`` in its ``Match.PARTIAL`` branch without guarding, crashing on -dynamic routes (e.g. ``/session/{id}/message``) and turning valid requests into -500s. ``_safe_get_route_details`` guards that branch. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any - -import pytest -from starlette.routing import Match, Route - -from wolfharness_server.opencode_server.otel_fastapi_patch import ( - _safe_get_route_details, - patch_otel_fastapi_route_details, -) - - -pytestmark = pytest.mark.unit - - -if TYPE_CHECKING: - from fastapi import FastAPI - - -def _app_with_included_router() -> FastAPI: - """Build a FastAPI app with a dynamic route inside an included router.""" - from fastapi import APIRouter, FastAPI - - sub = APIRouter() - - @sub.get("/session/{session_id}/message") - async def session_message(session_id: str) -> dict[str, str]: - return {"ok": session_id} - - app = FastAPI() - app.include_router(sub) - return app - - -def _scope_for(app: FastAPI, path: str) -> dict[str, Any]: - """Build a minimal ASGI scope for an HTTP request to ``path``.""" - return { - "type": "http", - "app": app, - "method": "POST", - "path": path, - "raw_path": path.encode(), - "root_path": "", - "headers": [], - "query_string": b"", - "client": ("127.0.0.1", 12345), - "server": ("test", 80), - "scheme": "http", - } - - -def test_included_router_entries_have_no_path() -> None: - """Sanity: FastAPI >= 0.136 wraps include_router routes in _IncludedRouter.""" - app = _app_with_included_router() - - pathless = [r for r in app.routes if not hasattr(r, "path")] - assert pathless, "expected at least one _IncludedRouter entry without .path" - assert all(type(r).__name__ == "_IncludedRouter" for r in pathless), ( - "pathless routes should be _IncludedRouter wrappers" - ) - - -def test_original_otel_route_details_crashes_on_partial_match() -> None: - """The unpatched OTel helper crashes on a partial-match pathless route. - - Guards the fix: this proves the bug exists and the patch is necessary. - - A partial match against an ``_IncludedRouter`` wrapper happens when the - request method differs from the route's (e.g. POST against a GET route): - the path-prefix matches (``Match.PARTIAL``) but the wrapper has no ``path`` - attribute, so OTel's unguarded ``route.path`` access raises. - """ - from fastapi import APIRouter, FastAPI - - verify_app = FastAPI() - sub = APIRouter() - - @sub.get("/session/{session_id}/message") - async def sm(session_id: str) -> dict[str, str]: - return {"ok": session_id} - - verify_app.include_router(sub) - - # POST against a GET-only route inside an included router → PARTIAL match. - partial_routes = [] - for route in verify_app.routes: - if isinstance(route, Route): - continue # skip explicit Route objects (they carry .path) - match, _ = route.matches({ - "path": "/session/ses_x/message", - "path_params": {}, - "root_path": "", - "method": "POST", - "type": "http", - }) - if match == Match.PARTIAL: - partial_routes.append(route) - - assert partial_routes, "expected a partial match against _IncludedRouter" - for route in partial_routes: - with pytest.raises(AttributeError): - object.__getattribute__(route, "path") - - # The guarded helper must resolve the same POST without crashing. - resolved_route = _safe_get_route_details(_scope_for(verify_app, "/session/ses_x/message")) - assert resolved_route is not None - - -def test_safe_route_details_handles_pathless_included_router() -> None: - """The patched helper falls back to scope path, never crashing.""" - from fastapi import APIRouter, FastAPI - - app = FastAPI() - sub = APIRouter() - - @sub.post("/session/{session_id}/message") - async def sm(session_id: str) -> dict[str, str]: - return {"ok": session_id} - - app.include_router(sub) - - resolved_route = _safe_get_route_details(_scope_for(app, "/session/ses_x/message")) - assert resolved_route is not None - - -def test_patch_is_idempotent_and_effective() -> None: - """patch_otel_fastapi_route_details rewires the OTel module global.""" - import opentelemetry.instrumentation.fastapi as otel_fastapi - - patch_otel_fastapi_route_details() - patch_otel_fastapi_route_details() - - assert otel_fastapi._get_route_details is _safe_get_route_details diff --git a/uv.lock b/uv.lock index 3e09a8646..a6179bddc 100644 --- a/uv.lock +++ b/uv.lock @@ -9,6 +9,11 @@ resolution-markers = [ [manifest] constraints = [{ name = "extism-sys", specifier = "<1.13.0" }] +[[manifest.dependency-metadata]] +name = "mistralai" +version = "2.7.2" +requires-dist = ["eval-type-backport", "httpx", "jsonpath-python", "opentelemetry-api>=1.33.1,<2.0.0", "opentelemetry-semantic-conventions>=0.60b1", "pydantic", "python-dateutil", "typing-inspection"] + [[manifest.dependency-metadata]] name = "mknodes" version = "2.2.14" @@ -1747,18 +1752,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/13/e7725e6eb32607fd4af51ccf3edfe835826e5cdf783b4d7fdc8f459196ae/import_linter-2.13-py3-none-any.whl", hash = "sha256:c0372e7ee5e15657bc06a8e841445e13237afd738a672d26863dc927af9f0bf5", size = 638185 }, ] -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, -] - [[package]] name = "inflect" version = "7.5.0" @@ -2944,32 +2937,31 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.39.1" +version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/cc/e4c9584181f86494df0f6bdec1a4f3280c50db44704dc2a407e994fc87bb/opentelemetry_api-1.43.0.tar.gz", hash = "sha256:107d0d03857ea8fc7c5fcbbbd83f800c281f0d560553d61c1d675fccfd1761c1", size = 73476 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356 }, + { url = "https://files.pythonhosted.org/packages/17/83/6dba32b85f31868400440dc7ad2ca1eab94cbbf3a7b0459ed39f8311a9e2/opentelemetry_api-1.43.0-py3-none-any.whl", hash = "sha256:20acf45e9b21851926835292e4045d290acade1edd2ff3de86d2f069687ba1fd", size = 61912 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.39.1" +version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409 } +sdist = { url = "https://files.pythonhosted.org/packages/55/c1/e8098490ab15abf116dcaf9fa89ededcb35547c7d08d4b5a62f573dc1e63/opentelemetry_exporter_otlp_proto_common-1.43.0.tar.gz", hash = "sha256:c4e32ba6d6b13bdb2b8f6764c4fd28d00192826561aa04f6d14eedfce7ac076f", size = 20197 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366 }, + { url = "https://files.pythonhosted.org/packages/d0/b2/41ebc74ae1d5859901f1b69305de58724bf043381103d6ef413521cbc35a/opentelemetry_exporter_otlp_proto_common-1.43.0-py3-none-any.whl", hash = "sha256:123c3f9cc87218562490c63b36f497bf3a722faf174a515d1443f31ababa6264", size = 17048 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.39.1" +version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, @@ -2980,14 +2972,14 @@ dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/04/2a08fa9c0214ae38880df01e8bfae12b067ec0793446578575e5080d6545/opentelemetry_exporter_otlp_proto_http-1.39.1.tar.gz", hash = "sha256:31bdab9745c709ce90a49a0624c2bd445d31a28ba34275951a6a362d16a0b9cb", size = 17288 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/92/0b9f56412483a8891d4843890294796c9df8ab42417bd9bad8035d840cb3/opentelemetry_exporter_otlp_proto_http-1.43.0.tar.gz", hash = "sha256:fa8a42bb7d00ee5391f4c0b04d8e6a46c03caa437903296ab73a81dc11ba118f", size = 25406 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/f1/b27d3e2e003cd9a3592c43d099d2ed8d0a947c15281bf8463a256db0b46c/opentelemetry_exporter_otlp_proto_http-1.39.1-py3-none-any.whl", hash = "sha256:d9f5207183dd752a412c4cd564ca8875ececba13be6e9c6c370ffb752fd59985", size = 19641 }, + { url = "https://files.pythonhosted.org/packages/b3/20/b685ed7af2e17c29ffc8af56f1fa8bc2033258fc30fb0d2b722f49d13ba0/opentelemetry_exporter_otlp_proto_http-1.43.0-py3-none-any.whl", hash = "sha256:647f603aa8efdbdb4dbff842e0729d0406a6fff26b295a72d3d60e7d963b2610", size = 21795 }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.60b1" +version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -2995,14 +2987,14 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706 } +sdist = { url = "https://files.pythonhosted.org/packages/7e/97/02fe6e1c8b1ffac42d0b429c18080edb24e0e0d18c86612edf72b5752382/opentelemetry_instrumentation-0.64b0.tar.gz", hash = "sha256:b47d528dead6271d7743114417eb67fc915bd9258111c48dbf9a4951d2efa88d", size = 41935 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096 }, + { url = "https://files.pythonhosted.org/packages/d2/0c/cb9fe342de5299c7af24582eb7d788661cc53a1c4b904da92309caaa9417/opentelemetry_instrumentation-0.64b0-py3-none-any.whl", hash = "sha256:133ab7ffca796557aec059bf6be3190a34b6dea987f25be3d9409e230cbdad8b", size = 35880 }, ] [[package]] name = "opentelemetry-instrumentation-asgi" -version = "0.60b1" +version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, @@ -3011,14 +3003,14 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/db/851fa88db7441da82d50bd80f2de5ee55213782e25dc858e04d0c9961d60/opentelemetry_instrumentation_asgi-0.60b1.tar.gz", hash = "sha256:16bfbe595cd24cda309a957456d0fc2523f41bc7b076d1f2d7e98a1ad9876d6f", size = 26107 } +sdist = { url = "https://files.pythonhosted.org/packages/85/0c/71c696fccb86d37af383ea1604af4729fabad0af2fabaf203e4c79c1e859/opentelemetry_instrumentation_asgi-0.64b0.tar.gz", hash = "sha256:4dd3eee566a4303f8e6b9b84f2a0a7abc57a6640df768926c68a3868bf5b2090", size = 26136 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/76/1fb94367cef64420d2171157a6b9509582873bd09a6afe08a78a8d1f59d9/opentelemetry_instrumentation_asgi-0.60b1-py3-none-any.whl", hash = "sha256:d48def2dbed10294c99cfcf41ebbd0c414d390a11773a41f472d20000fcddc25", size = 16933 }, + { url = "https://files.pythonhosted.org/packages/51/20/218b65a63d847a7ed28d1bea84c39234689160b74480b8702272e37f4240/opentelemetry_instrumentation_asgi-0.64b0-py3-none-any.whl", hash = "sha256:e0840b66e15303a9254b0540946010bd008aa0504f4d89b8e1b7fb63490a36f0", size = 15906 }, ] [[package]] name = "opentelemetry-instrumentation-fastapi" -version = "0.60b1" +version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -3027,14 +3019,14 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/e7/e7e5e50218cf488377209d85666b182fa2d4928bf52389411ceeee1b2b60/opentelemetry_instrumentation_fastapi-0.60b1.tar.gz", hash = "sha256:de608955f7ff8eecf35d056578346a5365015fd7d8623df9b1f08d1c74769c01", size = 24958 } +sdist = { url = "https://files.pythonhosted.org/packages/db/a1/52282e2cc5c08f4df13b087896d9907258fe2ff4f34035d3b21aa92684c3/opentelemetry_instrumentation_fastapi-0.64b0.tar.gz", hash = "sha256:05f75149929e433c1630de381688e650bf651c1e1cce7f9a7b649a807dac8a98", size = 26236 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/cc/6e808328ba54662e50babdcab21138eae4250bc0fddf67d55526a615a2ca/opentelemetry_instrumentation_fastapi-0.60b1-py3-none-any.whl", hash = "sha256:af94b7a239ad1085fc3a820ecf069f67f579d7faf4c085aaa7bd9b64eafc8eaf", size = 13478 }, + { url = "https://files.pythonhosted.org/packages/72/2d/4f48dc2d6f289f2febc5b871940dbea9f3b04ab9186d23342581b49cb984/opentelemetry_instrumentation_fastapi-0.64b0-py3-none-any.whl", hash = "sha256:43cbbfb2d3079dc81104478a2950ae93ac6d0e90a5020fa3987a236f8f2bdef1", size = 13263 }, ] [[package]] name = "opentelemetry-instrumentation-httpx" -version = "0.60b1" +version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -3043,57 +3035,57 @@ dependencies = [ { name = "opentelemetry-util-http" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/08/11208bcfcab4fc2023252c3f322aa397fd9ad948355fea60f5fc98648603/opentelemetry_instrumentation_httpx-0.60b1.tar.gz", hash = "sha256:a506ebaf28c60112cbe70ad4f0338f8603f148938cb7b6794ce1051cd2b270ae", size = 20611 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/2a/2893a8781b93894f1e8014904c0342da7e4302de6597c2d5c0cb6c1a552e/opentelemetry_instrumentation_httpx-0.64b0.tar.gz", hash = "sha256:c2cfcd03d3665762860ebd0c28038c6e47fbb48d7942dec31dd75fc634d25c92", size = 23555 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/59/b98e84eebf745ffc75397eaad4763795bff8a30cbf2373a50ed4e70646c5/opentelemetry_instrumentation_httpx-0.60b1-py3-none-any.whl", hash = "sha256:f37636dd742ad2af83d896ba69601ed28da51fa4e25d1ab62fde89ce413e275b", size = 15701 }, + { url = "https://files.pythonhosted.org/packages/06/29/a20309bd3f5a8051b61ca475e78623410c3b077e3deccae19aa4f8b5b9a2/opentelemetry_instrumentation_httpx-0.64b0-py3-none-any.whl", hash = "sha256:04829e5723941b5ceb0c88b44d63983e226b5c75b2b2e34a57739fdd0e060608", size = 16336 }, ] [[package]] name = "opentelemetry-proto" -version = "1.39.1" +version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/b9/d357faefb40bda1d4799913e6af611171ff22a2dedcb93576bc92242d056/opentelemetry_proto-1.43.0.tar.gz", hash = "sha256:224778df17e1f3fafeaaa21d874236ca5f6ffc2f86e0899298ec7351aac27924", size = 46481 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535 }, + { url = "https://files.pythonhosted.org/packages/ed/a7/3e5308cf548b8f72529c7db1afdb3a404211982376a12927fd7759f77bf3/opentelemetry_proto-1.43.0-py3-none-any.whl", hash = "sha256:c58f1f7ef84bc7dc2834016c0c37fe0081dde7ca9f6339be1970fbf9cdaaa90d", size = 72489 }, ] [[package]] name = "opentelemetry-sdk" -version = "1.39.1" +version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460 } +sdist = { url = "https://files.pythonhosted.org/packages/3e/eb/5041074274ac0956b03637cc039d434569112468e875eddfcc9a0674ce06/opentelemetry_sdk-1.43.0.tar.gz", hash = "sha256:d8187c81c162df9913e4003dd6485f7390d9a24fc17026ec7387b8b8218b08e9", size = 254744 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565 }, + { url = "https://files.pythonhosted.org/packages/49/e3/b17be23af124201c9f52eececd4cc8ddfed1597d37b4ee771895d325805c/opentelemetry_sdk-1.43.0-py3-none-any.whl", hash = "sha256:d1323a547c1ce69d6a069a17a44b7da82bb8b332051ecb074041f87642c86823", size = 178852 }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.60b1" +version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/30/5f26df29509eccd86b99b481ac9ffa39da49ba9577cc69071c552ae30447/opentelemetry_semantic_conventions-0.64b0.tar.gz", hash = "sha256:72f76fb2d1582d9d033dd1fcd84532e961e6ff3d90d24ba6fabc72975a83864c", size = 148340 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982 }, + { url = "https://files.pythonhosted.org/packages/f2/ca/23ba87a221b574a7c5a99d48849d80bfe8b047624681357e2b002e566187/opentelemetry_semantic_conventions-0.64b0-py3-none-any.whl", hash = "sha256:ea77e85e354b8f604ddbe5f3d9135216f982fa4d77e5859ac30f6d8a50505aa6", size = 203713 }, ] [[package]] name = "opentelemetry-util-http" -version = "0.60b1" +version = "0.64b0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/fc/c47bb04a1d8a941a4061307e1eddfa331ed4d0ab13d8a9781e6db256940a/opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6", size = 11053 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/1b/1029a805fd7242f7dfce91633b244c3b14a94d703232878f71e01ce862b1/opentelemetry_util_http-0.64b0.tar.gz", hash = "sha256:8a86a220dbfc56d736f47f1e5c4e7932a21fcf69052312e1bcf166444dc79322", size = 11102 } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/5c/d3f1733665f7cd582ef0842fb1d2ed0bc1fba10875160593342d22bba375/opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199", size = 8947 }, + { url = "https://files.pythonhosted.org/packages/1c/c7/5f8ec5b30546f2dc22cd5fc5759bce2ab5be6e89a2e710a405ac9ef64ed3/opentelemetry_util_http-0.64b0-py3-none-any.whl", hash = "sha256:c1e5350d25507c1afcd6076cf9ac062485a0a4f79cd9971366996fd3056bacdb", size = 8204 }, ] [[package]] @@ -5402,6 +5394,7 @@ dependencies = [ { name = "keyring" }, { name = "logfire", extra = ["fastapi"] }, { name = "mcp" }, + { name = "opentelemetry-instrumentation-fastapi" }, { name = "pillow" }, { name = "platformdirs" }, { name = "promptantic" }, @@ -5587,6 +5580,7 @@ requires-dist = [ { name = "markitdown", marker = "python_full_version < '3.14' and extra == 'markitdown'" }, { name = "mcp", specifier = ">=1.2.0" }, { name = "mcpx-py", marker = "extra == 'mcp-run'", specifier = ">=0.7.0" }, + { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.64b0" }, { name = "openviking-sdk", marker = "extra == 'viking'", specifier = ">=0.1.0" }, { name = "pillow", specifier = ">=11.3.0" }, { name = "platformdirs" }, @@ -5778,15 +5772,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/85/aa827c244ed4f404e99a91c3ecf5e5adb62eca806a9e9c8e3333bbad8660/zensical-0.0.51-cp310-abi3-win_amd64.whl", hash = "sha256:12529d3d3991b63820952111dc1d1edc29b2c9b3a3abb16c243bcb649631ebf2", size = 12628965 }, ] -[[package]] -name = "zipp" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238 }, -] - [[package]] name = "zstandard" version = "0.25.0" From 3000aaac40dad6c5aac749f668985a667888a666 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 10:54:51 +0800 Subject: [PATCH 06/23] fix(opencode): restore OTel patch; document why not dependency upgrade Revert the upstream version-bump approach (c1fb0c597). Upgrading opentelemetry-instrumentation-fastapi to >=0.64b0 requires opentelemetry-semantic-conventions ==0.64b0, which conflicts with the pinned mistralai (opentelemetry-semantic-conventions<0.61). Working around it via [[tool.uv.dependency-metadata]] override does not propagate to downstream projects, so every consumer (e.g. xeno-agent) would need the same override. Restore the local OTel _get_route_details patch (inside OpenCode server only) and document the full decision rationale in the module docstring so the upstream upgrade can be revisited when mistralai relaxes its pin. Verified: partial-match 500 -> 405 with patch, ruff/mypy clean, 1016 opencode server tests pass. --- pyproject.toml | 21 --- .../opencode_server/otel_fastapi_patch.py | 98 ++++++++++++ .../opencode_server/server.py | 9 ++ .../test_otel_fastapi_patch.py | 143 ++++++++++++++++++ uv.lock | 95 +++++++----- 5 files changed, 305 insertions(+), 61 deletions(-) create mode 100644 src/wolfharness_server/opencode_server/otel_fastapi_patch.py create mode 100644 tests/servers/opencode_server/test_otel_fastapi_patch.py diff --git a/pyproject.toml b/pyproject.toml index 072adca17..96deab6b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,6 @@ dependencies = [ "keyring>=25.6.0", "logfire[fastapi]", "mcp>=1.2.0", - "opentelemetry-instrumentation-fastapi>=0.64b0", "pillow>=11.3.0", "platformdirs", "promptantic>=0.4.5", @@ -556,26 +555,6 @@ requires-dist = [ "zensical", ] -# Override mistralai's outdated opentelemetry-semantic-conventions pin -# (>=0.60b1,<0.61). It only imports stable attr constants from -# opentelemetry.semconv.attributes; the constraint blocks the OTel -# instrumentation upgrade (>=0.64b0) that fixes the FastAPI _IncludedRouter -# crash. The downgrade-name for the pin is metadata-only; the code is -# compatible across 0.60-0.65 betas. -[[tool.uv.dependency-metadata]] -name = "mistralai" -version = "2.7.2" -requires-dist = [ - "eval-type-backport", - "httpx", - "jsonpath-python", - "opentelemetry-api<2.0.0,>=1.33.1", - "opentelemetry-semantic-conventions>=0.60b1", - "pydantic", - "python-dateutil", - "typing-inspection", -] - [tool.uv.build-backend] module-name = [ "wolfharness", diff --git a/src/wolfharness_server/opencode_server/otel_fastapi_patch.py b/src/wolfharness_server/opencode_server/otel_fastapi_patch.py new file mode 100644 index 000000000..5beb48693 --- /dev/null +++ b/src/wolfharness_server/opencode_server/otel_fastapi_patch.py @@ -0,0 +1,98 @@ +"""Workaround for OpenTelemetry FastAPI instrumentation with FastAPI >= 0.136. + +FastAPI >= 0.136 wraps sub-routers added via ``include_router`` in an +``_IncludedRouter`` wrapper. These wrappers are matchable base routes that +intentionally carry no ``path`` attribute. + +``opentelemetry.instrumentation.fastapi._get_route_details`` walks +``app.routes`` and reads ``route.path``. Its ``Match.FULL`` branch already +guards the missing-``path`` case with try/except, but the ``Match.PARTIAL`` +branch does not — so any request that only *partially* matches a +lazily-included route (for example a dynamic path like ``/session/{id}/message`` +inside an included router) raises ``AttributeError`` and turns into a 500. + +This module replaces the module-level ``_get_route_details`` helper in +``opentelemetry.instrumentation.fastapi`` with a guarded copy. +``_get_default_span_details`` resolves it as a module global on every call, so +the reassignment takes effect without re-instrumenting the app. + +Why a runtime patch instead of upgrading the dependency? +-------------------------------------------------------- +The upstream OpenTelemetry fix landed in ``opentelemetry-instrumentation- +fastapi>=0.64b0``, but it requires ``opentelemetry-semantic-conventions`` +pinned to the exact same ``0.6xb0`` version, i.e. ``==0.64b0``. The pinned +``mistralai`` (a transitive dep of ``pydantic-ai-slim[mistral]``) declares +``opentelemetry-semantic-conventions<0.61`` and cannot coexist with that +upgrade, so the dependency graph is unsatisfiable unless we override +``mistralai``'s metadata. + +We deliberately avoid that override (see below) and keep this patch instead: + +- ``[[tool.uv.dependency-metadata]]`` overrides DO NOT propagate to downstream + projects that depend on ``wolfharness`` (e.g. ``xeno-agent``). Every consumer + would have to repeat the same override in its own ``pyproject.toml``, which + turns a single dependency bump into a coordination burden across repositories. +- The patch is fully local to this package: it changes no dependency versions, + so all downstream projects keep working without edits. +- ``mistralai`` only imports stable attribute constants from + ``opentelemetry.semconv.attributes``; the semantic-conventions contents it + relies on are stable across the 0.60-0.65 betas. +- ``opentelemetry-instrumentation-fastapi`` is a pre-release (``0.6xb0``); the + patch is immune to beta-version churn and works on any version in range. + +Revisit the upstream upgrade (removing this module) if either condition changes: +(1) ``mistralai`` relaxes its ``opentelemetry-semantic-conventions`` pin, or +(2) ``dependency-metadata`` overrides start propagating transitively. +""" + +from __future__ import annotations + +from typing import Any + +from starlette.routing import Match, Route + + +def _safe_get_route_details(scope: dict[str, Any]) -> str | None: + """Route-details resolver matching OTel's with a guarded PARTIAL branch. + + Identical to the OTel implementation except the ``Match.PARTIAL`` branch + falls back to ``scope["path"]`` when the matched route has no ``path`` + attribute (FastAPI >= 0.136 ``_IncludedRouter`` wrappers). + """ + app = scope["app"] + route: str | None = None + + for starlette_route in app.routes: + match, _ = ( + Route.matches(starlette_route, scope) + if isinstance(starlette_route, Route) + else starlette_route.matches(scope) + ) + if match == Match.FULL: + try: + route = starlette_route.path + except AttributeError: + # routes added via host routing won't have a path attribute + route = scope.get("path") + break + if match == Match.PARTIAL: + try: + route = starlette_route.path + except AttributeError: + # FastAPI >= 0.136 ``_IncludedRouter`` wrappers carry no path. + route = scope.get("path") + return route + + +def patch_otel_fastapi_route_details() -> None: + """Replace OTel's ``_get_route_details`` with the guarded version. + + Idempotent: re-applying ends with the OTel module pointing at our guarded + function either way. Safe to call before ``logfire.instrument_fastapi``. + """ + import opentelemetry.instrumentation.fastapi as otel_fastapi + + otel_fastapi.__dict__["_get_route_details"] = _safe_get_route_details + + +__all__ = ["patch_otel_fastapi_route_details"] diff --git a/src/wolfharness_server/opencode_server/server.py b/src/wolfharness_server/opencode_server/server.py index 3a25686e8..c1b18fd29 100644 --- a/src/wolfharness_server/opencode_server/server.py +++ b/src/wolfharness_server/opencode_server/server.py @@ -572,6 +572,15 @@ async def proxy_web_ui(request: Request, path: str) -> Response: if os.environ.get("LOGFIRE_DISABLE", "").lower() != "true": try: + from wolfharness_server.opencode_server.otel_fastapi_patch import ( + patch_otel_fastapi_route_details, + ) + + # FastAPI >= 0.136 wraps included routers in _IncludedRouter nodes + # without a `.path` attribute; OTel's _get_route_details crashes on + # these for partial matches (e.g. /session/{id}/message), turning + # valid requests into 500s. Guard before instrumenting. + patch_otel_fastapi_route_details() logfire.instrument_fastapi(app) except Exception: logger.warning("Failed to instrument FastAPI app with Logfire", exc_info=True) diff --git a/tests/servers/opencode_server/test_otel_fastapi_patch.py b/tests/servers/opencode_server/test_otel_fastapi_patch.py new file mode 100644 index 000000000..019b0963c --- /dev/null +++ b/tests/servers/opencode_server/test_otel_fastapi_patch.py @@ -0,0 +1,143 @@ +"""Tests for the OpenTelemetry FastAPI route-details patch. + +FastAPI >= 0.136 wraps ``include_router`` sub-routers in ``_IncludedRouter`` +nodes that carry no ``path`` attribute. OTel's ``_get_route_details`` reads +``route.path`` in its ``Match.PARTIAL`` branch without guarding, crashing on +dynamic routes (e.g. ``/session/{id}/message``) and turning valid requests into +500s. ``_safe_get_route_details`` guards that branch. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +import pytest +from starlette.routing import Match, Route + +from wolfharness_server.opencode_server.otel_fastapi_patch import ( + _safe_get_route_details, + patch_otel_fastapi_route_details, +) + + +pytestmark = pytest.mark.unit + + +if TYPE_CHECKING: + from fastapi import FastAPI + + +def _app_with_included_router() -> FastAPI: + """Build a FastAPI app with a dynamic route inside an included router.""" + from fastapi import APIRouter, FastAPI + + sub = APIRouter() + + @sub.get("/session/{session_id}/message") + async def session_message(session_id: str) -> dict[str, str]: + return {"ok": session_id} + + app = FastAPI() + app.include_router(sub) + return app + + +def _scope_for(app: FastAPI, path: str) -> dict[str, Any]: + """Build a minimal ASGI scope for an HTTP request to ``path``.""" + return { + "type": "http", + "app": app, + "method": "POST", + "path": path, + "raw_path": path.encode(), + "root_path": "", + "headers": [], + "query_string": b"", + "client": ("127.0.0.1", 12345), + "server": ("test", 80), + "scheme": "http", + } + + +def test_included_router_entries_have_no_path() -> None: + """Sanity: FastAPI >= 0.136 wraps include_router routes in _IncludedRouter.""" + app = _app_with_included_router() + + pathless = [r for r in app.routes if not hasattr(r, "path")] + assert pathless, "expected at least one _IncludedRouter entry without .path" + assert all(type(r).__name__ == "_IncludedRouter" for r in pathless), ( + "pathless routes should be _IncludedRouter wrappers" + ) + + +def test_original_otel_route_details_crashes_on_partial_match() -> None: + """The unpatched OTel helper crashes on a partial-match pathless route. + + Guards the fix: this proves the bug exists and the patch is necessary. + + A partial match against an ``_IncludedRouter`` wrapper happens when the + request method differs from the route's (e.g. POST against a GET route): + the path-prefix matches (``Match.PARTIAL``) but the wrapper has no ``path`` + attribute, so OTel's unguarded ``route.path`` access raises. + """ + from fastapi import APIRouter, FastAPI + + verify_app = FastAPI() + sub = APIRouter() + + @sub.get("/session/{session_id}/message") + async def sm(session_id: str) -> dict[str, str]: + return {"ok": session_id} + + verify_app.include_router(sub) + + # POST against a GET-only route inside an included router → PARTIAL match. + partial_routes = [] + for route in verify_app.routes: + if isinstance(route, Route): + continue # skip explicit Route objects (they carry .path) + match, _ = route.matches({ + "path": "/session/ses_x/message", + "path_params": {}, + "root_path": "", + "method": "POST", + "type": "http", + }) + if match == Match.PARTIAL: + partial_routes.append(route) + + assert partial_routes, "expected a partial match against _IncludedRouter" + for route in partial_routes: + with pytest.raises(AttributeError): + object.__getattribute__(route, "path") + + # The guarded helper must resolve the same POST without crashing. + resolved_route = _safe_get_route_details(_scope_for(verify_app, "/session/ses_x/message")) + assert resolved_route is not None + + +def test_safe_route_details_handles_pathless_included_router() -> None: + """The patched helper falls back to scope path, never crashing.""" + from fastapi import APIRouter, FastAPI + + app = FastAPI() + sub = APIRouter() + + @sub.post("/session/{session_id}/message") + async def sm(session_id: str) -> dict[str, str]: + return {"ok": session_id} + + app.include_router(sub) + + resolved_route = _safe_get_route_details(_scope_for(app, "/session/ses_x/message")) + assert resolved_route is not None + + +def test_patch_is_idempotent_and_effective() -> None: + """patch_otel_fastapi_route_details rewires the OTel module global.""" + import opentelemetry.instrumentation.fastapi as otel_fastapi + + patch_otel_fastapi_route_details() + patch_otel_fastapi_route_details() + + assert otel_fastapi._get_route_details is _safe_get_route_details diff --git a/uv.lock b/uv.lock index a6179bddc..3e09a8646 100644 --- a/uv.lock +++ b/uv.lock @@ -9,11 +9,6 @@ resolution-markers = [ [manifest] constraints = [{ name = "extism-sys", specifier = "<1.13.0" }] -[[manifest.dependency-metadata]] -name = "mistralai" -version = "2.7.2" -requires-dist = ["eval-type-backport", "httpx", "jsonpath-python", "opentelemetry-api>=1.33.1,<2.0.0", "opentelemetry-semantic-conventions>=0.60b1", "pydantic", "python-dateutil", "typing-inspection"] - [[manifest.dependency-metadata]] name = "mknodes" version = "2.2.14" @@ -1752,6 +1747,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/13/e7725e6eb32607fd4af51ccf3edfe835826e5cdf783b4d7fdc8f459196ae/import_linter-2.13-py3-none-any.whl", hash = "sha256:c0372e7ee5e15657bc06a8e841445e13237afd738a672d26863dc927af9f0bf5", size = 638185 }, ] +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, +] + [[package]] name = "inflect" version = "7.5.0" @@ -2937,31 +2944,32 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.43.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/cc/e4c9584181f86494df0f6bdec1a4f3280c50db44704dc2a407e994fc87bb/opentelemetry_api-1.43.0.tar.gz", hash = "sha256:107d0d03857ea8fc7c5fcbbbd83f800c281f0d560553d61c1d675fccfd1761c1", size = 73476 } +sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767 } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/83/6dba32b85f31868400440dc7ad2ca1eab94cbbf3a7b0459ed39f8311a9e2/opentelemetry_api-1.43.0-py3-none-any.whl", hash = "sha256:20acf45e9b21851926835292e4045d290acade1edd2ff3de86d2f069687ba1fd", size = 61912 }, + { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.43.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/c1/e8098490ab15abf116dcaf9fa89ededcb35547c7d08d4b5a62f573dc1e63/opentelemetry_exporter_otlp_proto_common-1.43.0.tar.gz", hash = "sha256:c4e32ba6d6b13bdb2b8f6764c4fd28d00192826561aa04f6d14eedfce7ac076f", size = 20197 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/b2/41ebc74ae1d5859901f1b69305de58724bf043381103d6ef413521cbc35a/opentelemetry_exporter_otlp_proto_common-1.43.0-py3-none-any.whl", hash = "sha256:123c3f9cc87218562490c63b36f497bf3a722faf174a515d1443f31ababa6264", size = 17048 }, + { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.43.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, @@ -2972,14 +2980,14 @@ dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/92/0b9f56412483a8891d4843890294796c9df8ab42417bd9bad8035d840cb3/opentelemetry_exporter_otlp_proto_http-1.43.0.tar.gz", hash = "sha256:fa8a42bb7d00ee5391f4c0b04d8e6a46c03caa437903296ab73a81dc11ba118f", size = 25406 } +sdist = { url = "https://files.pythonhosted.org/packages/80/04/2a08fa9c0214ae38880df01e8bfae12b067ec0793446578575e5080d6545/opentelemetry_exporter_otlp_proto_http-1.39.1.tar.gz", hash = "sha256:31bdab9745c709ce90a49a0624c2bd445d31a28ba34275951a6a362d16a0b9cb", size = 17288 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/20/b685ed7af2e17c29ffc8af56f1fa8bc2033258fc30fb0d2b722f49d13ba0/opentelemetry_exporter_otlp_proto_http-1.43.0-py3-none-any.whl", hash = "sha256:647f603aa8efdbdb4dbff842e0729d0406a6fff26b295a72d3d60e7d963b2610", size = 21795 }, + { url = "https://files.pythonhosted.org/packages/95/f1/b27d3e2e003cd9a3592c43d099d2ed8d0a947c15281bf8463a256db0b46c/opentelemetry_exporter_otlp_proto_http-1.39.1-py3-none-any.whl", hash = "sha256:d9f5207183dd752a412c4cd564ca8875ececba13be6e9c6c370ffb752fd59985", size = 19641 }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.64b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -2987,14 +2995,14 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/97/02fe6e1c8b1ffac42d0b429c18080edb24e0e0d18c86612edf72b5752382/opentelemetry_instrumentation-0.64b0.tar.gz", hash = "sha256:b47d528dead6271d7743114417eb67fc915bd9258111c48dbf9a4951d2efa88d", size = 41935 } +sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/0c/cb9fe342de5299c7af24582eb7d788661cc53a1c4b904da92309caaa9417/opentelemetry_instrumentation-0.64b0-py3-none-any.whl", hash = "sha256:133ab7ffca796557aec059bf6be3190a34b6dea987f25be3d9409e230cbdad8b", size = 35880 }, + { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096 }, ] [[package]] name = "opentelemetry-instrumentation-asgi" -version = "0.64b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, @@ -3003,14 +3011,14 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/0c/71c696fccb86d37af383ea1604af4729fabad0af2fabaf203e4c79c1e859/opentelemetry_instrumentation_asgi-0.64b0.tar.gz", hash = "sha256:4dd3eee566a4303f8e6b9b84f2a0a7abc57a6640df768926c68a3868bf5b2090", size = 26136 } +sdist = { url = "https://files.pythonhosted.org/packages/77/db/851fa88db7441da82d50bd80f2de5ee55213782e25dc858e04d0c9961d60/opentelemetry_instrumentation_asgi-0.60b1.tar.gz", hash = "sha256:16bfbe595cd24cda309a957456d0fc2523f41bc7b076d1f2d7e98a1ad9876d6f", size = 26107 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/20/218b65a63d847a7ed28d1bea84c39234689160b74480b8702272e37f4240/opentelemetry_instrumentation_asgi-0.64b0-py3-none-any.whl", hash = "sha256:e0840b66e15303a9254b0540946010bd008aa0504f4d89b8e1b7fb63490a36f0", size = 15906 }, + { url = "https://files.pythonhosted.org/packages/76/76/1fb94367cef64420d2171157a6b9509582873bd09a6afe08a78a8d1f59d9/opentelemetry_instrumentation_asgi-0.60b1-py3-none-any.whl", hash = "sha256:d48def2dbed10294c99cfcf41ebbd0c414d390a11773a41f472d20000fcddc25", size = 16933 }, ] [[package]] name = "opentelemetry-instrumentation-fastapi" -version = "0.64b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -3019,14 +3027,14 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/db/a1/52282e2cc5c08f4df13b087896d9907258fe2ff4f34035d3b21aa92684c3/opentelemetry_instrumentation_fastapi-0.64b0.tar.gz", hash = "sha256:05f75149929e433c1630de381688e650bf651c1e1cce7f9a7b649a807dac8a98", size = 26236 } +sdist = { url = "https://files.pythonhosted.org/packages/9c/e7/e7e5e50218cf488377209d85666b182fa2d4928bf52389411ceeee1b2b60/opentelemetry_instrumentation_fastapi-0.60b1.tar.gz", hash = "sha256:de608955f7ff8eecf35d056578346a5365015fd7d8623df9b1f08d1c74769c01", size = 24958 } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/2d/4f48dc2d6f289f2febc5b871940dbea9f3b04ab9186d23342581b49cb984/opentelemetry_instrumentation_fastapi-0.64b0-py3-none-any.whl", hash = "sha256:43cbbfb2d3079dc81104478a2950ae93ac6d0e90a5020fa3987a236f8f2bdef1", size = 13263 }, + { url = "https://files.pythonhosted.org/packages/7d/cc/6e808328ba54662e50babdcab21138eae4250bc0fddf67d55526a615a2ca/opentelemetry_instrumentation_fastapi-0.60b1-py3-none-any.whl", hash = "sha256:af94b7a239ad1085fc3a820ecf069f67f579d7faf4c085aaa7bd9b64eafc8eaf", size = 13478 }, ] [[package]] name = "opentelemetry-instrumentation-httpx" -version = "0.64b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -3035,57 +3043,57 @@ dependencies = [ { name = "opentelemetry-util-http" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/2a/2893a8781b93894f1e8014904c0342da7e4302de6597c2d5c0cb6c1a552e/opentelemetry_instrumentation_httpx-0.64b0.tar.gz", hash = "sha256:c2cfcd03d3665762860ebd0c28038c6e47fbb48d7942dec31dd75fc634d25c92", size = 23555 } +sdist = { url = "https://files.pythonhosted.org/packages/86/08/11208bcfcab4fc2023252c3f322aa397fd9ad948355fea60f5fc98648603/opentelemetry_instrumentation_httpx-0.60b1.tar.gz", hash = "sha256:a506ebaf28c60112cbe70ad4f0338f8603f148938cb7b6794ce1051cd2b270ae", size = 20611 } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/29/a20309bd3f5a8051b61ca475e78623410c3b077e3deccae19aa4f8b5b9a2/opentelemetry_instrumentation_httpx-0.64b0-py3-none-any.whl", hash = "sha256:04829e5723941b5ceb0c88b44d63983e226b5c75b2b2e34a57739fdd0e060608", size = 16336 }, + { url = "https://files.pythonhosted.org/packages/43/59/b98e84eebf745ffc75397eaad4763795bff8a30cbf2373a50ed4e70646c5/opentelemetry_instrumentation_httpx-0.60b1-py3-none-any.whl", hash = "sha256:f37636dd742ad2af83d896ba69601ed28da51fa4e25d1ab62fde89ce413e275b", size = 15701 }, ] [[package]] name = "opentelemetry-proto" -version = "1.43.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/b9/d357faefb40bda1d4799913e6af611171ff22a2dedcb93576bc92242d056/opentelemetry_proto-1.43.0.tar.gz", hash = "sha256:224778df17e1f3fafeaaa21d874236ca5f6ffc2f86e0899298ec7351aac27924", size = 46481 } +sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/a7/3e5308cf548b8f72529c7db1afdb3a404211982376a12927fd7759f77bf3/opentelemetry_proto-1.43.0-py3-none-any.whl", hash = "sha256:c58f1f7ef84bc7dc2834016c0c37fe0081dde7ca9f6339be1970fbf9cdaaa90d", size = 72489 }, + { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535 }, ] [[package]] name = "opentelemetry-sdk" -version = "1.43.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/eb/5041074274ac0956b03637cc039d434569112468e875eddfcc9a0674ce06/opentelemetry_sdk-1.43.0.tar.gz", hash = "sha256:d8187c81c162df9913e4003dd6485f7390d9a24fc17026ec7387b8b8218b08e9", size = 254744 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460 } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/e3/b17be23af124201c9f52eececd4cc8ddfed1597d37b4ee771895d325805c/opentelemetry_sdk-1.43.0-py3-none-any.whl", hash = "sha256:d1323a547c1ce69d6a069a17a44b7da82bb8b332051ecb074041f87642c86823", size = 178852 }, + { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565 }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.64b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/30/5f26df29509eccd86b99b481ac9ffa39da49ba9577cc69071c552ae30447/opentelemetry_semantic_conventions-0.64b0.tar.gz", hash = "sha256:72f76fb2d1582d9d033dd1fcd84532e961e6ff3d90d24ba6fabc72975a83864c", size = 148340 } +sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/ca/23ba87a221b574a7c5a99d48849d80bfe8b047624681357e2b002e566187/opentelemetry_semantic_conventions-0.64b0-py3-none-any.whl", hash = "sha256:ea77e85e354b8f604ddbe5f3d9135216f982fa4d77e5859ac30f6d8a50505aa6", size = 203713 }, + { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982 }, ] [[package]] name = "opentelemetry-util-http" -version = "0.64b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/1b/1029a805fd7242f7dfce91633b244c3b14a94d703232878f71e01ce862b1/opentelemetry_util_http-0.64b0.tar.gz", hash = "sha256:8a86a220dbfc56d736f47f1e5c4e7932a21fcf69052312e1bcf166444dc79322", size = 11102 } +sdist = { url = "https://files.pythonhosted.org/packages/50/fc/c47bb04a1d8a941a4061307e1eddfa331ed4d0ab13d8a9781e6db256940a/opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6", size = 11053 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/c7/5f8ec5b30546f2dc22cd5fc5759bce2ab5be6e89a2e710a405ac9ef64ed3/opentelemetry_util_http-0.64b0-py3-none-any.whl", hash = "sha256:c1e5350d25507c1afcd6076cf9ac062485a0a4f79cd9971366996fd3056bacdb", size = 8204 }, + { url = "https://files.pythonhosted.org/packages/16/5c/d3f1733665f7cd582ef0842fb1d2ed0bc1fba10875160593342d22bba375/opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199", size = 8947 }, ] [[package]] @@ -5394,7 +5402,6 @@ dependencies = [ { name = "keyring" }, { name = "logfire", extra = ["fastapi"] }, { name = "mcp" }, - { name = "opentelemetry-instrumentation-fastapi" }, { name = "pillow" }, { name = "platformdirs" }, { name = "promptantic" }, @@ -5580,7 +5587,6 @@ requires-dist = [ { name = "markitdown", marker = "python_full_version < '3.14' and extra == 'markitdown'" }, { name = "mcp", specifier = ">=1.2.0" }, { name = "mcpx-py", marker = "extra == 'mcp-run'", specifier = ">=0.7.0" }, - { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.64b0" }, { name = "openviking-sdk", marker = "extra == 'viking'", specifier = ">=0.1.0" }, { name = "pillow", specifier = ">=11.3.0" }, { name = "platformdirs" }, @@ -5772,6 +5778,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/85/aa827c244ed4f404e99a91c3ecf5e5adb62eca806a9e9c8e3333bbad8660/zensical-0.0.51-cp310-abi3-win_amd64.whl", hash = "sha256:12529d3d3991b63820952111dc1d1edc29b2c9b3a3abb16c243bcb649631ebf2", size = 12628965 }, ] +[[package]] +name = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238 }, +] + [[package]] name = "zstandard" version = "0.25.0" From 135159193bba958ea48288f80f8425a0b76d7e60 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 11:25:23 +0800 Subject: [PATCH 07/23] fix(lint): sort wolfharness_config imports in test_tool_call_snapshots --- tests/servers/acp_server/test_tool_call_snapshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/servers/acp_server/test_tool_call_snapshots.py b/tests/servers/acp_server/test_tool_call_snapshots.py index 4c89e0176..503a0db31 100644 --- a/tests/servers/acp_server/test_tool_call_snapshots.py +++ b/tests/servers/acp_server/test_tool_call_snapshots.py @@ -11,9 +11,9 @@ from exxec.models import ExecutionResult import pytest from syrupy.extensions.json import JSONSnapshotExtension -from wolfharness_config.wolfharness_tools import BashToolConfig, ExecuteCodeToolConfig from wolfharness_config.toolsets import FSSpecToolsetConfig +from wolfharness_config.wolfharness_tools import BashToolConfig, ExecuteCodeToolConfig from .tool_call_harness import ToolCallTestHarness From 1bc581df39c05d8f6fce1e835772c9a7230714ef Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 11:52:03 +0800 Subject: [PATCH 08/23] chore(rename): rename leftover agentpool* test and session-pool dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post-merge audit found two tracked directories that the original rename (5b3d78e05) omitted from its 10-source-package directory list: - tests/agentpool_server/ → tests/wolfharness_server/ (15 test files, contents already imported wolfharness*; namespacing now consistent) - agentpool-session-pool/ → wolfharness-session-pool/ (3 ops runbook docs) Closes the residual-reference gap in OpenSpec task 3.8. pytest collects the renamed server tests identically (87 passed); ruff/mypy unaffected. --- openspec/changes/rename-agentpool-to-wolfharness/tasks.md | 8 +++++--- .../acp_server/__init__.py | 0 .../acp_server/test_acp_mcp_end_to_end.py | 0 .../acp_server/test_acp_mcp_fastmcp_integration.py | 0 .../acp_server/test_acp_mcp_manager.py | 0 .../acp_server/test_acp_mcp_red_flags.py | 0 .../acp_server/test_acp_mcp_session_cleanup.py | 0 .../acp_server/test_acp_mcp_transport.py | 0 .../acp_server/test_e2e_session_lifecycle.py | 0 .../acp_server/test_resume_active_run.py | 0 .../acp_server/test_resume_reconnect.py | 0 .../acp_server/test_resume_session_lifecycle.py | 0 .../acp_server/test_websocket_disconnect.py | 0 .../acp_server/test_websocket_disconnect_during_run.py | 0 .../shared/__init__.py | 0 .../shared/test_model_utils.py | 0 .../docs/ops/acp-handler.md | 0 .../docs/ops/opencode-handler.md | 0 .../docs/ops/session-pool.md | 0 19 files changed, 5 insertions(+), 3 deletions(-) rename tests/{agentpool_server => wolfharness_server}/acp_server/__init__.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_acp_mcp_end_to_end.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_acp_mcp_fastmcp_integration.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_acp_mcp_manager.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_acp_mcp_red_flags.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_acp_mcp_session_cleanup.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_acp_mcp_transport.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_e2e_session_lifecycle.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_resume_active_run.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_resume_reconnect.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_resume_session_lifecycle.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_websocket_disconnect.py (100%) rename tests/{agentpool_server => wolfharness_server}/acp_server/test_websocket_disconnect_during_run.py (100%) rename tests/{agentpool_server => wolfharness_server}/shared/__init__.py (100%) rename tests/{agentpool_server => wolfharness_server}/shared/test_model_utils.py (100%) rename {agentpool-session-pool => wolfharness-session-pool}/docs/ops/acp-handler.md (100%) rename {agentpool-session-pool => wolfharness-session-pool}/docs/ops/opencode-handler.md (100%) rename {agentpool-session-pool => wolfharness-session-pool}/docs/ops/session-pool.md (100%) diff --git a/openspec/changes/rename-agentpool-to-wolfharness/tasks.md b/openspec/changes/rename-agentpool-to-wolfharness/tasks.md index 04be04661..2b3a19f90 100644 --- a/openspec/changes/rename-agentpool-to-wolfharness/tasks.md +++ b/openspec/changes/rename-agentpool-to-wolfharness/tasks.md @@ -21,6 +21,8 @@ - [x] 3.6 Update `scripts/restart_opencode_server.sh` — `agentpool` → `wolfharness` - [x] 3.7 Update `src/wolfharness_server/opencode_server/.rules` — log paths → `wolfharness` - [x] 3.8 Verify no residual `agentpool` references remain (excluding `openspec/changes` archives and the third-party `agentpool` PyPI dependency in `uv.lock`) + - [x] 3.8.1 Rename `tests/agentpool_server/` → `tests/wolfharness_server/` (post-merge audit found the directory omitted from the original `src/*` rename list) + - [x] 3.8.2 Rename `agentpool-session-pool/` → `wolfharness-session-pool/` (same omission) ## 4. Fix lint issues from the longer name @@ -39,10 +41,10 @@ - [x] 6.1 `uv run ruff check src/ tests/` passes - [x] 6.2 `uv run ruff format src/ tests/ --check` passes -- [ ] 6.3 `uv run pytest -m unit` results unchanged from `origin/main` baseline +- [x] 6.3 `uv run pytest -m unit` results unchanged from `origin/main` baseline ## 7. Deliverable - [x] 7.1 Commit the rename as a single atomic commit -- [ ] 7.2 Push branch and open PR against `origin/main` -- [ ] 7.3 Link PR to issue #357 and milestone 2 ("Rebrand: AgentPool → WolfHarness") \ No newline at end of file +- [x] 7.2 Push branch and open PR against `origin/main` +- [x] 7.3 Link PR to issue #357 and milestone 2 ("Rebrand: AgentPool → WolfHarness") \ No newline at end of file diff --git a/tests/agentpool_server/acp_server/__init__.py b/tests/wolfharness_server/acp_server/__init__.py similarity index 100% rename from tests/agentpool_server/acp_server/__init__.py rename to tests/wolfharness_server/acp_server/__init__.py diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_end_to_end.py b/tests/wolfharness_server/acp_server/test_acp_mcp_end_to_end.py similarity index 100% rename from tests/agentpool_server/acp_server/test_acp_mcp_end_to_end.py rename to tests/wolfharness_server/acp_server/test_acp_mcp_end_to_end.py diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_fastmcp_integration.py b/tests/wolfharness_server/acp_server/test_acp_mcp_fastmcp_integration.py similarity index 100% rename from tests/agentpool_server/acp_server/test_acp_mcp_fastmcp_integration.py rename to tests/wolfharness_server/acp_server/test_acp_mcp_fastmcp_integration.py diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_manager.py b/tests/wolfharness_server/acp_server/test_acp_mcp_manager.py similarity index 100% rename from tests/agentpool_server/acp_server/test_acp_mcp_manager.py rename to tests/wolfharness_server/acp_server/test_acp_mcp_manager.py diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_red_flags.py b/tests/wolfharness_server/acp_server/test_acp_mcp_red_flags.py similarity index 100% rename from tests/agentpool_server/acp_server/test_acp_mcp_red_flags.py rename to tests/wolfharness_server/acp_server/test_acp_mcp_red_flags.py diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_session_cleanup.py b/tests/wolfharness_server/acp_server/test_acp_mcp_session_cleanup.py similarity index 100% rename from tests/agentpool_server/acp_server/test_acp_mcp_session_cleanup.py rename to tests/wolfharness_server/acp_server/test_acp_mcp_session_cleanup.py diff --git a/tests/agentpool_server/acp_server/test_acp_mcp_transport.py b/tests/wolfharness_server/acp_server/test_acp_mcp_transport.py similarity index 100% rename from tests/agentpool_server/acp_server/test_acp_mcp_transport.py rename to tests/wolfharness_server/acp_server/test_acp_mcp_transport.py diff --git a/tests/agentpool_server/acp_server/test_e2e_session_lifecycle.py b/tests/wolfharness_server/acp_server/test_e2e_session_lifecycle.py similarity index 100% rename from tests/agentpool_server/acp_server/test_e2e_session_lifecycle.py rename to tests/wolfharness_server/acp_server/test_e2e_session_lifecycle.py diff --git a/tests/agentpool_server/acp_server/test_resume_active_run.py b/tests/wolfharness_server/acp_server/test_resume_active_run.py similarity index 100% rename from tests/agentpool_server/acp_server/test_resume_active_run.py rename to tests/wolfharness_server/acp_server/test_resume_active_run.py diff --git a/tests/agentpool_server/acp_server/test_resume_reconnect.py b/tests/wolfharness_server/acp_server/test_resume_reconnect.py similarity index 100% rename from tests/agentpool_server/acp_server/test_resume_reconnect.py rename to tests/wolfharness_server/acp_server/test_resume_reconnect.py diff --git a/tests/agentpool_server/acp_server/test_resume_session_lifecycle.py b/tests/wolfharness_server/acp_server/test_resume_session_lifecycle.py similarity index 100% rename from tests/agentpool_server/acp_server/test_resume_session_lifecycle.py rename to tests/wolfharness_server/acp_server/test_resume_session_lifecycle.py diff --git a/tests/agentpool_server/acp_server/test_websocket_disconnect.py b/tests/wolfharness_server/acp_server/test_websocket_disconnect.py similarity index 100% rename from tests/agentpool_server/acp_server/test_websocket_disconnect.py rename to tests/wolfharness_server/acp_server/test_websocket_disconnect.py diff --git a/tests/agentpool_server/acp_server/test_websocket_disconnect_during_run.py b/tests/wolfharness_server/acp_server/test_websocket_disconnect_during_run.py similarity index 100% rename from tests/agentpool_server/acp_server/test_websocket_disconnect_during_run.py rename to tests/wolfharness_server/acp_server/test_websocket_disconnect_during_run.py diff --git a/tests/agentpool_server/shared/__init__.py b/tests/wolfharness_server/shared/__init__.py similarity index 100% rename from tests/agentpool_server/shared/__init__.py rename to tests/wolfharness_server/shared/__init__.py diff --git a/tests/agentpool_server/shared/test_model_utils.py b/tests/wolfharness_server/shared/test_model_utils.py similarity index 100% rename from tests/agentpool_server/shared/test_model_utils.py rename to tests/wolfharness_server/shared/test_model_utils.py diff --git a/agentpool-session-pool/docs/ops/acp-handler.md b/wolfharness-session-pool/docs/ops/acp-handler.md similarity index 100% rename from agentpool-session-pool/docs/ops/acp-handler.md rename to wolfharness-session-pool/docs/ops/acp-handler.md diff --git a/agentpool-session-pool/docs/ops/opencode-handler.md b/wolfharness-session-pool/docs/ops/opencode-handler.md similarity index 100% rename from agentpool-session-pool/docs/ops/opencode-handler.md rename to wolfharness-session-pool/docs/ops/opencode-handler.md diff --git a/agentpool-session-pool/docs/ops/session-pool.md b/wolfharness-session-pool/docs/ops/session-pool.md similarity index 100% rename from agentpool-session-pool/docs/ops/session-pool.md rename to wolfharness-session-pool/docs/ops/session-pool.md From a198c13c1dee573d2c30e6b061b9c2cb46bb0d5b Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 16:16:07 +0800 Subject: [PATCH 09/23] docs(readme): rewrite README with logo, features, roadmap, and contributor sections - Add logo (wolf head + hexagon network) to assets/logo.png - Restructure with Why WolfHarness? / Architecture / Key Features - Add Supported Models table with provider list - Add Roadmap with status tracking - Add Contributors, Citation, and License sections - Add fork acknowledgment to upstream phil65/agentpool - Link to deep docs (tutorials, how-to, explanation, reference) - Remove trailing test artifacts --- README.md | 345 +++++++++++++++++++++++++++++++++--------------- assets/logo.png | Bin 0 -> 484198 bytes 2 files changed, 235 insertions(+), 110 deletions(-) create mode 100644 assets/logo.png diff --git a/README.md b/README.md index 06d81ae99..0ede15fea 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,35 @@ -# AgentPool +

+ WolfHarness +

-[![PyPI License](https://img.shields.io/pypi/l/wolfharness.svg)](https://pypi.org/project/wolfharness/) -[![Package status](https://img.shields.io/pypi/status/wolfharness.svg)](https://pypi.org/project/wolfharness/) -[![Monthly downloads](https://img.shields.io/pypi/dm/wolfharness.svg)](https://pypi.org/project/wolfharness/) -[![Python version](https://img.shields.io/pypi/pyversions/wolfharness.svg)](https://pypi.org/project/wolfharness/) -[![Github Stars](https://img.shields.io/github/stars/Leoyzen/wolfharness)](https://github.com/Leoyzen/wolfharness/stars) +

WolfHarness

-**A unified agent orchestration hub that lets you configure and manage heterogeneous AI agents via YAML and expose them through standardized protocols.** +

+ CI +[![codecov](https://img.shields.io/codecov/c/github/wolf1069b/agentpool)](https://codecov.io/gh/wolf1069b/agentpool) +[![Docs](https://img.shields.io/github/actions/workflow/status/wolf1069b/agentpool/docs.yml?branch=main&label=Docs)](https://leoyzen.github.io/wolfharness/) +[![PyPI](https://img.shields.io/pypi/v/wolfharness)](https://pypi.org/project/wolfharness/) +[![Python](https://img.shields.io/pypi/pyversions/wolfharness)](https://pypi.org/project/wolfharness/) +[![License](https://img.shields.io/pypi/l/wolfharness)](https://github.com/wolf1069b/agentpool/blob/main/LICENSE) -[Documentation](https://leoyzen.github.io/wolfharness/) +> **PydanticAI-based multi-agent orchestration framework** — define heterogeneous agents in one YAML file, compose them into teams and workflows, and expose them through ACP, OpenCode, MCP, and AG-UI protocols. -## The Problem +[Documentation](https://leoyzen.github.io/wolfharness/) · [Getting Started](https://leoyzen.github.io/wolfharness/tutorials/) · [API Reference](https://leoyzen.github.io/wolfharness/reference/) -You want to use multiple AI agents together - native PydanticAI agents for analysis, ACP-compatible agents like Goose for specific tasks, maybe a custom agent. But each has different APIs, protocols, and integration patterns. Coordinating them means writing glue code for each combination. +--- -## The Solution +## Why WolfHarness? -AgentPool acts as a protocol bridge. Define all your agents in one YAML file - whether they're native (PydanticAI-based) or external ACP agents. Then expose them all through ACP or AG-UI protocols, letting them cooperate, delegate, and communicate through a unified interface. +> With raw frameworks, you write glue code for every agent pair — at 1× speed. +> With WolfHarness, you define agents once in YAML and use them everywhere — at 10×. -```mermaid -flowchart TB - subgraph AgentPool - subgraph config[YAML Configuration] - native[Native Agents
PydanticAI] - acp_agents[ACP Agents
Goose, etc.] - workflows[Teams & Workflows] - end - - subgraph interface[Unified Agent Interface] - delegation[Inter-agent delegation] - routing[Message routing] - context[Shared context] - end - - config --> interface - end - - interface --> acp_server[ACP Server] - interface --> opencode_server[OpenCode Server] - interface --> agui_server[AG-UI Server] - - acp_server --> clients1[Zed, Toad, ACP Clients] - opencode_server --> clients2[OpenCode TUI/Desktop] - agui_server --> clients3[AG-UI Clients] -``` - -## Quick Start - -```bash -uv tool install wolfharness - -``` - -### Minimal Configuration - -```yaml -# agents.yml -agents: - assistant: - type: native - model: openai:gpt-4o - system_prompt: "You are a helpful assistant." -``` +### 1. 🔌 One config, many protocols -```bash -# Run via CLI -wolfharness run assistant "Hello!" - -# Or start as ACP server (for Zed, Toad, etc.) -wolfharness serve-acp agents.yml -``` - -### Integrating External Agents - -The real power comes from mixing agent types: +Define your agents once in YAML. Then expose them through any protocol — ACP for IDEs, OpenCode for agentic TUI, MCP for tool exposure, or AG-UI for web frontends. No glue code, no duplication. ```yaml +# agents.yml — single source of truth agents: - # Native PydanticAI-based agent coordinator: type: native model: openai:gpt-4o @@ -85,20 +37,22 @@ agents: - type: subagent # Can delegate to all other agents system_prompt: "Coordinate tasks between available agents." - # ACP protocol agents goose: type: acp provider: goose description: "Goose for file operations" ``` -Now `coordinator` can delegate work to any of these agents, and all are accessible through the same interface. - -## Key Features +```bash +# Serve the same config through any protocol +wolfharness serve-acp agents.yml # Zed, Toad, ACP clients +wolfharness serve-opencode agents.yml # OpenCode TUI/Desktop +wolfharness serve-mcp agents.yml # MCP tools for other agents +``` -### Multi-Agent Coordination +### 2. 🧩 Multi-agent orchestration built in -Agents can form teams (parallel) or chains (sequential): +Agents form teams (parallel), chains (sequential), or complex workflows — all from YAML. ```yaml teams: @@ -114,17 +68,14 @@ teams: ```python async with AgentPool("agents.yml") as pool: # Parallel execution - team = pool.get_agent("analyzer") & pool.get_agent("reviewer") - results = await team.run("Review this code") - + results = await (analyzer & reviewer).run("Review this code") # Sequential pipeline - chain = analyzer | reviewer | formatter - result = await chain.run("Process this") + result = await (analyzer | reviewer | formatter).run("Process this") ``` -### Rich YAML Configuration +### 3. 🎯 Rich YAML configuration -Everything is configurable - models, tools, connections, triggers, storage: +Everything is configurable — models, tools, MCP servers, knowledge sources, triggers, connections, storage: ```yaml agents: @@ -148,46 +99,112 @@ agents: words: [error, warning] ``` -### Server Protocols +## Architecture + +```mermaid +flowchart TB + subgraph WolfHarness + subgraph config[YAML Configuration] + native[Native Agents
PydanticAI] + acp_agents[ACP Agents
Goose, etc.] + workflows[Teams & Workflows] + end + subgraph interface[Unified Agent Interface] + delegation[Inter-agent delegation] + routing[Message routing] + context[Shared context] + end + config --> interface + end + interface --> acp_server[ACP Server] + interface --> opencode_server[OpenCode Server] + interface --> agui_server[AG-UI Server] + acp_server --> clients1[Zed, Toad, ACP Clients] + opencode_server --> clients2[OpenCode TUI/Desktop] + agui_server --> clients3[AG-UI Clients] +``` + +## Key Features -AgentPool can expose your agents through multiple server protocols: +| Category | Features | +|----------|----------| +| **Orchestration** | Teams (parallel), chains (sequential), inter-agent delegation, event-driven triggers | +| **Protocols** | ACP, OpenCode, MCP, AG-UI, OpenAI API-compatible — one config, all protocols | +| **Configuration** | YAML-based agent definition, fallback models, tool registration, MCP server integration | +| **Skills** | Expose `SKILLS.md` files as slash commands across all protocols | +| **Structured Output** | Inline Pydantic schemas or Python types for response validation | +| **Storage & Analytics** | Configurable providers (SQLite, PostgreSQL) for interaction tracking and stats | +| **File Abstraction** | UPath-backed operations on local, S3, SSH, Docker filesystems | +| **Streaming TTS** | Voice output support for all agents | +| **Observability** | Logfire instrumentation on critical paths (RunLoop, Turn, delegation, protocol entry points) | + +## Supported Models + +WolfHarness is built on **PydanticAI** and supports all its model providers: + +| Provider | Models | +|----------|--------| +| **OpenAI** | GPT-4o, GPT-4o-mini, o1, o3, etc. | +| **Anthropic** | Claude Sonnet 4, Claude Opus 4, Claude Haiku 3.5, etc. | +| **Google** | Gemini 2.5 Pro, Gemini 2.5 Flash, etc. | +| **DeepSeek** | DeepSeek V4, DeepSeek R1, etc. | +| **Mistral** | Mistral Large, Mistral Small, etc. | +| **Groq** | Llama, Mixtral, etc. (fast inference) | +| **OpenAI-compatible** | Any OpenAI-protocol endpoint (vLLM, Ollama, Azure, etc.) | + +All models support **fallback chains** — configure a primary and fallback, WolfHarness handles the failover: -| Server | Command | Use Case | -|--------|---------|----------| -| **ACP** | `wolfharness serve-acp` | IDE integration (Zed, Toad) - bidirectional communication with tool confirmations | -| **OpenCode** | `wolfharness serve-opencode` | OpenCode TUI/Desktop - supports remote filesystems via fsspec | -| **MCP** | `wolfharness serve-mcp` | Expose tools to other agents | -| AG-UI | `wolfharness serve-agui` | AG-UI compatible frontends | -| OpenAI API | `wolfharness serve-api` | Drop-in OpenAI API replacement | +```yaml +model: + type: fallback + models: [openai:gpt-4o, anthropic:claude-sonnet-4-0] +``` -The **ACP server** is ideal for IDE integration - it provides real-time tool confirmations and session management. The **OpenCode server** enables the OpenCode TUI to control AgentPool agents, including agents operating on remote environments (Docker, SSH, cloud sandboxes). +## Quick Start -### Additional Capabilities +### Installation -- **Skill Commands**: Expose SKILLS.md files as slash commands across ACP, AG-UI, and OpenCode protocols -- **Structured Output**: Define response schemas inline or import Python types -- **Storage & Analytics**: Track all interactions with configurable providers -- **File Abstraction**: UPath-backed operations work on local and remote sources -- **Triggers**: React to file changes, webhooks, or custom events -- **Streaming TTS**: Voice output support for all agents +```bash +# Recommended — uv +uv tool install wolfharness -## Usage Patterns +# Or pip +pip install wolfharness +``` -### CLI +### Minimal config & run + +```yaml +# agents.yml +agents: + assistant: + type: native + model: openai:gpt-4o + system_prompt: "You are a helpful assistant." +``` + +```bash +wolfharness run assistant "Hello!" +``` + +### Start a server ```bash -wolfharness run agent_name "prompt" # Single run -wolfharness serve-acp config.yml # ACP server for IDEs -wolfharness serve-opencode config.yml # OpenCode TUI server -wolfharness serve-mcp config.yml # MCP server -wolfharness watch --config agents.yml # React to triggers -wolfharness history stats --group-by model # View analytics +# ACP server — for Zed, Toad, and other ACP clients +wolfharness serve-acp agents.yml + +# OpenCode server — for OpenCode TUI/Desktop +wolfharness serve-opencode agents.yml + +# MCP server — expose tools to other agents +wolfharness serve-mcp agents.yml ``` -### Programmatic +## Programmatic Usage ```python from wolfharness import AgentPool +from pathlib import Path async with AgentPool("agents.yml") as pool: agent = pool.get_agent("assistant") @@ -203,8 +220,116 @@ async with AgentPool("agents.yml") as pool: result = await agent.run("Describe this", Path("image.jpg")) ``` +## CLI Reference + +```bash +wolfharness run "prompt" # Single run +wolfharness serve-acp # ACP server +wolfharness serve-opencode # OpenCode server +wolfharness serve-mcp # MCP server +wolfharness serve-agui # AG-UI server +wolfharness serve-api # OpenAI-compatible API +wolfharness watch --config # React to triggers +wolfharness history stats --group-by model # View analytics +wolfharness task "description" # Create a background task +``` + +## Roadmap + +| Status | Feature | +|--------|---------| +| ✅ | v2.9.5 — Multi-protocol server (ACP, OpenCode, MCP, AG-UI, OpenAI API) | +| ✅ | YAML-based agent configuration with fallback models | +| ✅ | Teams (parallel) & chains (sequential) orchestration | +| ✅ | Skill commands across all protocols | +| ✅ | Storage & analytics (SQLite, PostgreSQL) | +| ✅ | MCP server integration for agents | +| 🔄 | Enhanced tool confirmation UI in ACP | +| 🔄 | Remote filesystem abstraction (UPath) | +| 📋 | Team-mode (dynamic LLM-driven team formation) | +| 📋 | Agent evaluation & benchmarking framework | +| 📋 | Lifecycle hooks system (M2) | +| 📋 | Capability discovery protocol (M3) | + +## Development + +### Setup + +```bash +git clone https://github.com/wolf1069b/agentpool +cd agentpool +uv sync --all-extras +``` + +### Commands + +```bash +uv run pytest # Run tests +uv run pytest -m unit # Unit tests only +uv run ruff check src/ # Lint +uv run ruff format src/ # Format +uv run --no-group docs mypy src/ # Type check +duty lint # All checks +``` + +### Workflow + +This project uses **OpenSpec** for all significant changes: + +``` +/opsx:explore → Investigate problems, map codebase +/opsx:propose → Create proposal with design + specs + tasks +/opsx:apply → Implement tasks +/opsx:archive → Archive completed change +``` + +See [`AGENTS.md`](AGENTS.md) for full development setup, code style, and testing conventions. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for contribution guidelines. + ## Documentation -For complete documentation including advanced configuration, connection patterns, and API reference, visit [leoyzen.github.io/wolfharness](https://leoyzen.github.io/wolfharness/). -# test -# final test +Full docs, tutorials, and API reference at **[leoyzen.github.io/wolfharness](https://leoyzen.github.io/wolfharness/)**. + +- [Getting Started](https://leoyzen.github.io/wolfharness/tutorials/) +- [Configuration Guide](https://leoyzen.github.io/wolfharness/how-to/) +- [Architecture](https://leoyzen.github.io/wolfharness/explanation/) +- [API Reference](https://leoyzen.github.io/wolfharness/reference/) + +## Contributors + +Thanks to everyone who has contributed to WolfHarness! + +[![Contributors](https://contrib.rocks/image?repo=wolf1069b/agentpool)](https://github.com/wolf1069b/agentpool/graphs/contributors) + +**Key contributors:** [Philipp Temminghoff](https://github.com/phil65) (original author), [Leoyzen](https://github.com/Leoyzen) (maintainer), [Million](https://github.com/Million-mo), [yankaifeng](https://github.com/yankaifeng), [tasia](https://github.com/tasiawang), and the broader iroot-llm team. + +## Citation + +If you use WolfHarness in your research or project, please cite: + +```bibtex +@software{wolfharness2025, + author = {{WolfHarness Contributors}}, + title = {WolfHarness: PydanticAI-based Multi-Agent Orchestration Framework}, + year = {2025}, + url = {https://github.com/wolf1069b/agentpool}, + license = {MIT} +} +``` + +## License + +MIT — see [LICENSE](LICENSE). + +--- + +

+ Built on   + PydanticAI · + ACP · + OpenCode · + MCP +

+ +

+ WolfHarness is a fork of phil65/agentpool by Philipp Temminghoff. Grateful for the foundational work and ongoing inspiration from the upstream project. +

diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ef6191cfe06e529d8435c97dbd23788765464ce3 GIT binary patch literal 484198 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 mm29nw{eZ zdq<&PBayO;ka+86&>pu~jnH;0}e;#bSq&l`j|qb-2=ged~rV$WhG z;G8eSc4X}4MD~!=PZ&>r?8W0pU-OFbz?mnF8wuj}Lx=c9*K5Z^PCIR!y!ZIn*N^$v zWPD8XIa$SD`xG?sZ~PI-il7*@AfM@{3RiXVLmJqEIkbQW41-{$bcAPz3axkM<2rppME6f+|Qx` z>N$E6Gc{!Ft0VRsjfR`kWj0x|isDu;G3&Ay757-w54>)Kl9 zy2Y#I((AuY+}hT>zVb-B8l_Yw@sYoklMFIqu+J>v2Mb!*sBd}Git*CNxkIonRjPkw zsCWL0iYAet<2Sr*{NlFgW8qmo-5ZbjUGqq2v>y~BBGP?W+?)H@QCS)UxiJa&z6mAA zWeb33&p*oLxUFz`lnmYgTb)&Ul7dWQ9MA}) zn=&{whJt#wW8SuemiVQrk6?+AD|IJ3CIBAIla6`jH3tuR3%`t<>_`|QlP;ERoodQ9 zR`?ER6)0 zy6L9zTkrkd@rh4;hD-NMbofw3nV`uy>R4({V1%ZOeYx#V`Re*P{q48kIj;KdcgLlV zzG$3w`l-Cr_6mi?yj$EBQx1gqbNX-3I6j?ly{*G54F@5??N7aKoPO3Ba5?yL3W{VP zW|Sn}P9nJ>H8}^EBz$FL(6l(&=%dF8J_&AfB8KlfVI#D-&*Y*FUZvFcfrE#}SFgNk z{Km&WF)rsGg~JDrj3+$gjPd%H{qT6%OJ6*m@${#RGfvq%4(z*c+;QuzoIF`Fd9sR~ zg0Wg_jw9-lAxPWsz#0mK)FQ{2hq{}aO;0@T=<(zyK6X%(-fSjQMPz zRc|iM)tdBW&30uB%|`o+I`*}D-nr-TM}fP!3UPSceditd*k*mxNI&W+=gX;&U(FoH zvM77>Lrxzreb!UPkG$l?uYQ%D-PK4K|>cQ(t{E1ibckfEw4U9gqJrM>}zFWQEB=V zh`iN*agve_b7afstVc~1P>{0HZZ zY{_oo!e)?yC3`l-vitjm2fQd!@>3?C&c$o1=2EN7Jt~atsk!Z(;h+sO|1V9h3 zoE#XoXEVI$0M$u!nuT87C1;SD_e7<-FT>eRvdd=q3X4Hs(VaTMBfc1JXKv(XfRiM&wqZr<*n}=x822+3I3E%Msaq08Sq<>Jv?Rk`F67J=LcObS#Z)kDz<*viyVP~H-Uh|q)j-PzpPmE_j z_u1owlTO0MQR8&}knw_toHg!ZQFt}@ZsTUptLWzwxRLgpGfvBR>9Ir(6QgmOlb(E& z9rGBn)Kup@lcPb1k(U=6?pA))wcbFt`pVMk!nq5!-Mfz&kL60p<1V^roPNUb87(sye3+BI&aUoJTJpRqmVfH>EjDmR(AF8_SxAJNy zb32|_T*a>BB5{&go zkdJ0wJs^iO?fDYk8HDp(15D}L!(S=x@de59Mcm03=<)YDHJ zM^f>@W6>+E+R}4uEvl&Nul{ESrffmfW_kuN?NjuYJ@y;%X;)t^UTZlnBCjQReBATK zH*WyNfJXXKX$_njnk#S;kU@Zv4*CL9K371*G(|X;NRCgAiKk&;;Yo_KIV3@af0=P*&Uz-iNc{=_Gcd;mKj=`>`E99Yz5(1!=EE zS#g%$)wt+dq64Pj&09q=i@!4KKwBEl{|V47a-B$a2Gh7SQy(bg?4>>hmSuxk zQGrgsMrk`Xw52ecl`ESq4~$!Gxn;cT-R~WL_L(p7j*e^FOpx0}Pyj69n2#H5F5l~_ z%ko`t^;R*Bl8*d!9-E=bxhzrLVXP%y)p6_JXIExcE*cH3)EDusUz{gNI0VEOa zzxp>mJ=>W_`bKjlf|*b`4=fx@M8OgIfV$iyeX_*xtjy~<;|^}!{gw}0&8<0}3*aquU3FS_L7@wykkfGa-_BhFr_aLCzL zr*Q@7qDNg!+>ah79CysPf`9wJc!MIy1wX< z7mPvl?W^P0`kDvV zaaR1Q>8c-xHb#S0HvvX$ev}S1vT(#_zeaA>9W04UIYrT9LkSynq#Hf`H$X<*hd8wXKq{Hv|6`3e}}h@i!K>@EM2^*>Dq z6!p-eQeHmpQEt5x^PQPxfqhUN-ey1;{Op|MU?l?uVWqQTM+>$KHFmAW&vXWVr@w%n zh_$uhDL(DETUa(Y>Y|C6a(a*4PXFl`gtL#)_^>P1yMc|*=+g&>~#_o~$$f zEU(jcjKWZ7g9#iYBkFo9=Gt55SWP>4wXUAuSUAZ24qyA~<>O!ezrQ^0Js{t!C=)d5 z!Nq>r6iU$H+s7*FL=?XvdmxmrNB?p7nrm+ux8HgT3zdt;si&Rl+=NIEn9ah3#mW7= zvpfhsFR3R1Gk=@#RZ~wwT!3Wa$(~#s&%5Pw9(L|{%2S^-9>Tl*uYLK8O#X+)@%$v| zD}UsLFsnXPz~$o?s|`+}jb9m52E7T{4K|k^9YJB{)=78NnQ5t@y@Q^1wTCPFjxXk9_V6xDdZblb@?u zh~B{!pKpKvx^b9qsXXSw3ozT$pLorzeZ9t2+Tcr@M)HPTk1OkBi z7-yY!%6JS5uVcCD*zr?59k;ceE9;J9%z2e4xy!f~gYxy+_;erZ+n7WeTfjfrJp*a=2i=}6&!7Dw)zew?!n%=FB7W=_4NRZrg-3G)Sp}W>oZ#A*(zP4yIB71>rY{zrs3T*0 zIq^KRWRp*grzm5+{!^PQ<(5=%Y#Z_YAk6q$GHVPiZ9VLIqgPTgU^oc|%mQfVYOi3! z6U=-}Y-S>_V9*(lXe`GH#*X2@OG0W_oy^HOA(pn1rKRACfS8htxXZ>iC3sB8hF{#_ zfvp`Sh9*M$8Gtdk^{KprJ}3SSuY9)YTqipH>D6b%cKFbC_=gXho0%Lv$S2RX z*morc0-~6&P233HchA1@tN;Gj`O4!JOs2cxdGlQ@Yx1aanAYYjXlSoFc%WdjsBV& zZQAE+5a2i+WEjwiM>+j09U3<0jg9PG6e@ zgP_=#t&+1j{x@URSV?k^`RaRIW`S(-H4~if={soJGe@z-J~tNY5Bl*eyv|{9^}{cG z!8mF61LN=g%ukP(Jm*>C+=q~}zmga3vyRoUGvob_nsBT zwpRyv+2P)Jd+qhtXMytjfAZ;Z7vCuJqbH-+Mi8=zUt&pIw)JOQXV~w&L~G($c;pAC zv1-f>{q)VRe|@~-M_w{cW)Ytss9U`Fw9y>LzV(?$7tWf(W&J0;Gk%Fq=sttPYH`MM z@*Q^jVz#$D{jxZ)zgv}+`79M^s;s>94Z_x*I)Q*tA6uR{*7#Ab<6~llw<`hRW#Qhj zS5JEZmDsF8nfQYzb!9A-b&HwcZ$3aFq0c#9l_@>E=ufQCpE}rg9=D*LJY@cphW-YN z0mRfW@i#Nk7EZDgN%eH7cQE0MmPi_gpr&Joc}{+dtBXjeNkhe|kJx=5J{0#v~u zif<|;CW8=Bi(^RH3Qjp#Su8+Y4r52hifSStR?yl&Dac_^BNzGbVc?E+j>7bwp!= z2S1R^n;v4*+|)<3kd0>vQbq3ido`I0~(4i`0O(o!55Qs zFEg79yGLGJh7Mc&$OV6Fddgs7aR2^sBi|PJxqtNkaDv z3`Cmd3d87X+md+M3&px8?Y$=)H=g$Fr;KwRe$F`lq?4g@!d6C~2IwpL*hxL?9v`$f z)^&kS7Jzjd-7FT>=b1@G9pyO4SKga2)s@@!u?rW!UF9O~U%%(QT7){f!o(n)(B7_a{EmyMtQg*T0>?z>~$%r}?5$w~Cbp82$~ zhmXMsqj90*z{vHD#EzES_Ga6)M{Lw`gek(7eY%e2Y!`5 za%JtLWBGO%_ipV5k91LLH}j7B(Be!l^=bS(mt4Kgm}MpYsRQ(4C}tItmTW4MtBh2& zqs_#CUh2ee36FYwu?Z}3K+F6WKPt&<45@1`yv{$`Odkb@!o1?3=g(kOUh?EEn)X)F zjwKkaFP>G}<##_tW3Ti%4@9qu@nNjQl=`%tc;l%q+QzP3Wu9mJI|0O>IhZz7F9z|L zOMOU`u3RXox3NMr|uATiXXVbU^-cmYMVuLB5>pq39QQwnFW72e%B#aGHr1g ze0LT}cz*Ne7;cQ63fS*w=IvWF^{xKQRrb@qcEqDEbDr^Ln7#pPVkv#3RQi{E zXH+ciulfdd0W%<6UPQw`cs&<1ZaQ`|2MbCvp_UHF{O3>o>GKQeyjt|FMjXZv7cWHK5*w<a4M^zZB$hd`#QYR<6y4Im_ZIxUey?g_{9|Xm05JkiXWw1NvWFK2|l4UX$9Lr>{R5` z=)A*^k2JRa7^fTjhMHKYm?MAAJH;_6j}z5pU-{a2fcqC7#m%(G^VQW(2t5emK&*3O zo+$%64;`h}g@d$J`f*BquZ8lAT$Rk)$-%;&K&Ax#)=4dWN86I!!jdr#EiTz8VoVzM z#Oql>1<$cnj}66jnb;>@f6Dv-3&m@1zID9qgMT>wEB$eR*v{sgB`<&K6UR@z;^pH6 zPWGNUJL&h_vmKW;s%?um8@t%~^$-2gxEBDYf#JXZu^$~LaTUq01fRy0g6H!Kz<1n# z&-m6Iw~k#W9Y0?9!e@*({M`S+{W}lMO|G7(AI3dApZNS2#y!+;~tgFvic79srap*lfko_{%=k^ zj+@5SmawT4yW^*+RIv5Ltk00-RwBkiob!W;K{)+`NUv;lUW*N|Mq3cnx4E^R;Q}9? zeid<(0<+{L1i!g(a4042*1C8-#K(EJu|RqEdwys9r{DbDabWLBT=hc6oGr7V*D~< zD;4m>FQ)1){3u7x{%GGV|K{DCD!+W#-Z+@N5bveu&@t*Tl4Q1wmM%tR%o?3#aP1n{O$YxrAuqU6asm4%IV9O-xGSQZMB`Ut<{ zCGF{9(app1Da~d_aQ!b&Iu6$5KYI1E^m4UqA{1v(1xMxVKiSFS#FYiv)N!X?GGHB< zJ{qTa?zekLW^A$1Y1LGyQZY`R0Rc9~6s3qgRmOAbI^yLCuHb4XyiB&`S6y54K*&Kc zc`yrn#~=+heS{NyR?{xnsRSt6(wF}V7srER#GePE`jiYX^I^kH=nb$}E_TM>7?)F% z->+Gtxb%p^2-eBU13#T4fBBqoNiVkL9FfHnin>6_!XeOBjJ>BLCZ!9U#){u;*&iXh zAup@{?905ni%R2mt9bHif>R(+f)D1>k2hF;yzr0z@h@;G{yJ{f^n~F8W%@!U?K4j^T>RSMwVmkGzPl4fBzP_j|;q?&b+E zacNWsbByT8b`I8wb6pWp%zIn1ma&iI`zkcz>iBj3PCb0bQV_>(co}ECq={f+(pGY* zfBTxYd+k#TPnJfSKOh5S0}GTxhxrk->&LtP_+#UeZ(Gi|jO&xxyIqH^)aX(N-+^6vULt-g=nj?&M)S_JcFPRj?5NvUl#4a?@dEDHh7}Ftu&{HS zqq@-#OKq$KOezosWB_F?RvTKD;^5br3J^GIpn>kagxDuE?5bGytaw zP~3poxmgEy$#%%(u@5iFp<%LkG{8;{+IKLlcOoXZ2Rk;X?@V|SI+N7_5*V8pZFN9{ zB;a&u2We~8VR&3`IzWFiMa>y#>r>znC`IYGNn0uJXyHfnqBQNo?f63RMmd15jqgHuGmvj8I9Z+|8D0h%6tFhQ{$HV502dj_Kp9A|J3n@*4^eX zp&93q@ty0gA0PeR)j3H%i7OsY{yPSs2ERQ8gMK+lxrR1vq+y1LP>oR)?zgb$3hl$Jw7mH@~{bR4F|ELOpx&% zo_WV!!cvUmYR!LIGRE>Mw~j59q??J1Eo2X};Jcl_H2BSTzi+(r{eLj-KlY??h{br0 ziz%0wch>cUKbIv+|$I17P%Oa!a*N9p#RFR$SFn?i^0yJx(BZ-E`l zN1&O1r%(NL+-kZ>wI?#?W>SsI;;8$L98KYD%F03F(|*H>{p4IL(3hNw8Dmp+(SK0J zb}*|j^)=mmO&)~RXL6deLP#vpRmrjBgM!YIlhAoqIv5=g-tXMjJBuYL*65o7KHcjKju2zQ~CgHvGx>RZccF`6CBnPBp^6T5jQp|n9@4?7;< zra6nEzAe#p7XZH9L{8aE&d{x^YFYL8S9CJggs=o*t?H(OH-u#e-@&Ji^`e^kiWdj* z`2+mMzhoQVCdR3-^cL6l#x`*#53Xp9S%2^ovpkyJSfOkPc04+$_-di;q|7TP*+riF z4!AaASv{1yIEmrdnuiF?(iYw|U{^f$of2MsSuYnUZ++|A$G$`JKUQiYPL0#MSo7mw zzGa_%?#a!-9l%c5zA~8QN2amr4{b1ZpFhk>yBvA>T@}Y1bY-neuAvxWdD+J2gCWN;6EMxg`Y$@X8l?lu+d8ypC zu{VArRbRFnBeBGV`?S_Z0rSBw0{xS^uk!7iw|)2{<15$Q5WCOhH(g%;ikFS&J&E6J zVd2IpY);nLLD>U;uj`Qf(v@4a_ich5cJPcHlV_V;R#HwReIj28OBQOAc(Xc+cg z`h_fn{`L>QX#6n0I=l;P-{n4+_x}0k#vgEl@P7LG2(%WAC0F)0RbMdtcr$k6)lt~j zRH}D~Ybg3ZK9kxzf=YvarO{D2=5+V2Hkzc$w^c{8pg*nKhOirEfv z^A&8+HgLCQmbqY^fp2KK)6`~xLVcB$i=FKU(NLc0O5QfS8H3|=XJ1R+l*kAe3NH_h z^j~t_y84T^`apf}iK$er;#*G6lkN z!^_2qJ-s~+)!L5HEvEWWXXi`)M#k98M_ya}ZC5Yx&m1gWeyv$#CW4){Sy9enCwu>+ zMP9+;0C`n1(A=+f5Gl+Hv|c9WD5)-O4;T}Y!M$b;eJZ97n+#&=bsIymSN|FQQ)c4P zRnZjRRB4bqE)7EHO12pMJlTY%ldNb(|GbD%;$g!xxR!q$rH!1643oHFX^uvGU{kjBqv#JW7hBX= z7{ndps62qH^?+U_GjbtTQy&~?6UD0emIIBG^KDlMX^JFCh5; zA+gEe(Vh?aB|Aon&pi^cuQns4G9JAU@>zMgv|9+n#! zbArIf8kYhwS|33GuSLV+bo@|r%)-h$NeFp2y7Fjy>IaayiHxmjSE$fDP9lTtT&!Yz zFTxS}-LDVloijr~DYv`$h)1-4t=Ya53GGofIN8di3;*-~L(vQ{FF_385 z*VtJf3umE;t^AhAbfZB%>G9c@IOdSqya7tQHYj!gU8DoW{Nyt@dsw9hM69=X>z#e6!Fl)~XO#;%E) zQfW_`BD@lv`}vCVc%L+*UpyxRUpwUGtz=cV^6`kBtf=PzSa!@^i^@NMak1upr)Yhn zqkl?b!-prs)*I7g2dD23O<*zCnzETl6r934V_NmKSH`;j>AC8#+r*=HFgRaCmW^Xu zo#e)VBd_1$b}0C05m#H?+JlR`$v_58COgz}Qu7dxJ*{Yq7Y=e<*`!$SXc8Yb<%coJ zv+flX%kTW|2gZ%J-i_Y;mPJiYy^gKsT86PW_^8g<)8EiCK=CCne2vr2FlC=68});? zs6t;flE*yYHxrH;ci(q#eCR`;7;pWxcZ}=#NWyQu*za8kd9a~6?d~vzadW8?UIF6| zaCO2i9+_m5iP^X^mh3OIVv?#SpM(cNPV;Gn0xkCE1h9H7H2K={wRBaXEk82Uy;|T2 z<|BXp7vonx@PTpNz59?kGM>dpTtD;jmyJg~>^#0czZj zu_H7g+P0QGf?jFZ^aJgsn-dm7J@!N)yoE7VrzI9BTzR>hD^S19SIR%|u|F9%^S$tc zNBOJA^|m=qwa+8JSrXs+*GzhPN>3l|N{*j;EN>eH?I2rQnu&p!h|4kM|6m!Tf42Db z?_NE=&PTr-8ZjZREc7%^y>pAUrd{*}NBE|R_q6hsAAMz)ao$c}5i_J1n*AyPAUq zaZNIU%)pq+t^yjlaru`fw(P8J23aU^Szq;2xukfiB;Go&O7c;k zu^SuHAMT8&?~bN#2w9k*z0J$QRbZu4k8Yyb48}|X*ib{;t;~8>zvjdrER;s#a~Ho>UOB*V$fhZgm#?q9++{pm~-PJr!z>e$!vQsypWL;`K!(YB{( z>u=`l;GqY`H~HA&(Ej_!#gDmo?Dbneu~R=Ym9YUO*aRRxiRN{Sb;hES3lwr;H7{f4 z6}|YD3Q26?F>7P9n3bLMNh{u|vqL{qp*e@ut4KZh`m_A+`NKaRZ{?2y@8uqVW8pvd z!bgn1|JPnS&gPd*_xKB-Gb4TXj0v!G!fWg~W+(o{NXk9A+|NA^AN}%|#uxbr>4E+G z#{c}9*YL}tC+6L8aLI%+)b?6bxb9`^7oRE(x4c>9Ao{IgCu zZT#`)K0Ef|=Oq49<}v4<%YVPPnBs&-zhxytylWw;ja>HEfc-I#Ik6wj1ipub&tosT zkY9JbZ+w@Z(Z83Ewr;rdPJZO?d*dmOdNhk8e!$NAaXg{UylQEaf9sU?Wb0v4jhy>{ zr(*urxX9R;^7zO&$(XgDIv++K9etTP-Zafv zXo%e+eqNgBye4t_v?jLrOe4IF|H-Sorlucz#d@T7(yco;g%t?JDq$O!jQW-8&

+ zg=fXH+RDXn#_#kgjZ!QfDavfIIo7tvv%Dp*Ol(4MEXX4hMi%|W+x4b5zUj^43m|se zlYvQP;H@%~wKbrUCMg@o6KwMlet~F5#~zlebKKL4bI7acXWHE?g5ZS~*%?3vR`K<+ z976a_a;AS_=~G=XuB2>i!MAfVktPP@I0zDxKJ^z>FTHxQii_F_Pf4A`yt_FGQL%(B zH3;CP+PLLY*1=|8eQwR7lSxM(wNN;AdI+5`sAg`TP!eZUwjJ(&{5mDZ2lCY?cgWRm z4O&nBkO`K^ZU<6`(gZ>|#AW-pzvG?blYjczaesaSAI$JJNbB4bS>Gp?Act-JrKOL9v5;V-+RIdjUC}lys7I$ z|CHoL@U+0_{TDfj%EDh{Qs-oeSp3>nWxmYQjxMCXV4%2>K~&+iH5fbDgd;;!DwzYr z^n`UEUo(E&?|o?e{ujP9?&a#$u_Wq6ms~jh{_9@HiT~JqyrN!C^r|nk#1VV!t(m)| ze%Dv|a25BF9N-sw&*x(s|C6V7z4}BV(x0G4xf%F@ zq`3%{N9;QAXZjz1AwSK|iR^3FTr>7_h2f_A?i=5}>gsXnd5;*!^R;V_-yHwSqm*>p zA9>oMV6^_4n;w4=F;pFv@4$2nK+8B@@UXfaPjlfjO#oRyroXRA*o5mnB z;xG;S`%XUM`QQitc)aCZzdi0aYR}m3jp?qw#3U>$g3)RWyD>-jf*zdV&FFNZin?`Z zrHg>@%GaWUy-~9bIaE2Z;TX$dN-n)$><~Xia6EJ9V=lUIoXM50*_9Db{;L*$VyOT2 zM@)*tmvZ)5G%II+B){g+@DZY#(ui%_2k@reItHXwxBD&vDi!*a-x#DVcj+g63k{5n z4gCmW73n-W)+SG2l*AOkjbXKAZ_ZKFJ5JP~U{7qz!@^mgr8W+MRK=u}CJACzb~ToB z9Wlh_l5>2RoLN#&(=lql(6F5s8%@rKB>O?ivJv0dE}R9bGC9U$LV9_f6ZJ$JEXm8# zo_4YhFtE)5X+1AYqL+6C433zE(9YIrPzRh>85%fL1x$<*)NR_)rNF{a?a#gN;?F() z*y!^w;hD6R3{M!=Yg_BQ1gXOm8rJiaCsQBBH4i-fYS*@l6WfUG#ADre4|az2#-xwN ztP3$@a6#Qfs;thXZ7?m{+M9NF`kOxDKb=qKVW;iNC)Z93n09by;irG!0#h~z6?Oj` z#+TUEM77CTFgJ(IW%Y|Spp0|3|o{efa1-fXEdjSv6f$H%X}?cL+9V~!j5V>q_cr~0lBwy_VHpzTNyKej`{}r{LW31y4T??|A50G`9 z&x80=Hz)eBxDVg_BV~^KT_p35Uu}w`b6)eSZ84hfnvuSJY1vovH0kDS%BXMuZ0FA{ zFSCTUc_k+zpLmU-_@!-MIr(Cb+oFmmpd16Wt4$xpQ@=EX5ug!Iqw?}B`f%v9AKTW! z^1ZOczx^4kTYmh-ZSAi3lQ-As~ndJ_72XNXcMJvEZB#em}j3RFMzwBuaW-V?|*Q7@WY=Nyy0fT@SW;3My@t>Ni;TX zum#@2qE6s8w`oSEF+|ou>;bTf#MrFE^!i-<$a3ahla0-s%;;a=d4J`LUm3d*@*j=& zp2R;DN5L39Q1k&-rtRcof>x$-v6lLpphE{n@`CnsA3N3&28&a8>#Hwq7QH%fjYpA5 zwZzYH8%hLoH|~{4|JV(#u?ZFPsT_4vUekSl6+uX zdBgSN&$)tegg$yK|5NiMu3q#{2pduErwI`KJ}~?s>Sk_m{r+9|j(zv<<6nXwI!@>6 zmLh6+;!S?zb(`CsB<8zoV-yd?EP0cx(0-)eX6<#w=4`$zy!mN(S*g#}9T^p6!ETu1+Mg!cePmxi`0Kj>Sq& z!hX&8e*OpR!yo#{_|><*W8A%GZ+@sPzm*cF6UXdmk>t459yG^qEVE0Vz`K5y-@yUQ z#BaV+jGvQ$$AC2X+H^d%?HJ}^obdIb9gF{IB`$yER$0gDDSM9_kK@}{r|~b*)iKH$ zGis~u-ohc`cA7NbyrAe|)n%H`7-i4r;cGma_z7nlU*OYthtQ$>($GhX4czc4tL6Tr3E(VgvmCNiG&4!s( z4i#Ti{Al6YTW%j$TzhR68Be?9((zC(*(bOegMA5VXvJ=>{7n|`8A#EXF)KKWwQU}b z;>300c;-cq9DDKkJ$~=wTVCnlBP_IGmC#&5UPhcT==f|w6#Y3V=nKjW1xA3vG! zX~Z{l{i_$gSfo)BLV#{4*sIOlbgd73`Ts~pZj4;t%SR^n^VRPUe&9pn-@WaffNheQK$zy;bKp?asvVxRaMNGz_oBe~Wo zD}Z$cEoPw|*>mjp{N>-|?=il`Z{%cTCpOI{P6W-bZ;V}>MK2P?ZQnpn3@gjZSmNJw z&YXKJCcIfd>i_^i07*naR5WNAV@b9B(LPCD%o`;psB2@I3qA9n{YtBn?lq{J$}1>u z>8&f20S|T2mCN~S0(Smc*ej%!+D=;^_QUcQFUB%u!l{krLvNW^Xa4lL0O+gpl9h0} zqaM%1(*<1HN;Jj}pyW5ir4BN3lQMVK+eh|Yon*KC(b)9sq%EE9+wU3o0jxn^Gfrr+ zJ9YCFQ zE?4BXBkv$dHL}uu_0D?^5J$6R?c~{v&a|kS$x#NLQNmS*msii^wddGsU){XiQlB&t%kk2= z2fcOgqv5+D<9+Y_K|2Ztc;T6N2%Y?<3s!d->JZjCX$gljEz`UZ3&)7=C8{ufFJc<4OFy z{BHj{X!_G)$Bp;UKn9}2Kp*L2b68NpQ|Crjy}R$dXI%E(E5`v4Kjh>S#-sVcwUhb3 z9>)CQzGI<-O;w zd8Ue?sz7EzkXb=Q;)H+#8ckFjGDtMhL}PA-bys)#8gI1kTHSqD-|nn7#;dt%Vw|H9 z6wxTg83dU{jS4arGZa?k7_dV~~``OR%f1W*@{hsl}wQJfHFOYKvaUePwsU&I@|v1hx7Z}cCTgEcs@Ic$hW zT0j-iw@$dkzTRrD)AUJ)Mhp@|;%NJuXvTvj_MMQL(Kp9Qla_@A3zYAm=%I}}+I757 z>-3XPXopkJb(Dg3)Z2*m6F=niBjNVy&-n`J2h4`sr(UB|w^kqV zu>o`1kL)NGPa>}lmclo%h7x+ECZ^DmQ=b6P=O%5e+bkx>8kRti!{Ar0j6T4AgW~{_|u=H{@rqO%xndRt?j_!Y=uYdZK&PZ~KP4T9N)hr)tX7uVM?I2whigMH1 z=w(BXzUO%&-GBWujy4FnQ*mr_PFY^d>^w19yxNdgS?xPPiJsoUEY+UFihbVO1QQ2n z!P(~(AWkKvSQW8f&H=dkG1P>ytzHH{)rkij8VrT9&rU*uVJM#1sG%bp9I@{;0h-b?EtTJ8PYgvJ`TW}vj~_7^zr=7Z51yOXuDJZl_K6Su zV|$!a9JxkCSGyf$T(n)2J23T_n8#YxAKMJuQ+x{Fh$j`8@Qv67hq`Bif(AW-q{$vT z>wOQm>fXY?|My?<)piR|8W2uwRjI(m^6anjN$RiCSz zE_M0?9_)dnCYPo=hC_IK`U#wtSwwyOoCna|#}SpC?t!AbVvTfl#g4x>r?eSkQNGw- zz<_R_0puiV_Ej>XYp%DSv|I>Y8{s0Bp-aA01{&SLh;2-;O_!l=PYkfDM)^G)$=c4XzEvREmfULDtz*)KJ`SxD2R(6QWXpH zP^b&qM2f2Kmu1V461zAe2A%^ zo}1G14d7lLcNlO3EX)Pmz$)YpKqJ;4nxmb}SjeZ&%H!~A$W=XKNz#$P5CxE-Qr!%X zWwj_@yejXU^rv#8oU33?!9`Cmjrjy#9FBbirF@IhO%{TG`< zoy)*X$_kdE&G_%e@)1+)rN%913CM0!{J|qH9TswYgWnK|eBg{|6ck+uwbOv8IgaGG zamhC2Gy03F@3qE&jK(5()tiH}saw-hwEy5kkF;-p_iAh`Bm_0^sZYG4E>#t~{w7nh z`jCMK;_ykE{uMO8VE!(4z6;xC{*E!W5jrbwRN#z$)Mrt~p)H4;i-KmY^hNW@l>Yof zx3hBx$i^+Z+rNJGn>nX`$=m;+t>TD^JNLNXW3GNJqz#fuylfZHqjtnkt5fF|0gJdz zxTXv>>|D~8l}EPpi>-VxewkbmkMD9(@<+e@o%Sf#|16rfpq+Bi{_QQVxS$D=GF#R7vcF91%KX#j(@{l zXysW8=b%Zn-mMc)we4#=p4{HP!0r5(a-G-1+jq00=cp3#Hwv)H4hABf2C5aYu{xeO zLp9%N!&b0E8;cJHlgGsuO)lKEjSH^nZ!j_^>LoM$(P=iM?yLT?3Zr(UL034shFxs$ zd+hOc%fJ7uJ(KIdY*Q|o0HI5D@gM-n>6^hIFFgzC_y_kKCu?ry$pTkodp>nn{#*AN zuvqr4wlGU%%|x^x?XH$KQ=iJ(l2wveDPj&e?Z;v&w+a#q%2Q9>V0aTCxE%+?(J_um zuu2_QgA-ocb|zXfOJo^i<=i^7gZ*lCD)f|Y>?9pjq^y@N?|yUOMgJh%-h>vX*haGd zh|%$~tWySkvPe4hVH~=+IQ6GoMQ@k2wnCR+Fs%~O!Gnz~ZdkS9k6X)KLJAo_)e*s7 z7<3|xLf}Dn1|AKPSzR>fQI=9E1%P%Izr9SVbU#HK((+K06%9Qtk4q}1r$c}|21l6< zqU%`V(EkU2L7vK#83JAR$Xf}Se91wyRc%=sJE>{sPr(F>gl*3X&_8hnzrvJdN25(^ zFElpp+=c>aS!7aPyMA2E7r6$|%7@5Lsk5P!gfP^h?wE7SI<%TdS+`v_>Nh%(6+&4- zkNjprw`^Eyd7#v-H~*yFyY9gpMNwY0RrhxHP^5|!UGh7x zZ6Eyaf7A9`%+8bR3kJ%x>l%AP%gzzS=}>H)yw;9VO-96R(X~I8@)N`Q#Hoc1TX(lF zeeufn*^hp*J@MFj{6N+f*~Bd=(kMw;*JURZein4450SChXeD9{C>byS&v<4{FNU02NObL9@oLKP>WjQSmo9%FsA{bmW>f?TllOWrEWCh2^kvk5mRm>q1|= z2sMM6X+4JDotAwm@zC$&GlFOqTmDphjyv-3b|lxl%;N~kHMif!vs1YKhP4jeW6(Cz zZRAfIdPS@X@l=Lf=rxpDHkY=b4v(t%B-}NtR!dM=oa_F=SB)k^^N}I(XaCh?^4OF_ztCph7oJK3HxyWuDfpb|z*m_BaL|I>bLM zYLpLxRm3K1CIpmp=0Q)@sT2Fs6w5LaDT6}Za(^@a`tAc4=@W0+9yaVC>K}P)G7sj1 zrbyZrDcg)=-r$Sc03((0%V@KxOZ$F$xq37*@@8F<*N$b0?F>D%p})xaRhJlQABnfK zc)Dj952~&d(SQ)0Y%5(N#z}u%s=XI}S@>f`^K~F`Yo$2QbfeZyXbu#7G6O>@fXi*_ zl;%gkYKBjl`K%h0*L;^zmQT?;E%KEPddu^brrypcTD}vWVC-SYK-e*gm9iXu616@_ z8>@2hwGViVNb*38+|(H(OMsBm?;PrO^a4a-Dr}*M(*T3f0M`VCyu9I`i9yA?@QlGc zSQBgMLSftUWqV-Inw4h*^@6*4f~q#EVh6UuBF+R2-Vw*D+o50IO~;tZr~IIet?k1@ z^&5Cn=oc=%tnK4mxh{{kT>3RPgQ0s(Y_x9?KiadtSjm*Xn4d?xTeW;a`-?ySllI)_ zoZjB^p8tiraCUO72wbD?qF$epD4!Dd5PDu)6`#V?(>+FoSuizluuE)Pc)a+hKg~bb zPyTb;@R)D>b)tY=kKcqH9ix4KyvHprb&S=9D2jin*VN_k6PpQ`K8T_Fk~BN$Jv-YD zPVxNnx4zpx_Pwik%kS>Cn90M*{0n{0TYtZ;IbeBP;6vJa{H&YNhDcQ#r2*Mi$2!{j zXm=S1uv>kr27({T1!EPCcP5*t4T;V-j#Br?x)ywXbnh zWmi8{DnuL+xAo>HxTT>J+W7OKebd$7tIcaz)3e7saaG@}_>H*RHwq`W8S|pVf4$-x z?W4TS_~G4L(?LAPzhS-XlIRgy$0n{o0-NnY`iMRGs_%j=XXLVloh`2?YPzZ=bE7K;%D?(sXYeiuh>YRPJ6Kbl&|rdSlG5JF48$N#8I)EZ@;tM&C{N@ zbEj2K`KVF7Vl~FD-6!kUIGa!XcO0WFTbv-ETm6Z4(%7@U`%)P5TYTUg<0GoeH=+=` z+Md03JOY{NTg9XL*u`e54}VA94*H0}$lGt=n(z)=`kD4zp_qxouVT^Zss?KUJ=Gcn zP|QowBnFmf84 zX`t3ph~d#jM7qqV)EYdHBv=M+cB2~&LoE)%sfe*fUv>=2gr@o830Qp}jVN@BMIle| z)?ieXrj6hQq&^tI0M>`3&36CE51!%2QyNLEbbAvf?FLc#SV$(nV&5Oa<^`dNBU#PI zhAHWNu!W)Li*DS?|7_HR9eu03wh36zSOy*&IO|RZ^Skd_*KYao&o~vMjk@k3CiNA& z=}Zl)eZ5Vvu{^>^CiF2mM&Sqx~QFh?n~NR-~0w1#3dj0`)97F zu^1%N#^R?QRGUfXFbkmAwoxKpaj|;Fz$7MaS!G9MoILuliw2hCh z4@RVW?4mO11d6PP5*yM}of0E^K*iFS_BAx=;Ebxmn;nTUque`dHgDzKC*S;T`%wcQYIN2I)ovlZh1|Idwqe!rjaJ+L( zOsJ>2XmbunZoc0ZEnnH5#*vM)Pd<_Ix_%_}rcBzh8ts3zMt{eTiQtl!qg-yKQ82b* zasO7MggW-L(~do+9mMH9UmSSF53loB4HGqHjrb_PYj!}!mb5q0=LiY)r;P7*&QXC* zx4haxtaf`&REqyi=fAZ5*$aNHEoUOis}tKdetBp62$RAGc>YY;b%NUMYCEg-SZT|G zU-fD4WhYI|_87A5ueUWQ#xA_1Q{H*lv@M+Kx{CMk{Qbv2-8Rl$#8CimrM6#{jN2MF zq3b<{Je3R#Lz{M(!Pkuawx8BZ7pA^zoSbQg+FwqQ1+;A=ah@Ib8kh;Bk)B~hf8_TjSKeU&GC<_mLhSHMbt zbl3j31IR`@RX-hv`Dq<2VlKKAF_1IK!nU{HnQ^vw6OSIh;DNRFDPk<_wLhsiLZMI3 z?9a+l)yL4S#i>rUK4C4J92G?kjV1$u+~RXcWVNh1k+hrvP_6BGMY>RJQJ2EEDpKrQ2alb8`>PkqD< zP10^NV7o&h?faKD@dVggd70oc4v!qeKkB2oICnm$NjSJwlLGW*K*7#DVwRo{7X3(k zQ0_MI5(9i<^Ibw!9=Hi%eD8QnAHZ`oPT)?QCE(c#o*(@9C%J&uCRAE#Vj}Mt)N*lb z^7w+a%IlXqcc&==@AX|FFTdIOTQjdX>v`>uUi^Y~2uG!MQU0ylf7w3ugCFp&qQ`jt z%&ydx(@nIn80^$sQW&ZFvlI*oY%#{?a6@-nK_``VUi`+Tiaf=$wf*3GKWy*&=qK6} z+#1hAn{x_s+~(}5;>6ltZMm}4R9IAfo!X!{ZD}sn2_jZ}zE!pe;z?@{YVZ7=-)Tp2 z6w80OULW*==bqV4JdAorKCRbH{8N`IYK*E=h+}M*rEpbEz!;2$)A48*G5^-p*R{u) zjH%Maejg5S*{!GXz_Q*pC<;AeBRu7Xwv32%Q1x3nrS$p^9)w}FZ#gw`{B+4=k?m8) zKg!GpL-bM}Zn2HN-6?Lmzi#8Zq{xY_+NgBdN-99Y8-9rP%Tbo`l?kA>;-vUo#Wk>5 zmW2{^cYAJ)i?J9q(KsF_K4m2O3KLi&>!z@(*0StN)Xh4(%yQLC@?=IGsjbo#L%3w` zOONR+KP(T{VTZi!(GhQ=$6_9f|L*sir~x4K07|1%86&lml^ss049u3+r&q$@i6AyhR~TiEd=mUr|cN$-^mMFn4Xi%3xyD zoRwh&)w7Ba>?u4Ip;jTtys1qM+`i*v})C=wuXx< z@4EBO_6Qf@)zd2rPqitPvhuCLOudzl{T_pOFRKe%chtsAQt-ONhIbF=8uqf2yYh-lEJ5sl>vq!y_iapHf3XbSoIyjQFWP>6F>F#V-sy@t!;b5-^@Z_9yg}eGGZ9tvwoXZPNznIPr)V5S*=>yO* zX!Wwh4cmEYr-LKwm$?Y~4o>4O#n;z9|7@<|*e{b4cW_p3CLqWN+(}C1(VIsF@L2qZ z9%|P;z+>@wx!@^l4sOrpVQ#_G<8ND2kv>-+tDuS3JJJr=Z)Lmj_S@Sw`o%*JtZV0< z@f`SidqvRGHT^h3uI)+RkBW}3*FoysIEi(#-3$B1$(M0Ij7jhzJmBsA#~*L&IYM|3 z{%_>Ekb`*O+kq=+gSlW;@8h;XMenToi+)>BU zi|sKOh*`y)5@P`0-o0ED%SE#P z_O)-dzy0Vxwe`I7YP8nYwJIZTmgS$Oq7c9yP!L8W5%ZSCw;Tv3*C1ZQGu+!0Hyz;M_ ziSSyUjdSve$1~yf*q0^z4u6qJU(yC8uC@&qCt3r)ba&jrqjl?Gmd%JFlW18VmVqxA zNJgbLD5(7hHp)!5-)$ljJc>uI@@BMeS()y};hDiQ$~q3pf-!QW{fYfDq>rmX|E*qs50sV4^lt~=4&|<@lek@YKK|dsKki$3=gvKkJ=PxCyp^3d zb&L-l)kz#;Z~^c%VJx>sLd*8U{dD4dL}44cD5^c zP}}?e;gfCso_UQ+R@+`294#^vXk=?UbfSRRl`q5`!^S?2b>~uFz2+%lQ6r0D)ce7U z7q^RFc5XZO1!uPb8QOx4n_C%+R$!j6Aw>d#^Qi6;D zV9nzW@F|b?=cwSNmw%;g=af&bIT{rNvohMR{(*dKSigSA!<*H76~6>+^{c3i z;98|MN{<{n2Q`=B&Zn$v2VjK`Fu*PylVu}iS67H`LU0GA{Wf^3RZk}7f|(DV3e^@Le9{G1oPu-(N=?>_jEPqxRN*iM&=?oQgPx2ZHg~y40us z)L0ddqf-p_iS#p++ICzVB&j8iKw{an{fWGM3%Dx#)WZ*Ji}3eTOipfL(snCH3%8Ko z#xrAH@~l(aJSM14v=G8A#e?lKs+;6LUt_K&Gj~yUv@?N z%oo0ryNq~iQ9W-5HPI9WFtbpS_Gz8QHugsUDXUl@zTTaihc_C7NIGsLLHxa2sC?Hyb9tl!vf<&EiQpYbgEQ9Mf>IPP@NgIZxBN zh<%*i6^<&aq{_m?PbPhgXEPI-@=yE2jl|m6$WJCKg|;2jGHQ=6JcT`Dh-Kt0%T`(Q zHpKK1Maw4`qCZ0ysYqJZ?WsYcCDLMV*n-b|+Hx*V8a5R|vcB;@da*y3fEbhLl}lUB z`h&UX>WhZxuVG74Y^jJPavft)Y<|5k45)N`P_?~|r1-6EgyWAo#+rC`y#z{@9xu0L zL0+CXtHS8;H#wEppd!VOivcSG!Nt!AU=qiQi)ObVzF(w(V|T=7DeKov5thQS|Jw zrus2aJ|V6y^bU<;$U-GJQ2CL^n^N6@T+3s_Km4_?w|ls=!^y}IE0(n1zu>%f+A&A9 z75wA14qV*9y7Vl$k+1e-u6VQz4(y5}{Nk|@n1{ZFYaqVEdwD+jjc>O{cx|s)5LD18i~V3M#QHJE$xcv;ZRi%ci;7x1 zWbM=1#wVU=J9q^APdNQG53Kf=19&FSL!|%kWiM?)w9uIqjo7)%SWPDtB-y<})|l#It&~x8Hc)S(x*>7%Xer zShBkFSM#Xz$*S&v%@{_&q)3_Q`%arLec|%<&tLj_yMN1euGd(Q`caQO2$USM(v(lT zPCgW=#ThW98DdL5MfpkXi_!53N0$))Qx9F+{)p?FjyU8HnsC_}6RWa3r!D8@kI#Gd zY3*6p-q5c8<+`?q_yiK{nwub+j0%6szA>U2V8;`Az(pduR|{S zwb8K*x(bH=StLS?W{f7W|!LJSb~ z(=XCNHBfr0Es!6JF<{ldv2@j;N6?D2eDFH4`ISYAa`n>-A%w~;U`sr%&%HpI?@6+19FtfTmDvH z%tt|Y3$-m={QJeV(9R;K_ON#^wqcj@3I^w^1 ziRzeWRjyE>oHs`ee`d|NByW_s~474a>$5uMUQ#E6% zo1Yq8DH}NHINh$dxqF&t#nSohb+5Uoz48?oa(E212Fkr0)mX%Y z?`a$@`k&wU`u5DDp4L_@Sz1Twa3-~ct<<}No_fh^J=G-+eDrin-4R08a%mn7i8l_` z^2}-X@MPD&|H2h*9f#eYL}nNCJseHgE!Kte+fx4NpLN2C_0L5=%1u(XV^?&;D?R9X zeci)5kbd~!{cSh8PT}_D(+@j5*QD4=3Uf7?8GW{A*D~OayCT!KM+Ywr?GlzhaYS8Q^-kN!vvTDI0Z^(@oe{&VTl+ zc){VRM<3lDx_@1JV9VCFnfR>3?oJ+Zcl_ap^Wp0q_) ztS-vCA5_+z%&(3`sSACRhs8*3L+`I&@KUa`THKEkmQO0SEfi*SVPhH_)t~kq$5Wpg zSHB+@wIt@`btPpKnRby&9JILfOKHkX{`Y#&#c0%K%H=6NVw-#nQ`u3MQiY`(gi?Q+ zU<{t(Rj1>E{P9(thE;p6;28GjjtK#DW!SJ-UX!TuvA6d~jcBm#^--7=Q|kcT^~jTn zjQPS>sY=f-8@>#j^3BF;+e>|Q_eYt&mrTcREch6Qisk4(-LeonPrOkdE9zZBVrJt% zaMOIUtrC)*rc%lhRFYjuI$2Lks|M%R8Yn)&n(>pbdDqU9^3emrs$*7YEjBR8!PR{W z0it-v?91Q=FF$cEb)y6DFsLLrGN=#x&fYkf&d2KSep4=os}G$| z=om87wv4rWByGY$mu=Y>Gmp3C-{ixOJl3w`5!btUhQp{^g;QtLk$mpN{Fkbo;vvCO zdCSG=f}iXR_i>2-q!W(g?wuF5MT-_iSNw-dSIXz^f55f2nt#$Ce&74r{!6@G1#3NC z+Kv!>g#}yXaL?DkZO5PR%cw0OWuOpatu;VGq|o|W&*_lMK7V<;f_3wTO>H07bXeil z8+6H95rYE=F(AneIjZ-|TK{gu`%PXa@R4tRyFJ9wpoPTb;5ob7fBO&bY{wja2uDyj z#gll}wH)@J)Uz!ulMZ9(2c7UiG*O1Mmz5s_NJdm8fgfPr$Tc2!b9=RSTG5#<^bw=@ zS>p>o@sGR&$1AO+ios$(aj7b=vh-hC=Un>PlCA68yZ+?u?PU7eLh4&a%L}oBd_A=1 zLfTVco|1Vccm8m0Dm1k^!B!?%z#$FzY3tTSR=pstW46y6vC4#r2^qS#azXA@*ItuT zsA@3!A`w&66L0&-Qzkv-Sm^qyluJo#r}6?d9S+;37({4nF=F^!vX5iyQKZp>GuD_x zziW*>BAZE<|59qzR@%FI2LzHXDbt`8EIz-%R%2_`S%~pN7l+N-npY8PFB|(eiP`_^ z-_Wnoz8Dv6)p%=;1s&3+C8qN;(KR=g@PdEp->}A47=n*Fn!SW(tRRjtv1pxtlnpVV z1Y?gOS&MqpPFc#+=CN(?$3MpeAkjg>h4d7huAu@@LC^{F)f{?Y95@YB8i@8rzJe0t zQy}H5k1|zO0U1F_sR8az*@z8*aZ1{Jf@bp!JcsFZ0$JT8G1LeYXz{N;04g55ZEF|lJ~#C1%Zw4=TnK=jX*DrcKA&BYWEiJh74 zz|@h1F%7ND)DBI4)!|FmIkux?iq~@W=&OK0PFEXV8m0K_3Rh;)2cqz%}jM;o#d0igxSfKw>lH- z)QdLig4TY06F}_$lC&9hg38iITSd z<$*u--mzOpW1UN#If?P&VejSo%h$i5{lTfvY72JnXqzzerJwz@ed?NP+oK#|aagln z)#a&3bthI3#bkdFiz~gSZRga{wO9SHefVRa<`f%8(tIH&b_##Vh%HK^*CZ@h) zW(+Wo;`CP%)qkDv6X4;GEChZY>q@3W7oUG#d*w^dZA%v}OyBalq%r3Iey6!j%txJC*V=x|19nRlBQicsi}B( z;qFiD#Xap82JMf1B18pJW_s|9@pt%K<6VvSaef3KI18(BC~fLXFHH2&n*7imlVEXz zuiuWhZby73zAl%#3bxrimA1@?l+HLBJ;q1uQ{9lB+J=~kSp~Av`uT%N>u{e{;9?XaP6h?0LONpQ)EF~ndSr9cHPTuV3v0Wq-T{c z#F=gMbotToW&)rNlq6m{(kl#yg4<}uIq;42@FPCVZcHUR1!(vWUmBTtbwobVm}2s) zm5>NsXH9(2moq4qdZ4e7@=fs9>Dz>pN))BjGErrKN=^w7)ceWY|Kn zt4DQKa~R%>NVOH)*thL!H+L2R$&&>h3t?V~O^;BipB0~Mv+m?7CX{!&`dmYIi`oV7 zdz-RHFf4o~Z$Vywz4(Wo_=PU#8uOSe9Q(AxxFg3MZM8Mjj1UPGU;2X1)Tz)DPyH+( zO^r!~*P@KcSg;2R6BQ=33zsg=pHXd z9!A;PP6mjy8SL!Yx4UiSg@!l$@P_t*zyDZ!aO+Oq#bf_=@Mm$W7_p6fw)z&-hMltF zNuNSW-e5(X@b`MI`mUH8dt=KJWx3w#xyK*X{_@ZMgbDECK7J`ws~ctVaT|KYWHEO> zz3fHLZ%3~=kVz%cq%3ub^6C?XIcagT);LsY!580*fBL+IyPEFig@gA!_%QK-qS0>G zwpw`!w&}ELNb$6@mHlbrfR5}e*(R252c8K7G&E+VeDn$1f%bCEmhCk5@E=;mvhY-U zG%Y`C8H&Hd<`_FL9=~hfXaA)Qg2M(WPh?Yb<)0lde(P=bzr4m2m}@O;1GjOl{Y;N> z`ooyaVZwZmLD~}hQ$3CJ*aC3q5?j^TtZk2Bwe_Nl!?rL@c*qxa0JEe`C3HonMMA6S z`aAls7>$d=ob3f5iI0y&s4{4NA=?6CB!*%nB*|IE4l)u@fLaB^sh&5+S)#ZMn(y3| z8j+Ve`3t17@U{B*P;}Yt$86C7BiC|_4jh{hH<4XrXZT_OpaHm1Yt59FWWRz@x!6Bh9 zgKLzTkAAm(twuCBA?nvG8?n`OdM^FWJMV6H^J2E0{QIBL(&gc!*@d2e!KzEvAJ@v4 zI%JBJ=QEsg!ZX_GXFNMQ+A$Cn!@$;=gU#kWmN{(^*H8TRZ@sZyaKVdsI&GSMj`%2_ z4vJ__0U=9^Aw4>ZokUPw8yh*qm(?Q6Q>&-lI>mwidaQi0j zSlPkk!^zQzpG~MH3pKFV4m`zj`L)-#fBGKpFhPD5JHc0+dNPkBe`VXB+dvmHDHwGU zv%RgQpQSzMRgXLj4x$+#pjnVXz@55uNSWNSwHLc);^5=~a0efFK)dj{XSC($^;Pon zJ7I9rv6@ThU(Q`LIRHUf+7RW5kDP+wM_9E%@NqO6Ip4$%NIwTrhyYlBhZ=b#K z#&!p{N5?K>fHopHYwK$jc5&qF)|+l^@BhF@+q#WgbBZlM3motZe|$)zvp?$={*aVS zz1h)NFpNZAtQ~LnqndQp=|b$E%ITW_^rwH;R&oj}6G1d+GICu;$SxBt48#ZyOS6)Zruq%+(+A3&qU=1H{6i@5=5wcY#0h7 zJL;v%AX}7jb-312V=NfNJd18=@P%HyGr!ofo?RivWD**IhN{?4K`~_Ps9JuCm8aUr zCbET2Ofz*EVhT!WhWcul6Rf9T!dKZN;!gt50rgGFv;QI5#$7sVw#s5ZafuQLvkQ~0 zNo9QKsK>@)C!CRw&RKZWWSoYfjz|3&lRCdTToUC0u1GT&F*qi+!59A0Z~eu7DQWZG z_3n4Q*N~JyDXuXbgA-#nU}-w}XjFnCZO0yQaE2;vIFoz_KL%2D#D15bX{4n~=Mo6@ zsGFtqxzi}L!AsFV9wvT*ITgph7MZ67&SYmKJaQqw7Nn-6i?}gg)_Xa;@uB;x>cqo} z;F~hilkcII_G+AkKR}9HHc6MgvSY^LG!_%)povEtuC?^^Q*09d7@NW2U{X-Ldj>rT zggWY9XIDl}T5mk0ypG|r%f7^UCVi!V;FiX+v~50)efQ)OT;X=IqI=` z|K&Lna_lijXXh6G%5?0DCs=~h$XHmo)#JbQ#he~Fl7HWK-*s18cmD(Vr&1lo_UBHB z9XGty{%Vp^?M@o{>RWZyGpV;~nz1Ka0%tiHPfK}S&MuA~{`97s+p^_L+tZIdns@Ro zsGW3=iHME)V?1j8v%JOlGI)2*SOo)cefZ&a?fvW8USv-?@Sygr!w#!q9W22J z55V|v#0x^CgAt!7aTr{|1mtk8IeLWm2dxL2JG=c@pS9+Ib_utjpLpmYyl9a};1iP` zW^9c9PLw~b~nY5EZckW_Z&7CRtGcno7sjz!C^6)o~ zq8+$wd0V^OJJLoQV~c{l1br8$0&n6Pt`G6_;(Io3;hGxm;_;Lt_%)L@3AgFANfM_0 z8ST|<#ugg|u~NJF{1f#rsMg@Gz6@r0Sm*EE*^XbkfBVxvxTKxNg|bUIf~GFpP4U*a z0*PhF_cRK|4%Ulky#V)WuA$mUd$I339;IEx4-pjx^+!YeASdP&!J%VWB{R|IwB7!^ z2H{0ah<&_(zRBwnj3IO)1XIOSTlJ-nkX5Y4qUyBPMaGzJGgwWdC6M?r1|tro(Yz^_ zvGexmX-k8s2(oUxG*(e5_qY4s3nPY} zX@46f>W$^HF_mke#{*3x7OF~G^(@BAe076L584<93Tx*Bgh#Jj5Stat3S#!TWn`c) zKKS)UTQv@=fUnqD&5ZPfwPTz2SMUC-_exZS+A`21Fg1QV#t38r?#{nJCRj+i)DEKL z3ZuW}L>VnTriZ@K*yO{qKPfZqfRp;nKP7P~=L|7yJzs&5KRV+=@y4IzOJ{88A0LDx zA6!*1HEzqNO0q10t;(!y2awE+Zjrc-#R^HK5VPTLmGzD;=13p~k1>@`+s4&FT76=4 z@tkFE?1|NX^@(Bh^qA>u zVvh{iJepMfjRPp0?s*z_ysTNetUbitKAXUKFO#Ty9(x4Z@^+p11Y$tQ{vBu@OcjQ?E+f&mc~8lIBA<23$zIdQJQO)mOI%Hf_!A$m$W_ z;7fZgRq?^3@}df_`g)u*nI^xRqdY5lqVgGB$h(@jR(pmo0!YK}$C$9O3HGI8H~Na? zX=i6TjZ`(ZJM$$t`jm2sgFlq%!@#J`jK3o9_A}{2&xEDxO#6!t%HS2IOYD(Pe)W+u0?Pr^GA{QJwILRb!e1GFL0^viurVe)Wvj}RNu%oWrLSy>#|Dv@s+I+m zmEdOJ#Y{N zlej0W3~q_D7&X+5B{>ZBvJq3|l&P5=(*zX5yQ|n95J`f~D7c z0qH!PzWCzbtO<(JlzCBS{3l;Aug;ZOxw7m8v}Bn_Q53Ttma^l2-gBSR-t@b#ZyP`G zk#_fekCt_@7-I*K;8MgoP!@&Ia!6fnXp+U5SSo@YHdbtSjh`@7<>XNT{#&;7n|HO( zf98vA8H4UCe)E-WE*ArMYUszm{AK(2x4+$Pe(=$@kQb~S%)jWjaq;5m#~qW$+13ogO#cv&>0?Aspi=q*5IJ zD;Z6_4per+AqTg!d0xxUAAO|l$C0Z2+0iax0_X(Af2b7+rC4HbOhjT-VsNHU#^;Jj zu)%LEYK$;tyXwA?8n`>W(vJfkT{`oar?*u+F8^a!f4}{7%jS05rY-FsufLYp80=`z zd%8yixh4w1TW|bP`^3jT-+uP!hPK@|g*)a!7q|7~VjOK@(JlTT2KwJ{c ze_Tqhxx(TE#&q#j{Hp%sHt%Y`9ra>O#~(X~rG!O%#YevviD!B*hsiRI*$B?q4 z0wSe$127wu>|^PUSuQx`$66IHq2vc$QT?%FO|~p#F>hPW#AjetBMw*=nPRP9=+WEl z|4LC+G7Q0Q5acfb^p#Gt7P(aCDY}Pc!w;h}sW8&TD;6$x;^P(O($YxejUH(9jF?g} zi(K(nq6^#LO5XU{l*cEoRWy&W# zWuuTmOoMkTu2Dv!4=xrgWGBAnfMxAv=be{-*s3y)K|SQ51vA^4P>p}xaz}l(1N#b& z47~Cc7qypPbRLh@Uy5zxZVN$iY_sWZ<~o%~TPl$lTbKr=UGw&ZwDzcR(y^D-2oArj z8BZzg;T-#Y4{vIhfBq}&Cp;u=4yS>xyYE z$7Ss7vVV_d2T*mEw^|TyYSFq+U$j4V@Zw}uKHUns7qbSlz{I4!tYlf-spm?6q;f&4 zJg`2oX;a(66II!Rwj)=sPF>`si?kCNDl*u?&=}N%%Qn{6i3vNw(eaJRK*}i-6~{jk z*D)azgWlKCDuPer*Nzq8Wa&tbrWkxD0FQ6kgd<|AIw^gL7MjcUgb~F~u&XbKU%Wl0 zIAtH3#u@a_CC>Wg9ZIQ}9$!0veJypm_Zh2bVFVc?Wq_cP(t1@V)i==D7Y23DKxeHG00oMb(C+W6z_wh2v-4qwIea<%T zQe(U6crfV->jZEZK`m`&W-KgrJ*m3athiX!7$~Gj1a}c)YsANidX3LL#=#^FVVsMK zV55L##Lskt(g+_=Bexge1OE1|6RZ5SS^- z{sS`9q=)N9Caj$x-{39nl&3mWqHIysR7o^wH=5nnGU76dN^g}_TBWwpQd+dK9UB4a zDNL--wHLX-TZ4ZGX=?+0=_p#BUx09dE3q_!0IY!_rSNEDY8*(6fU7TMpf@-!!%5wK zif(caEW@G=XjEvNi?pS+<7X`Etk^5~3BusbKY7}j)Rc(uhcLxE-JO5 z51N47aj08R>7>{qitUZBySOb|=EIygG80{eK6OemIus*O5$9K8sS3`tR`k_GZcd(7 z`ws4ux$BO*+IOzLnP*y{NC!Ovz=khN`D{kbp{4*m>gqfQCT*PNB46ZQ4xcmoOl-u* zL}eEZ_}050ZXfyN=i751c%Xgb7q_={T)(q;@sf5V6Q=*;kKW$au3C}XTa9bIk+p0F zVqC*cH)X<8LHgzsT&t~tnR5L$uA-r&$EfHT5R~Jihb{>lK4eGS zRC%I~TJ`T>PT|eR?v^LFwGG@MqlX@aQ*IvDcZfUc<5L}7h_f+CWT5-e?>z{St%a?2 zoK$6;W9y^UEe;Nq<}wMfR1;!99b-n_d_(trH} zq#76NYaX#VcJ%@6kAClW+u^)XdO;eEh>J;|b;t?4M?*!E3A+<0b@8b&LXGBYvAR}r zMCJ__oYy}5-Rs&m?>4eys1Gjn>!;J9kk}5~5@KD`whf)t2b_44$^1Q>o_&zFP9F(g zZ@pLERb8ZJ`nh)0#p;OSI2~PZjWIiT(d{;Aw(^X0QpuoQUEr)_oU4vyfe`Y&I4 zap3XOeY4D(v~}xIc%v5^EROe~DW`YZ>9mwl&)HX|x~ojidR`L>An8W1vaPXV#&^9k zmtW&R%CXbfYpQI>QFUtGf=ukOshsWIBgu>j2$Jwq4NPsAb{(G%0uhuChoUDi)Kcg6@)_c3SXvKQaIipmfB^ z7GicdmSI-|Bb^Gw_Ml}e0QuQ!HnwA5x>#x`nr$1SiKhbLR{^Av53jP3r!vFmuB6lY zcJQZiyANV=Rh{*hiZkdJ^MnMm>?U9{boHu^p!H3#|4?dDRTHI6G zP6~hd-c4f@9cKDQ-IG1(YG&&WfBwFm?f2gFrqJY(KI&gK!(H{CqWn0hrLDlLOm#U_ zBu)&_oJyI;gy%r64fyN7{;PJ_Av~3IAxD0w3hP}Tt#`g7lGzTL8fzjK7bZUC#y^4Z z8Y>2PW8~JOC_brmH}-e)FtQu&c&Po4uY9}R`PgRO^g53_QkJ*({l~v(2d`YtZLM6M zN?XriQ-@)8jPCMk2ti+8%BHSaHy-_{7YVx4$sIJ>Hf{vUotMYAq#L_ToJiPyb2LNZ zEV^=*Nn6kL9vipu%nz1BdG3raax~7-O?i-6uaMkn`fQUt4}~3Wjygc|xhKZOf9$qY z182%38EGfxM>)P2OF!8OBTKD;z1Gl_1&0?C@6Rp2Yga7i4w>EDO~w0#D3f;~tEUTX z#IfnPgI~6pv?KlRX}GFgvt#y?qZO396VWxmRU3&Q7IXm`r3pjTdBRn`QRaoy4w|dD z;PbCvb#c3h+oJby7tcD5O8xESUv6Li@ojC}k`-K=!*wXM!M(HtADAb8Pe!Egdgxpi ztPer5r0&d!oevO-H(STndKQW@_Cxti9ZQG_U>fn_kyWe8$r`HRg*0-HXN{ zHfXetvf^KlS!~;mu&2-NsMGhT^WYR4t1b2Se(Sf|oF~>Z*|b3zH+7=ldX*_Q%y+tF zUR>)(nlrueP$T-zTuwFKzhPs$>!Amjs*J#s_564WSR&*oZO9WY8`~6C2|#@`_99Bt=Gk#J|eZ|0YM3fP=cJl)tQ$ ztJ=0ddtaKqpseKqm}my0a$?ly!Zxs5K#(BD7qPR-FQ4d)sW@WPRPvPBX82S)wvDq? zp$ZFV5>YMz9U^BNC$vSUNkCEtAzx-hd}IwXzXeXY>Rh@O1~!5i`BVhDKow(_$&r9W zPqO@|s~j+9`aNisN-olg89ZuI|B$OrsvZgqx(y;c&ge>D*!TJiQ5NWEicDlXl?^<~ z6_~_@WClVDNh_%>D?UepBtxt!D}M-`Ou-fUFyhwAWt%}%r@KE&xFpVybIv}MRW{Rb zdilVWtvXfX4@>EFvSNqgC207)?!Nol{Y+Bk`rxU00(~0I_}5Vu#ETS|r0Yqrpi`J- zfp&#CvAy8TGur_Ntzt)1cC|nFwQf61yN2Ionh=v$w=Hz+1mmUy3CnzT2G-qCM;y`q z%YXT+_Lu+Dd)k8=b}}nR4!W(<$yEHrbTzZ8Swo@j!pgRwL2+RsE`Z&&lQ+9^$HLqt3%Leiep^8PY%cWt53m09wu}doXv=&5_06E9!w0v9`f5Io z7hXh2U>}R#&zRv&$QKGJZX0G{SUR>oci0ld0VPj|Fg46EU==f*fLh6#6_9Ix0xDNA|)dWvBr0~t+xZ_ z?QO5S@cee+Ip^eofYZ*U$JW}CH}!@uHU628Ci0ZZoBGp7(Zoz}l5GcbwBvbPxAfh+ zA8A`6S3H(w-;o#@HFZg&KqaA*L9Keza@^8iAL8xe53S$8qxWHC%;C5y{=iq(BdZG6 zEa*~A(X53q&%&<=vw#hX(69WaMS|AzyJ6>(Fo7{%St76gsXJ zfY|PM2Bh*Xp!gv!Fy(ZQ1(6R~QGxXz00g9Y&abC}F_K^nW0b|PbkeexQPE+@fLTU0 zgfBaP^3ZhB#k4XNWYfwb2oQ=&&hh{=!8)WVAN1%LC8c^Qi%RWSp)wZ1-w=YW>BjPp`FOt=P zieE`iK7`R?qb-UVUK_d{WazduX=H48J2^+3_+$GKU+l&^S^U?Z-GG=$Wuk?cq7^TL z>7dP0AfQ_)!NtanEDz+m_;s(PIjF-Yb9DUnV0|r0{3D5ul66x9VM0-ZI>r*ifj^_e zoILjXB`g&rTyO@_*i@V32vKaBuJfU!p_82bEyjC2X1)`jZ5MpBS7?7|2+&j zkAHwGjySZoWVEk%MTEA zW`&l2%H*?SVjCFniF52!#Nss+iHjfw+B4&oNo1vz>##aqbea1+2Pw2)Qo!9%H$Nei>wFWB@Wo|z9_{=UMbe;N24M>Hc( zU({O|_T?ERM;)}f-NKu%H*DP8p5QK|)!cF}yE~7P?qf-OHZEWiW+qc8C9SQzJzAs` zaxg3Po;!}PSLQ`hSoCNf^=Lg=MK#IQ8P(O5S$e*U_RfYQWIQ$afHm#FZ9Cc%9QoOI z{OY!s+q}2JyNx3U`&KS$^A23mRvvv=+j8q|Z6}kJIo$n|on@?|Mw}{h)rX|Y7ipzuWH}>Ki}8pFsYR#^(v-dhF2S|3^M6+;Ub2DVNnJkVewDUW4u`NaV|dI zMLe=2=Th{=j0+wyOF!uPtj?}V@P;Ty^GwwEQ^iTkt3SOgHq~YxTs8LDn;mxx3kg7y z?jNs{vJE+BG4{q`_-ao7Jcn5Jsx11kQ8g1GVI&_N9gohdZ#GsTy9|Am=Qj!f(HX3- zjOStvjWWI&oBN6p6_z|D1Yx}L^3zE=k zl5Em=6nyI=3!B7J(-7>Rwh6fkAxLxX|x=ztOY3HP0Xg*dKG8jsDD%P z^vX>c&d|^^Q#GH@&>L(LJCBkqVy>TThVAky>@viu5!eu<4WDlgBE=I3O*~Nt55uUy92=7wa>%iqxQz=ts zT9PTRCK`#<3)}8tiZbYQni5r3+NMS#XBtJ}pq0?z_i_G}7tZkvh+Q;!&&BQp&jtpO zjhi;pct_{!*5FTvp3KIu@C2Hr#ESUaev%QR zrdWkK^hmb8vOtfI+1W#ppC5S_a5UqROMb6i{OSw1D?=G}R2hWjsg6_E3#g>x#HJ6X zBw{p0vQPfcW#_Z>(1Y6?p2F!%?>v<6VQddY@AWzfp6a=f+f?VwncqJ4$kE)itrRdZ$cAKzDR;l5F~j{lr*=Epk)4~(U;X)?)6N&>Naa8}=>ckQ;pmKTevHe^f_*B-HS)%7rZDxmfVTde z)1TGW@|I-pva$}uHKHf`ppCTKC?c5kCT{a+=4*f4$+KjhpdD`K1)gHFr#!ibF^D!X zbys!71GOvKw*zBkgHhe}iAeZm4^6vCD^Tl9`!hm}drYvOWZZj{7m5CY=l*P?J@2-S zh94>NHW@KEPazRqZmJ*l{sTdCmXCf=eL-TU+mw@(iLZUMx9?t%O|e%WHa!-7%9sVd z(lkog;+|#YXVJ@Yh#@47`csUZzj(**@LPR_b?B;oK4R^*`X4F#Mi(#!)sLXtz5;@u zvBmae11T}7tNQIgQCseYtOAGWk)9DAWyNf;m(n<`vX;;`%TVfO19_|9z$$Ls}6Z^H&_4WmH0bc(L4@Xs{P8T>H&9RW?oLvzsBnOEIa9gD+;<&RD>@ zWBbmw`3W9E#hZrb%;OxlbQQ5F`?i*Xs8QQQ#<)NrDxP=mgL%3(^ixkhjazk>WT#n& z3mNQ?bJZ#=`K{QSuNv6rsS7Z3!bp%HvzX_7yyLBJtGd^jSZ|RL5^{Jz_P8MSc=RIOq@g&y7cnxvfk0V!`c?x3jR5VIfhx!!=Z6Eo{kX#t5OXh;Wp^i)|elhl4YgT`=|<4I~k2e}~xj->)q=bWPjy*v3p=<3brM8TbrGxrSV| zMhI1Jg0oDK1>A0X>XA=t|MBf_Z3odVoz#gjgYpVw{l}zt+wUZHh^)k>Q@}o5}g%}vR`!}d~FG=bjU+q zl0TFurEV7atqU<0-WZ?K(x(2Su$@lc(poyyV;NtU#ISFpQ(G~V;;@BIR!x>g#=u5R z`5XL-Kr=m5*cC^aUd+rD1faLw7wf=YrhQ9PK>tq|qRer!WVDfMQcTED22q9ZQ;e(L z3T8$vU)D)RGrE+JBS*GII}1V&2+{!=YW+l2oKzVL0OgszsMn9*X(64?*6kLc7%mp^ zt0(#-kuz)8)m|t=rvL70g044*z;O+hg z9&BIv>et$p|MtCh%dJ1l(xSjES#$AgEtZm}Ji7*5ljnez5)B`##kE*Z=*!khqYt94>iG(zGyJ6CHgYz>1ARzU4P| zz^Lk6xT##{PAn_id1w1l1yXWVeH6F0iBmlfZ`|l?xgEkJ#ed=29x=8GZEvX$gwto_ zK)(7w>2+s4me2v1clI=a=Vg;? zVs5~pD$OHLzL)1PZnb~g8(!N^IOfQ<*lWgOR|TcV`fwe#Q$W7zLyNApNQTiL z)}j}Jo`Y=L_2rAF9Cuv%;*Ga*sB1}tBbrpHh~4N>mHPSuV|Yt_h@L##%F7pbI05bD zIr&YT_HmTT_TmNSPJG2y{>Z8lSI5VmWaDB!{*W#{Xzc95*J*{y32NOS-~{D~f)Vlc0=d{z0c{-<}xXl|pxquw)HF=-{QD~nTOTrvm zg(Y5Hs_XPx}%YG&7QF&0OBRe3is)Q$Gg#S>3`aAa3z@L4Y1 zF2-(QTC)Go#9wKBi`r+zpBzxucf)-n%Yb`zA-u9#}2ao<#7Z?Q|QXhUlr_abj2esHvPHyxs!?AHP`*5 z{qtu&*M9#G-pm9oCl3sgV5c<`9Aw0tI%F$Lcw#7p6Ti1@Cj(jj;dlH{4jM&nqC#vq zI^{QU5$fYSjBF{Vst#JdoEHze<1byxse)Wr&@rW=LQTwaI--Mg;|`_H+`v!1{vqiH zb%ZKoX(-e6NZ3|tAkrS6G0H2ePspug^0ao@igwRKkGA`H((6`kM_xIf34!%3P&aH7 z`M|XRz38tUQyErYk4V|FpVHv9;Swp5;t@u$!IS!kF_C0{<+m#hyLCvQ`~7eJS_RhiCKPe|ME590YNdpRx4 z8GOGh=45=NH+13Gv5Cy96#Uifsj^Xz4OlAddAx*2t(5qXrX z+c@r$nhTgI=a?T z$CvW7=v&v_*Umrg8ST~2|Fw3+fvegI>W>~1p6CTz>DThCv~bM(5gSYW*^7$>H}r`s zJmITIB3%AN-XKCBF&{CTgi;0vF^rG+nf8pBH8W@=A~GfdMnjZCuZ2T*XHDNwPI`Qe z2Bf-QsIN5THCAU&pDTZ4DK_JPZrX#5+I}iBzb;OS%t#(sZ4UC~bdP)qfl3^Fqqz*~ zpw4Ddfm%h+h%-a(G_}~lWlm6K9bD2P9a@}?J!SJP6D6FqKTi>dF_jKa95z=GfHTTc zFlbP+s8X8S;6Z7XMH{s2_NqL=gCGC=?|3tMc zJ?=v*BBc|CrTpVN|D1E$eZ1S}|M|c_aufl*>G+;kM^l$|6FF(_m>>izK3dmyJoB~l z)mK=Ujl-GrUQQdSA+_V)nt1NzwB8m5;Y&Y%MSI>^&uu4isN8>}z7NKoPT{SGtm(GW z**d8pX`R#&0Mb=Z<;Er-*u()%WG*2^RNhI#@jsaZdmgP!=vQm1#ctB?6urj1V9n+rmWDeaC={xqn95E_+| zYi$+R_Z+f6*Ko8uc*VeDJlBOcJFX~T>Wj0&Vo6i8m2_W{4-{jf|QO?!H1Co=E*;Qwg%J-&^jBQ^2#=!X8;3c`=}THKjI;j1QjvVFPBez1oXSz{G8J1VeA%$kmQgl(rYm zluX_eM4$3BbYn8H&}+WL3s1tCwmQ?=3*HRg8Z&H3eStw+8P|yE6WlU>H`f#W!>8{X0*`z3ciL+IM&b#+%PSms3C|@n-BfX`g9ZMxg-2 zm>3n-CqIP+QZLP)Br<9Dq5Z17wIfzrQ1uE(GFDGz6Ey8rik3qPKGwscAAJuSIQga`FXxa#T^4xa%o|%b`NKYw z6e;D`@QoC*Aqq2SR+>yX&_TTUH2_f#@(wzbbz&n21BGc=!v?L%5Xsf4?kvjWqa*5( zEzBuUj&0&ReOqrFnwKN$SXy*3!4#5FsIsN;iJAtBFkp*1s$%M$`awWjOahHwRoFqR zQ<<8j&F;HeR^nj>)w{T!{=3hA;ds$YUdDl8FQ=@**b%WW_iUtRHtyQ%>*qgn`{D8S zOa69z@b7+n{3|Y3eDC)@XPm+7-%sbpOs}VVFM$SYnKGI*)V0CR<=`>nqo4WchrICk z1dlhV<@&i<61(MtQhoqSW(Ff*cHGQ>t}e1C8xt`>y|ai7x2CW>rRG-u7C)cF2Mc0%>yyWB@;#H`0FJv!6Y#x%!&%idVmR z96Z9nuBw>Yts|eU5-4%XC@W1s$Rp+_XwSXC@>se$h~1h<`E*3$3B4DU4*3oe{PObN zjr_sf^M2(w#xMW+e;*f|cP?$n{VDo0+g`oPA59_z>Eu0t_!Nc*LdDfzC(X)^z1l}v zoH7#&Y%vdl2d;0nyLBhb*Ylr3{wlsNH_e{SB^Bf31OXPxNC}S`U65_=fQw!3fhsGx z0?H?4+P3YN%J$J%jRBxh4k@N3v4H@DY-3%rg`j!{%ja66Yk8cs&8GEv)Jl#s+A_rVM56UpU4b$9Mmp z<1~Kk?dK-s$V$KSAlS*XB3&XH8_ieg)GEw4>z$LugN%K5(ivX<&P&D%c`Nko`_CA6 zC$Z^cblkY5-7!)oEI`)X$ow!*nEkt#|M7UjeJ>tA`K<5a|Ecah?z!)boK;bg6RX-+ z{cuOO`n%NM#D3XUqR?6F_`Ud{j|1%ZG-nN!zJW7N;8Uk)6A5yYT|*t2{S5_NmXH{z z637n|t5IS}T}XN`)rA*DRA>BUQH00Hrj#;(~V{?b9G}tkNW@HA@&W3d6W5 zchNamWWWL*>w_n7*rT0J9F|QD#;hASaKU45&VTOxdxGa`a9b?dc#a zEy0Z5rOP0(qQ@l-MsSZZ`~Mg-@9+5b?;d~flGpGC)YHd-L)u~^%fdk6V8P9Ivd52z zaZuU~?P*L_j*VaVmERfP{k=cPALw1l&8%F0k$BmjrfnPnZDJ|@6q;GLvw8O9FgghX zEB4sd(XaK5l{l~0y5~D>lTO&`6gIkg((-A;w%axj+OE?+ZS{pt?qi;NX}bo#$pv7m z9Qw*^zNlBbw5x<36+UCS70NNOP6KTYm9gVT> z)F*$KeU!5%-}7Jo^Z5DKziC{z|4h!FXghJ(i!$)_IPhWM@c==0Ty78m- zE3bN&owGrbEJ}AtT=1E>5sw%j;VyO>A0S(mZB`T3OHRtr4|Xh-zsQ0Nyrqn3)*()W zuwCAsDFUVtMXX#%oMP>iFEa}?>i{E5Ca&yb1435vr!IYkw$mhLi#XbYZ;WfdlJ;<* zeeeSwIDY)OKQO-dK^Gy@gPmTxLf3DVY7ffn#_>TCf^&?gosM%b>N_R$?*s>K_uyG5 zj=da24|5>nhMw5(#Oyf!-+cSQ@ve8iXT0ydf0HM>(sab5@5#~59V_zEQPq3OV!N2n z)wyz9^$yM%l+l&^l?HflYXG5sfTy;u<*bBn#Jx9XQqJL#Qt8zmr{!a{$t(mORDtR= zcbXHmS=gFzDN9>y34PFjz#_C>W~*%QYeLy6N0g#X`O%U|)IFGBI=TKQF4_544GL9U zR%Fm~GSPA(X0G%HM#v&d{jOcy+fwuaCqwXJX!a{Jo36s3Vkd{b+`=#E-+$RF$3K1c z4&KPD3|5~S;|$x~{C&ikTtYYh+yMzWDIM)Ax*Le$A7|H-G)taRvp92S$ZM-#pm1m3-RU38O?28F?vh;vuCX zocN+~#;CjcUwZf@g>Eykk6xpPUXZZ?n2Xz-Ws|3j;cXe9bdy#;GcH1!o~T^;gP-!) zdCtW5`uLgzCqX3&p-N)A5tB_{T*_@-yl2+Oc-#B_W?Xsb*f`8PWqc?g_DeYZ%U<4T z$Equ@4cm4(e(pWSo8I@nOrUa~8f}uv9%RwSUXdrIkJ;icCBwLu74X^|d^@ zELC#Z{1WVD8j?1)QM7X-zvbv(Z2-2Hdm=$v5|)`z8fgkMxf zxC4^Tj;mLB28S01Ie`$bFJ3~3#Wg=R86~ZAyV}K~zIq+~5Ijy$Y?+8<-!{-bDXX`> z3adTK=G3R}@=514a_#Lzk-CCFSg+qol@`0Cb)9V+jE#~3M`>MBQpN(%D^3~(&>#iNY^lLBVG2BC3IxCO7yRaLfk!$R8 zEKsH&Dku2GeAEdD?Qj~GA^6Ks$T4*7=G6ajo+OgyglAl!%j^)_jx$TV@~Rug ztN-i`~tbJ&mjO#A^84p8C`%hy#h8(rQYH^Hj(o=O3fj)Hi$A$e&v-GBW>8Y}quRtju;tbR~xKHfT%Pt?c zagaNPB59oGp7iqR)z^*FxOsN(gYP#^@=lb4KFvjYWCDepy}U=|*gyQ!IC$Mn;lm8I zxq)uBZeRhhnQ;Yo2CsYmy2n0d{J^unE05OucIET|ebNTj_JxkyZ3o9Z1hNLFSU$;P z!PLdY1oo+irtq=~FR^WAC_1*QQ^nG@-7U6Xq63^`o^3&(Ll%t)4Ig%wpLSpppM$eG zMFx0=FM^dHBcej-k?$1sAbrb6)V-uANsT97v1irmQSj3{^JX`f%sHVwZ*n$%(|+~2 zq|T1d;$5~XMqATfkROJ?A)`+}YHoGTkj3v6+5GqfA&s(2V?59qzUr5LBPV1_X7}OO z#z;3-9#@PV#E~^^Cq7Gee3IRDho5p6RNdx@i~ib2%%6ggUQ}?}5F3GM`m)#2Mv1M% zZ^Fd4NvAfx%3nE|his@vzRfZ7NncjKmUhn)nms~I#HgDJV2v}jNZvJ}PzYjoq{p@> zmoKM(z-mlIHC$mX z-X*r34L$&-Uk>^VgpvwdaiE%VdM5|_BpF0@8l}5&Aoia|?c5F+wb6D-#E5la9w1ix z^fW7i;ygO_FZV@Yi+sqT+ISaF0vx^Zrg7vmw~Q0K_lCe~Fy9D^tfO4U zyYxdJ8*hKxUyTbdfW@P;0e3;(FR&)n<+L z^5uZ+B$ZQh*K)HUx1WxSdGz=q^h;@LNTW2r##womEhI-~yXP0Tvo~Z*XWe$~!3!Ah z*sSgh=)`fhLkD=|7k`ND!7HvB`|iAJ?D_nQ$GH!Az&OHd{P%Hr;yCc5*WNS^a~37} zgwLp9!E?u+X#*vJbHNVar$h7DFL=QC-+t)($34$GCudP=bE)OfFJnlcBewmp2>oeZ zOeG*~&$UDfLW0$nAm%nnkgR~o_C{Lb*nLQ&R*lM-IyA@{yWV$)jG)*M`}Dh1Pl!uL(KzH6zh+^Rmw%LF4%iQ3rkp9XPpE(8NDa|%zDm1z(4Om<@{^wbq* zZc>j@0`Hp7&nuv`WmB5C}Jl9A`^eh$Z}k8sa^^S1n2nTC26&+O%w)TVg@WR-&z45W)O1)SI%^B&Vp2~ z+J&=|>~hLv-z30Zde|6(j3gkg#bKD_jnU$i<;Jp5z6sK_r8)`=9@z<4=v$5x;WMF; z?;urCGEN;P;_SRrpGmttXi?*#b+4@2J3B{+H3`oXSMkb7B=Y`~Lk#Nv`m_&7*RG-x2L{Na6udH^2|w^P zXauev>E=>vY^RXdetDIj9FR}|TXv;vC2g#(*|8|akk}$^*&wby?a&5WMo7w3q%%81 z9=@IS1R0{pV-r148^hCiwDm`x`@_7)hvrD$%l2Za8j$IZLp!V0~F>)F=bC8Juec9KKf9l$6d1uT1 zac};N@!s5%;yo0(JfUJ0u&WCzX4D0N7wtHnd#PfRgFJBh(=jK}CKvGIyh&8ulB922 z(AMCAd9bq&YJJp7OFy8$lnc@q=QH@*E`mMX2%lM(V@p#0G#B1rrP=qvZ{ zb>?Z~|KMJ#`}3w_56Ffs2fFkKU2orzjR=B6roOmGu12K&Z9Tz&B1EMYS+zYATFPl> z>hd~n+o;rHum3-Fg!^)Ar|3{b1kQS-RmN-p(0pQpdbBSLVH1~JCdTe_*(lmPyC+tg zrxMxb>RVtjB{rYQ&90eJh71Da=!;A8O`EK;3RawZeM&(T06#vhw{F&#LWRMFQRNfL zS&GDfK4)yx9?2bLN~P_5%ChlUF_D=zPX<`6J?1|QEB;d2cG0h|&?(OPq6S%5!7FCtS>V1&4r;FP5bCkdD>UW0Zzr%FGHZ9^)5D=fHGoxB_Vut`6Y z7~$f8oU&v9++kxV)B&w$5Y_}Ea)Bk7(p#RNWz?FWD5GXdZ=f2u=r3YxLjqyaAVqy( zRtH(Sg!#xBR9n7HX$p*O>Z^}#OAX$yWB~MMwT~hopSJ1}JYi~Ny|m*^h8!+JrvN|x zDp?-l8J{l7imfP%I^``TDXq&NOQ(KI87{}NnJ&V#xf7{QGQ=U55mxD?kg7Jh&tR1! zZR5U(^sBh7tCl&&fOWi(@McvT{?L62f7?6en<|AB=`ouNYeNzq%;_-x*tFELd|n3{f3U?C|ugmwVK!|9)2gsojfQqvWsckn3QP?sk;*>At+1 z<9r^K_Hop!xa6}r(~EqF8yod*_?&uZ#=@&{=; z^v=H?cQZNhGLN!dZ67~r8)MFQwVmbN-sH(8LeH}KnD@ek6kBs}%f3FyW6a#{gXpXy z2gbks(H|a<{qiqm!qNkG+g%zK1DD{j+W`s=12YedyqGp~1s-AUQmV#H`$T4O!N>cYAH$`sBK-ynsjIAd#sb^Vx7}JIm9Gv=u`|%i%Bj!{hpKEABy(yN>i4y1? z_zl$rF4Wpr)m6rJYff+a|MX?`S=DTClO?$5ub%d2$LSq1mwoL2XZ%aP z5>8pEWcVvgVWA~{Dxh4#J8~+_SuykvSs~`8vhFRA(`Om`6MQE093?jPPLb}qpaeXm?HBS+e`7DU1&1-Yft9*G`NUBF zSIA$V{RQgykvHd3?^t4c@0et4G&V=>!=8uoG0wuCr6aC)+vvWjeq76-XH$StH);!aML*|j{~$F(h8_0JCn@- z06+jqL_t*OL}TWwaQxWMqtp2t5agZUX4)<7j2$%k?WMjP{NRgB;KtHHefPn;#t;23 z-#Nba$zR2vE%LuS!4+S79SgR(ugPny(2wQQc8~GGwcp%A2a2jsdw}J$%GQ@oA}o1q z1%|3qGY!|3vgZbbmZ4}*=r|x_j@U4zj7rV-#UF|V##jY%qMtLiTjI#|`yD8ZQ z%3wVuPTIWUH}{wtSSBaH8c2ODl;K$FuR&) zPoeHGe)$HN;}hBua&klIxzrWwX3%L=3c z(HHABG8PN$ zY$wa9vp0+oVPx&J!wNgaW)lr#1BZIMH~vvnTP(ArEzql6Bky+U%wFiUAq;G)ukjR? zUwGQ)8r|rh`xu~|B2ft!FPGapp7i*~kDvUBA7Q3`kOQZ@Qg9fAOd8u_wUaPKH$K?v zWer~$ERQ#fvep|&@MX2h#t&JMAp8)c>#zR$Z;hL8zMa0p6ANBL(7)uKydFGlUt^+H z56oIN7j2<$YMXYtB$O5@=*8+E!BPLzbvJO+;Au?C&KmdSQjhn%WP;^n!TT^sq&$Xf z4$cWg_SRi1pGh{C`nyAOvM-+r}C`ZH>d$w|Qz=KlEw! zNt}$I2B`U{sNd4Or>Iu^D6fDgRm30Ug+R=ye& z*xJizl&(TwxXN-X9<_Airv6AZw5v7M#*q)?jUYT}lz2#+?Rx{ZXbmyE=pl)1xJi zJb9!HHRXIXie>JCtCN_%fbzsf3v&&OP4?{|8=63;!!V@t#P{logV0obZB%R)NTQ$4 z>>%SJU3^O?Dy6my>p|JZ3@JRG%Wx6zz5|DbM@IG?JaVv+h_#lr+x8kW5@u(DQ*m7_ zThu-bJId-KdY(y9vn;WidVYxj%jFCB0Byxk*?xqZh%rU!N=p3Ggb|1pCT{d=(fkB7 zWy>Z!u=>9`v>@#bJG*rmYvs|3fD#)2w%-(=*tLC?A1JbjBjrtw0xm9QDC!hGmHSKo z9B$0~=5Kh~_^xmN2JV00nQ!gNvf?kL1G6?D6m;w~X4H>ylWcOEcdEgeh$n zhRC%lkMGLyrGD4na&Y|jpZi5#vCjXv1lJrhk9s!3d?9PVC3BJ+#FCpxGHIZmgDN!c z<*WZKw~Zn?az6CQD>>tH2LFk3#<(wMCz^ASoLJcZC2VZl-!VA6O0mgTyjTA~9{F`lJOD0*Dos`S-h<;K>Dw4?`)>L$di`67PhNG+_#I!uxaV~G6O$C+kr;n$ zfBf=;{ko9;Mr98c==07Q&;7pd9uL0X z{nGYfRf)=3bgTX@9|acP_BRgHZ7>!!xvUsTo16{Qg9dr0J&?KXT(-GhM9$12eDhI- z^_+Q7Z!xiT+8|65LOS=!I?DFJOjH(Ma3q#&hZrI)0jW8X|7vf6DsawN@GdX#MV3>%jEU{1U)7JA@{8vEO|b(Q~!F+QxFm zbIq}=KiF5hr$FFFa{Z7D;SxJ_g<|HUirT*AYb%sNA6#ln4O-vQ$toAyDPXm& zyxN>_n%I;RKO5w8mXOglr3b$hPMHcZ7Mu1^-3}S6?Q*$CMXpgc_h!LHcwmqKOJiW^ z8bliWHbP-@z$AdHVLD^BZamneux&Vly5!oqLIa(92hZY6=q72+)xO#k&!*iCm>tgp zAOVUWx(7Y=*hC;A(^Sx&EZw6qI-`4Y@T(2g4>7V5z!=cjMIlN%F!NpwpG07y(m@=! zpV&ALzOdEqh-*bWr3`=SSgY59tCnqZO8Z>`r_Xo~il><~n2IyTV%@Piz~o{Dd(nkc zo&!bdfZcE6O~p@}OTHEV=0^NMM?Uclee9L7jr$zHXIrSev9)c{g>VP0wns|AS8r*h zUu)XY2<_CT&Z%0s<=8Q43M)bQ$O&yX4EMg*h2uHj_3h)4U-A$RU=E6Sqpfr<2S6k5 zCj%pJd9-ym@5ngHBqgWr_v{*bxg??EoBYjzaMsOfKjxG77qCCVWaW*2`R;MayWY$D zOE{AQ%*n}mk(xJ3diG$&DrW`^nD*_oX$}H_LB;XzXAJbkdnrEri7OK8^SK;x5AI2E z&`vB8D$Dd4;)3(J>Co{fZ>j{A8>~4q;8}!CZrCqfiG{6(xWsfF{7YxM_6&`_>4S2m zO&lp^*|e{rfo4Ze$aBAoE*xiZHid^sxi96$OvL@Uy7?g6a;3Kaq`gpNU$U*!mink} z>Weyg8o!|U@}^_kt=Tjzr=@fp7Y znKaUa3mp3*lTE`fPMZ;EGg(d@I3FXgZzhl?XK(!UfxVZ04taKS*$j!fV~%zrM1W-z zkAKRje>-4->#KNHX0)LdKIOE})V2tW^Ytrz##qF6Zzx}5KKMzOexWSZ)kku<7aglk zF=d}r16o@wX*6;wYS~cTQ#>X$zDwWEee+>!OL#tqehODiY__cypJgnVanm+F*wh%e z+?a~dhx#E5!q~rf1ZAwy$KZC*0^7_lEGx{sz!bKpF5+rc;R<$1uq;?-jbTV)+}Ox5 zI7&?5qosN#i%c7htCKI0k!yt|?nd#}K4nC{QQon%CYB|;SymIpMQw02*1B>Mpw)p{#r(0E^Y&PED z=0KB0W*6nLY|lVMU0}7Nb*UQb(#H&BZN*G~rK{FiazJzFVp+Odo4i|KbWE~U&IC4k zc8y1W*_VwUe9m`{``zaP{FV=IBb%~e(z)rNSO+~j(Q#ghp35Nox5!@pZa#)U5Pzt- zSYDRPn+1N$yG}oQ{QNKc694UVZJvr@IGP^^)MS1JNn>xmY#m+YHE4u<>RO!O&~5(Q z#hY&bna7X!?A^taJ?G~=6UH(=nn#GsI|?}b?&48trW0Mgr{pv)Wm%`36}x)Bi3i_W zoZ;X2Fp<&fZY?SRFB0#*FF`*%V{+Pf@V)QDKhQFPH*zh6Fc=asg%rkYA z)KWfb_Hdhf6~OK0FD#r?_&Wc?Om-aG`r<&8Vfk7zF2bh~p4~a0w)xI)`1J=3eC6eVm83WfT3+$nF!(gxl8Pwz@!l6`b@1;? z>%m?=vx42m*J|21iHUq@)*`Lq19v}VC-$&whlYrXL4i<V^|Mr8~U+1#%sIBSP%*AhO-6ZJ74Ce0Gbbmb#U_Froe0zn7V>`;Iy#?djNJ> zdx55x44L^-?4VO4IJAVm2LT5Q+?EIV6H^DQ8%Uu#8YiIDC{{aK1*r;H8sEJO^a_)d z2<9Mz9Lw67Uxpy74?RX_(ihlD;M!oKhQ7ZfwKb4Y3VsfZiJ5!^-*P1K5{nhn2xhavX_s~?8-e~o+|$1HYxr}=tH+C8_F5hl z=2hq73`;JER)Xfc1PA^Qmmki;?vupiH12WnEtdM|BfqwLZ7+;%>c&#IrXOT;{mlo) z3xDs$C)Ptd-ZT-a_QUCyvk$%dbw4K;R&g+;kndjbvbmO5s z;gjFp&N5Ze>AQ#t4*dN-@1EoAy&U+7@kg(^3WBGk&w0N_8K7lJt72nrFG@OzM!%m4f@#u5J4g(xMSQY`dre(Lbwtr1H{94$_0fJpL zAqv4j*0$(=7yZ%flD;x0Uc#xCt7vQT8O%Vdh56(F2!?dokgssP8pwPyD<8I3&z?2c6;fR7TM9pRDl^{Y= zSV&-Da9@~qE|P%UOq~0e%HDu9Y|~N6LOTN#+AVMO2$aYL?o9;37-;BLKteXo~@Al^Ns?ABUP(|KoE zdE54f!3FusYnx7C2wWxG-u&FTgm$V9TpMF&2b*)xK4(1pS>MVp*+-8v_c>+a(x5WD z2|U8AAVB}J;};&Una?E|PVJw@Jri}UaZTrjuxFB#haWq9H>adI%;5O0zrJ+5^RM1L z4(GM+itk6RUiNAG39apz&cVZ9a}%=Rk-PDpr0DqIKmJ2*?A=fKK}=E{%yNbx{v%TfXO1s=(1UWXP4Lr1+0#!`pEf8bO<}Y^Q4 zq^Gc|6FPm=in-YMu|--fn>gV+m^Z~~NbAzB)gonA7YX8v{@|Q^-~x7&e4o(+?#IQx zzGy62_Kb>e+4e_V`XLh?|M>HK@@OCb0p!`a5SDXWmRVT#6k{Hmq>rs27|-_|IXLcn zKL0`up>4R>HhdI%d?-%*DaRuOXFLMFU9?Gmq3+jR^n&REU|V*KtG}?ZY?++AM*a@^ z)n6h=Rq&RsGM8L+iVJ^;i*J2VO~elQfylg;Sx!q~VDc=&w*-q^Ts={D@f%X&zDGw@ ztr50EeBEKBzb0eggn9ZY_k==g0;~g_fm70 z*e%bTZDEUx!(JqB?1gKZ5pU(!{z$1GNw^sZi5GboH}KjPCE0A{uCqgliOlLoN*hr= zVJF77Jo6j#YW$b{(JRL_H}UVMG?bktLyq=`Pv4{ulbZYuPJ{m1j% zhC64uVhYp?zUtDms#%|%SpKW$@`sLBd}>_wiL1G<$b+E+2a7t%OYg04YLIK+;I+*s zdvbNy|vI7v9GWZzH)Cq$xN4w>&qvOwB^M>*8hdp#W?28}5w^=Vm@bahn65!kU*wTM9?Kv2*kyUl`aFK+-<$W2I4E?|A+@flXZEF8HqRUP zZJ$$?`4HjgRBziejs>jV@PTVm1*WP@~IG_7eJUe4-{OG73i#Z@`Y?j7gMq{H-`u=Drz5fgJ$A9|j@e$6Z9XgKB z*ct1V+=xRT6OgpM18;n>Jy1*j>^b|aabhq1RKL7mX_}BYApLRQuNM8}nn#r+*T7nLC z@NCOsTw=4ik8|we>uP)DL_~D7y!fIdph;vb7CCZaGk_jju(q*cGn z`S@;3%s<$q3T5bPfBX^2>br@Je||&&*~?jvr#$kJ<1T{#%D;Kfxcv4zX@Jw|1p*baN-AqC5T+=JO`t5ssl3v;G^ZQCLM9K)#&I1pBS#oS8RHOSIcgNTsS|nl zf(4emkVx7X4e2TipSIOMyL2h7fDrVC~h_ntD(pFCNWn(b3d(l&6(FmI$qUR>n>YX+fuLlxJHqExqqRi!V06`0* z7IPX9c@qr4k)c1+FL6yUXeb9i`VVOj?kl3t;dg3zM2q$cp!1XH_>7^;{ zW+0JD$JrY;Cy}Wl%a7kIbFZ!bURA2}4%+UQ?!fgQ#+`D7ulPqU6wxCu>XTa)68F0&H=y#y zT|~3Uw=+hySyW}{FYD5++^K8nnbnBWi1-M7@`l(fo1}9Al%nx;+|fR1<@Ml`Vklui zar99)lOA^8`;FIfDP}JRmQUYs{dmO17sbxaWnY~}lrK(;L`A5#;zO%~nRV%-daO-H zIg9e{_q=z!`8}78JCBevp2km}NpFrKC&Cc84iHtB1Gx8SY484Xd9sK9@UdSWy7kW4 zC*>E940#h2^WgY6okwSX+=^)&rY+^9m#COHGLlS|Mk0$C&X+#w?>0W;r%jM~(sX zIaT$=ZSq74bjX7~b$=-*IWgZHO(}J(Xwg**`D6oW;FLPkHcz$9FyHN#n8i zzyG+;1^0+v$NJE#VDZN8f(13-p@Sx8F97DaOTFPX#zBKozf2i%LLki01s6TKZ~v)G zIl>b!krVw!Ct`xiF@5q%z4Br0g{@`5+{YUD8f`?yVC@meYP?-1^R`rgbyL=sHP1|) z(ik_F))ZTPl~g?u&)MXSV~eS}?VL5ufQX9}lEj3lps3J5a^@rlSUG~zIY?rB3N=7_ z5&6K>Bx~InU;^o0vJyv5RqLHJa7hrVopJ?%j~k^?e>y^BmDaZ=Mh?-+ffO1)5y@7b z?Ziz|Et&G9vvU|}m-uNtB2%wvo$;T`o&Jx0?3a%RKJb3y8=wBv@ms(Bg7G((ewaJa zxe#Q(Lv;Kh7JK)c;D)(fq1B_8PY@krl=a$?%h zq(_$Rf<2zbKS`%c5~cIXS)8tzb{?p+U`O0zV@xyYany#!&L?e7nNljD{TOGd^6i2h zhI~q_-ic~k)JLUiawbeM1PJ@Ymwhe?N2qOKKt&fHTQm@iAHUtqb_-4`)B;8uH$F zx###?>)6GaierZkK^v89+E-!~K$-^Irr(l?fYwLDZ|8+!@3`bWedhK4x5|c*-U-9VbQH+&jfwu=38?=iW9Iey#IZ3dfJ;=Bi;To<;j)2z|P3g z2`^JxiJ?+(86{7w)sEBrr90!SIUX!Li2CT{W+E#uG8B{CG$sjzV0-~Dj^C-d)ZsxW zqne&joZgQwxG!%9#-20JJa>HRh8xC7&OEpz*6wGVv=wbLA?u&82F-z;+@>mt8|}aD zhMUHp|K;0}do!17xbcUh>SK5d-MW8n_uu}S+w+t@^tdD9?;#_>sV2?Mw665cr0 zD%qtAUrxYb5>!dT_Y z2cQ#E&$4Gs4NqwkQhk%Vf)e*mWN8^<=mA<@V{89eXQ@ICr+#U#4}IwP!?(YEyyNnZj|2QOa(+B&hrZ32pH9ww5&ipred;%jr*Nk8 ze*8ISCX~8F{;&A!S}y0tJ3||XHul>#P{h^*BQ<3J4-Tx(8t}wOaK{hfnS{whIgx=h zzRFY4U|j-*PT;KRPbzRO+Q}E4ieQYBs`4wcEOW9nai<>tYoGQe?4EE{q*8LeR}oWGxm*t|L_0Jc;>f!(>U{t)B9&w>4aKAXG5NkHT%mrzjEROnw(77p$Kt? z43)DOop4(B;48g0#&+AoNeG|N>oXuN_d5pvAa5A0dr*P$ay%S)vR4 ztmYS6g&;Z#wIKej`ZOSL4=FCOGX*+1gHH2lb`WfBXO2~6H(n|wF>Dp{qb6v}G-mCw zL))`L<;3+24m98VP2V^!zx?v?`!9MGp*lSnS@JYbL`cab}IdEmVo##7aa zmDM=_D|^L%P4w|JxpLVjuNeDzft!=f2VHbAabb|>ARL3>SE4d~fn5GjkFz?tJPR;! zuUg90tGy2V@^`WW0tx%;YF|KLuOntEjZ~KI}Vf-|5Mb+i$%MUziM(qWP{Y^lP#f-oC%FC%59Lu%#eowc?lWLxEH2e z+l#QJtZgX^n`|Y9TwI{ix;Agz8&~^4eQ}&!N=0d1_bEs4HfL;8J@BJ{sj-*V6p#2t zLEX4$hIrSEnc)4dWls$)#BvB{z%y7aO7^iY!YX4U2W!@>w#rJYff^c_Sf@QTw481? zMq9zb3{DpLh?71pgYPN5%F96riPC6TY|~jj9Sng}&cZI@1CnC9Xq(F#6kCnZ(m80b zEImdvfmJsjyKYhgF*QZIDYX*cJ+SR#0K5mY@aI1F2gg@)%JJ8q_Z#Dq_r2eKG#>wi z$Bdu(>3=^y|MTxN_VJ4K#-Q?}5B&~I2Yq1?0d)qic#Vqn1gZ7@YA^xC*+KiS$o0V} zq|@Voc5Auv+o37yyzy?MC1}bIUjHDMqr;V@NCd8=P?jLH)3%VIFL5*<=zMb~x$>fC zyX0>>sKj3)nxmDWi{|Q7mHoyX*rlw{$&DR~!O(O%^HSQ$7J=eIBOC;BHf>Hl69V39 zdKUj;`>%fVN5OE=e2lHz$?Z>1vXaC??2T$|Eq0$LQ7yU&Gj%dGp{t=sRApXde zpBy(sb~^DqQ#)AxD5U|Z0(QJ$YrW3P4y1D&%b zhq=UYfX8N?oc`47{&M_hWbMJh4p{gKqdBRJbztm7m;(oA51;jI&m2GaJz(`u)8GBWC&p(F5@*`o`8+IH%|YgD$(OjRDX!U>Tj&lRAn!OiaRz5m zcnZm1?8k2BPbg8C2|N3K2IIiVal!86<5&KlpUb;n@_2T9^Z?%et4#H%%3O7yML3J~ z#2>=ou|tEq1ty%e{YnlOn4!~Vb0y@ zN$(7D%X?Gw#9`x1?k~ESOX<&e{Nu*sAMx<<;y3=qc>UjhICkHgd(3|L8Q(m<=CO|& z7clYMzidUGF6dvz56zn;7V}G=@f5kbxB7<~q{~4&1tmi>BV|aV zUpe3_iLHeq1CJV6U1mki%rB|OeSMjtzHUVPq@q4HI&_Zth zE~_0&Qx-ODQKGWlcE&(|6OT|8Pf-p`s=Uxh2S|G}aEqgyBZ&+dxu`l& zQ!}ojv&re^=FO6&>=i5Js!{TVPiF?9Up@l6#r4-zbZerl3LqHu#Va@b0_NV0bNFTa z%x`|?c>krpFm};S(%BZ_Vs{#ogu@))^#D2IORgsJA$E#CNLRyK=96{5oX9vyD%A zts6Pt>vP4GpBlgMf)|ZzZ$HGO1-)94c0wI;7p;T81FPeNaXhXMN9;f3>G6I1a;2Q^ zgxAv!V-d%V3qVcQ_TAhv@CQ8h{iToivhma>KZ!WcF$nVJ2L*V1TINb7^`E-pdTxF^ zIX?KF5AK+xL95QlMX;7ouKmVP8P(^YpGQZX$Y5{Ic3{UzPWKzTyf_X1_@@NLvE~h^ zRFq{K`>WVI&IkOSGtc1hW1b396*tWuKX|Y%YrFRFaeI`fOP>C%PapTKwfWJk&4&ZKOJbG% zDr;Ho@Jw4`q*7(UlA<~+uxJ}wqQin+g<9F-v@t&T6jbtxI<#>=vL=08LIV3r+WP6l z!&l&otIftG)y9mrspY03XFTr3# zNDnYQ@oU?(+sxoZp5wvhM@Hfu#`teloso+i>+G_`mcVALXIc0(xALiXLg*g;)n1g> z4<(8bzOi4r#CU3Bm;NQ0wA2@^RRL*+jb`!1WQ*d+gDyXkN8w)1Hgha%_|eKI3J}iua(RZbU?eZJk6SCxQbSIgzOnzL|6&EBcHb zWqhxMkiaE=Tb5=ELx|M!(RH;s$xdCLs)Vvmx~$$t(cEy7wNNlOuLVS<*`mCaQ}BTE ztbzmb$3AxXc=;<{Nj$t6Qr&7(hI~vOIk4~Ig>K$pbb?>d-aNaDbugh^ert9Phcd^n z<-Y!~MpV2tTwEy%yeq4JCiPXZSnxH zX?Js&2xtpL##eq1GF)NeateDU=r4THOU9Rd>BGl;F1jf9bgcHY-Sy2giC_A#hmQ|l z$^9tsoS;oO4MRb!Jx_C;Fln#hf-m7~&e`9B!RI{CfxVE(6#m2lyLRdoAD@|wr5}XW z_LEW7eY#6sdzqx1mOmHd?3$PSqMH~yk;6uFz{%0>GtVCH{@cGBU-h_0jc@qMuN7IQ2XV!;un{kbs`oFzKT2OEn* zL^d{|rrMTJ*Ah72pDqp5HH`t+DTYuVFN4 zfym_&CS$eIl2^8$0{Ny5aH{vn;JK5Q?3WxwLowemPPg8)=HvBw%^>0*ugSYxO-VtNW~+M_x$ zPY9`AlvbUQGuur$ej+1C@^Ma%)vqm84pr(kdXGU|igm#9S~ReOZBn7hMew6|5oKS) zrb$3l!cL<#W-gYOqXB7yfq*bUTKW^1+FOTql%ZJxwezSa4r%t(XMQB{g_%5omrngC zjd}+=OG!Fr#qnRfi!#`yk=zH~WVE}xc(Yba)rb_p$KP4E7N641TaJEs8L;ep?(K-N zE4Ct2t7yDq{!-^ONf*IFto^an_1oe(SmH zc686|^pXZP!t8OVzUXr(4GtWwt6zDsoo!^hhc@)t>nDes@Pk99<;I`9O0485yRuXP z87sWTK^py(W-j_xy+H$Q+_(D@t^uq1OWXW+lA?p?Gv?fw{15;5@$pN)@*CqHKXDbl za~35w+vdch2F12rwB7L|hj>rQnY?Y3dx`ut?HLQ`6`4(Vr%jf%T^xe6MZY|V2iDEx z)9&S&Wx%veCP|PhCT$6C@|1o20IuAqCvN+Ahs%BLec?Ed8?p6c;go0j2>+1$zW0A% zyy>lfHIAHiCQ5wztae643@(ozXclnjot8zUzy zC&ovK^IyL89pn3-{VdL8c~%2j@!`?aljAXudDQsbm;L!TxtDu{$j_e1sqPxH^}qG7 zQB;$3v%YqzYka=FE)^o`>L>XUSN7>!;EcVtxhDOQrKS6PB8!R2F%E18xmh{=+fecG zOqTssU%UzXs$1?Jzw&~YjB}Zce&ypH&y!qSW)iPm=+G=HEvKeILZdg75z9iyX{|A~EboTjJ^TOGfyk-%|CZBzWSEV)JgC-k)hn z$z{K&r^=*<^8ruQswBjGsA@AE(BKvbxpiq6=;ks=4pICPu9Ek*D3^ z4_D-dPXUTzXWY!8)yFr!PM_smIJ;O_wK?!^qDzEgrJ3y!K4_Eg;?P+?(W!vQqYR>% zT6vMK>C5Xdip#f!IM8}zs#83%+xl3)#H+8VMcYGbJ8vDQ9C_>`gn(pbn#2Gl?W8ea zP=$WMQV&!u#YG+tsyPQ2$_8iYkvEAKArp>16{SJcZN#H@E7NZ8$KWhsg_txR(9{QN zvru0)ZD14-X%G^KDPAh9IJMEhCYe$6zEfHU3E^{L%ve4}>wK;I7 z-5}FHPf_|Vlq!)~xN=O5`qOeR3FVZxcsf|tP)!~aL;GCZS!PW_uurUZ;59kdbSHgJ z|1DdHfiAIjF(&3pt=ZxvFr~$1i|P#%ILi|UQ~4%7sFrO{?=|pIQO_(LbK=H&l*z!V z3p>i>!nimH=xbvYH_#v!+8&71&eMPz`Loi@jwM}7GtWlEk`j)de5ar3^6-~62yjDsh6gxZ4vD6DDXJ7}{y zkhdkLP5L1o1z-Xa9J?i(fjf=Sdr%U`rnc zt^Ix|&n4T#pSf9v^4Y;Aztq}#1Y17w**nKC|NcwHhc5d_ z@;W1Sa$>P|aPHWo{R-LIU|f2$v;Bd*<(?d026&YFa(vB!tL5|$`wsXcJo4;W!o<`z z(g&8jZzhAX7OsUd6@4`XiEVr-g{+CY9JW!~sl17sWa@BHmFto&E<%kt72N9}D{&V| ziW6@wV(_`TDiEO|1n0g=(l0XcpFGv;*|^djfiC}bMm>vhw*Wko4ck@I8-SLlUcuW1 zF1{Jt97DDq&bS#n+AQM>T$IiKtoZElP!&1GT1b3Lr(or_{gn~+sE(U%=F)c9_LJ4V zQUr(Sojg<(-$k{tZvl3~(HjHxY{vp=`eba_;sS##mdGHl%iK?L92T_2>3`w`DBumM zF~}pxMSwwYd8J19v`m32wu{s_Q1v=CWD^Typrbo@$vpXukOy5wu7k1V%C*4SO}0yy z&;ZaM5@kCY68Y6BmfUT_2$}Y!Bo7kda}ZMhYB&SKIG;rg`YtYJlORo0Ot2G8L}qqS zEPZw$PrYh$L(&7Jy{xZJB%d$22*0APa7Is8!!W2i1Jv^7lq9`M}TVYSg@ z_YI+*1<)RlIO9G$jViiQaM6yHcne3I z12?|L$j{`DzfmId#34{?lDv`8NyDwT-8wG0UJSLDn?8NxD5n8JlXii07ccHGhU&P z+;1e<4#`<-!r0Gx)liov_Q}jmr3daDPk!>hz%C{!=#Ma&SvY8l>JyQ&Y=<+j(IWwHR>P9G0@6!i zC-65v(oJAt=^f;W+=ziBP1a_R);b#Jg{v7lYGxHD; zjz5SxN;`bynp?*6e*cfgfBrW=J|6Ob2XVE~_j!0=l8_QRX=6Kk%~yXSh%?6+BMuxs zI$rv+SB$&vLMNE~8#iZj!iTQiUS4*OzTF6X=odU_JmImA9%to0hSb(&Qc9F0ELq~( z7T|?T>!h8XZJ0P2d~IZ~aDuXne!ooiuSDd_%lRm0QHVembIvD~GNF)ZGA1sez#eUy zi}_Ta^_OUhUdWWSsw|l=i=9ELnDW$<*&6mmY))+oTm7&12*%*DV4t;kVp)4x+@;(q zF);F764TEqZ}MLF@^9`Hk(g(1xzWx`_W)f$qQ9a~8|X2|3 z;o(|3h0SvANpVuKaAl%C1GsTEA`r+|+9d51q`IXlcoQ7^rCEw>dxIS(bJQb2;B%=C zB@{2rU+zLGw+R)xoiddeUrW2uB~7HccOX)t!O<>h`9%1sI(2y^fr;G^nAmipNw2(c zDQoHo&T{nB&m_Bm0842(os4QB>R>e<;GB4f@%eu5L-EOi*rN;v@g$)7C1X)t&Q67$ zW_Cb=KVFN3PoFa`NMpO?`l-A|AK#>*l!by&2Z%gs>%cu_L~01Nf>dOe310(KSnP6A z!{7{lnn?j4wLW{w@|X~lK&%zP1riGq$X|Z%diN#cdB6Gm@fq%wa1v1#V<0^;u}QO{ z6aTn#WSo8Wx#LHF^tm0N!Qq0fAHwLKdi25gsOuOneY)+AJIC{W^*6>lFS(R6H4dCT zpj2tC%p_2SQ=Sv0g*dg~^oy z@h1awXtdV%g{TkHcIwwDrNC3$iuBT;x|2!ZasY~djip?+Qu0<_XpN1~Lp-nZ)^}ez zzW(bzF#g3?Jbvuwtd#r>R_%VuSAE6!qu0M3TgdejT$X~>gNbrnOo`aV-hN-x<;5-> z(Arw=1fsU)jNufJw6sskNJGUJbtG==E%ULEoJQEbv(FfN&N(ysj`K)5r!hc_S3au^ z4vQUe+2f_$1NipdEB<->>hoVb{?kwX#JKl`7t%%!$&E;9VNC&8><>Efee3iq{^V8T zg@5*X&dy*62O$4tJ2wMsB|4p?=)DH`ChPm(3~%j=FBg5O?}-egjlqFHft-{GZ;=y);77W(uK_{w|QoN85~w zP6}h=8Jt8IkgePaw-srOX%KzK6bd|}Vstt+lngwJd)M@JZ6+3~2!Hx@^)@n;k(05a zMF_Po7G`t;VsGr{mw`+<{YC+g;nTz_)ZXR@N3o3d$%+8)%y!>d`jq>P~%*9g&;k*qkx~Zg7fYFP?2+!l^}15ebS7(PY%R zkjoTUoQe~LYrJa)FZQ+AdL4@$2r)tHLnK9i8g0sr9pSI;daMp)!Wh5Pd1{AJv>tSP z)4%*Gu+Fc;RgZnfCWNI8FZJN4tRzvQx?KF_WpexU$WaEcj1j`A+c}+2Cq;DBMLXHa zr|#-)$F#GG4;lQSoNO7$ganTd5hcHaPgi5Oe#J`WnVV43nY1@HsoRQ?f~IXaakEpW z+bx<F50~Ob3aWE{x~nZy7H4(j@Q5O&ExV-+JdEa0kY12TzP&|Ly0G>#pa_0GDpM%=qY^_Qs~dJOd@&Ar34r_`Mg8 zH@)qz#~pWjxq*XVMg(k~BST$Tvh;(pBN%U^WR{abdk_B)vYUHAT>R0J%RHfU;}i-$ z1lhh>DQDvB9#y7#*R&c`jg>7Ry+LYjwGGqM0V7wFTow%+o>_Y0qrW`w-r1eA9LOPO zJV-d@d)}}A=D3x|Z}VR@9Mzq$<$$Cd4bmPqIpD`-a!kjvIw@vRA9dH}o@MPcAKB-C zG5d=~`t^)TN@|48~t`!d~V}1tU!vlO<^f`z z*mD6d^aFS3rkmkwE<4^3fm}+mzxjzC^vmi0Zr#NXptpVaW8=48_@Z&!op;7(t&1=U z()-Ey3DhTf9CwV(O`q{h%!|eioK3m$@Ud~d`$I>^4F?a88(DAQG4dM@92z&=b!c3F z=iTEP`pO4CdDVFPd*3&%zv-sjc)Hr#cxb?im%mt54c#&T4kJR?0N~@sAvj!EKkc5B2`QR1piTViQ}WCywe>vktnzN@QZAE2lKaoD`#TjW$3!P!GgU41Y`rjB+@EiS! zExPPnKEBC|^J|3r==XqGn==`3kaN`**5$K}qc$7}{je(sKs!f7wSAV5co^{t1 z=gO-e#%tLUUx?kNEwRr-`1J2`0Ef}XW{ z&1+vbUh{^xj9d5z+V=up`cQG*t8M0kylGrT_ zi3Fy;YfEBf8(R*lF-uM)e(e*hlQ*xb+ji;XkhYmVwyhVQ1L45~9qrInM%JfXsgw&^L+Gu36 zH-7acc_$`*Nh6L+BE}%RZQsbD?gZ<&dnQngg;M>%XPfa^@oyfOJ;anVKD&8qtS<|@ zo6CjAnJ{=6&;zLLk~1Lq+rOYQ002M$NklRtBi4hTNgi9g%}=62ava1|l*H zau|exUyUUF&ZP&x7?D`mND6R_cB$lmx(R-V4bZ5`tv(nc|BuHS_wqNqZM^Qy zZyR?U=tL!{5F0cuA|k_=^nKuiA0F@jhbzXNPSTxh=OAgnK#xLQ*x61#a&4@&K$+32m1kbrlnaRe)#0ed@V2|Z%Vk*P|*hGlHdIb>#tXBIcTe9wSs0M^C z9kMbKX4S}-08%C+B2BMZ)-_=9UOSdJeC+)=uAiW@wUJ|nT=^s~%&@SrN&G7#9c}4| zjOy(@Fl|hu7yZbPUf*40i~rcO`TrB(;+uq0O&ikaFihK{WztRc@n4}15PqO#S#4D8gj%jk<|UB42n_eD|B zn2jANHX$gd+vNnbV$>HtDF+}+pI|YWOD9zN_@_;j?Vjq=cb_)7^|sr_>;L>O#^qOh zn!#f~;|@MR7g`2oNLjESK+DSMgh92Ai3j+fzu$k+i^oI1_>0Dc_vD{{op?wLk8;%S zlRTGw>>tPP@l?r2KK^MYM4SN;6TCE*+a9V+E)uuIk2;ZJMIRHLW89bGFKK^K`;^aV zyL>%(uJ?6y^ zk9hF?`CEnya&LmaT#oTVt3&+1&|BX87JU8`PdUgdz1qz@^JMB3f&meUecHTUabIAv zAv}nVoE~rhDUd|m#2g`gAsTRPbp@~0)}voD-cqeB4vVZXY>6{T`gnaKOHUcj0oG#cq{2NNd9;f9Zq9Ao?pfb5n&P>oEJPxLoA??;n_Q@}hr8sx(%`Qp#CK%8K`gnTx3SVhN#s zvRGD$tINWf+xRUwuOL8fWFa*DE4CvW9md;88(S;3t7s>vwjCwlw43>+Q(5qr_N;Cx zrhWyoY~TSGr@ZMTJ1_eXV|4CkoKS+C>FMB9s|-24r!RI@lGXn=FEguJ_{#ZBx+>jGP#|$ zCP$!EXFQUYm_$EiB?U*h$2;9OR@kRc@~!?36!4A({x}9$pIjx;cpFz4(Lrg&PW^tM zmF(07d6h4bF~!X(AUo}4sZj<{^I6$Ul*p{B-u>1P`8UKO*BW@Wqd4&VdU6C@HcZ~> zrZsf!5MWXLSZ{GH+PkQ-pKA0{(UG@kb~sw!%h`xbiP5ojV+mgkmiP#q$|65yH-c-A zW_Z%Ir(O$f=LlJn+9az_wbQQYgt0LY=2D+i*0vOCv6@V{Xa*iF`Z|4RY@(!cWS5SO zIpl#uU1?+M5kBj1EKaS64ia)U*Myj6z&3aarL-`mDV53EvBQ}-yy8z@!>M=g?ch?5 zwnVVhm9mi~C`)}6tv?429UGVZ!^Nfx1outKA%012RporkD5s@)Rh}_hClDCYW;1v9EeBaAKHG>57jop+t`B%Cw zabPuSvuu2mFqKJR9H4SdQ0&ohh)o^qSP(dev!jQ(3H7NP=|Y4qh{I@`p{vIgVeNMpK^u(!t|Zc3)5~llTv(O!n@ecF2_i^ zTY0(M`~LRB<8Qg-cZ9!#uuYww?d3Ah7xFK*7hP~Je;U^b>*80?>aUGh0D+}XReTgG z%dstOssL8i%kPgO@c{~#ECe+$! z8PhU>QDhZxiDuXM=ruQt7ro-o#>YQ>)&IxVd%)>+o%fjsm;na80|Y<PE2}#MAosm-`v)ZS>^0oFN+wH|!^;H1CBUlGbZy7qI~o(0Ct7*`o`XI1Dd44Z4IYO7hEc?q&Ks{O#hv6qpLq7u*5p!jdLGhE^b` zqpBV*Q!-uL1e7lE7k&9Lef=5L(GHYxJQA#()S6nv0Z+Dq&e zIB|supV6UDgDi&?rMqlSf9Xxxy(=%u2F2OqO*pjmo6q9nklA(<-zCW!40v#{*5~D6@EJ&)vFDpJgfiM zOXoJObZnk>`P$i`ao)X->)eonPh-ne%AjmQGv!6OO~;2phxmNLbVNA4{~%w*W=#`r zfbt@d{ONUp%=ISR+ZDR+Y`}bWciw4E(tY5$P3=!V`I)wdj})F{*IDQQC*_y7ZEN5D z@lV<=zM9Q}1L51c!=jQtLo_XOFo8bJqf#$V?72f5TQke__qX(WcMnHMM2|mK0gWpAJ*{4^+82g8pjUZinW($ZBe6K_t~9A* z!L13^Tfc6!q#YGRWLCCXYeJk1nI>VKen_KCj;0*X89l;R8floeWVZfONnO72thTa~ z$#4Wyw!Tb9zD|Z-m#+#7e5YsNd`}v{X%qZK7s46e{Y9~m>G?8zGM~LZQpQY!JlSUWJ`Gst*(4@aj2vO z70JY16^HFkde0iWw=mt+Gwl=Fxf1Pp$_ zjFCXLQb-MAOMY{s!`6 z3>Dn*0#KJB(yq#l6u5H-gFl8KuSz42R-@6dtJO=1_yw|{*Hl)e zLx#{0pAaSA7$Xs1{p#1-eT;0JVgYQSV3kQHe{jiGtX5`pScrLq--=5t7jR>6b5XZ zb|i%cimpz+ip+u#Dx97-b*N6oZ`#h*P=Js%#T6tZV5T~jGNg`;;R2r(TD`Fr zbksn+`WdFeUw`@9w&I+nHPu6)W$T3(Hnwkm_b$E>#P?;;UEX>vZ^%4B$Na5432!Nn zJy~UffJOlg7n!D`6IlsYK{1$WIKxaA79B(ab*BvQ@3R*ceN-^N47-nUh~AHW@*t=G zZb&CZ4ve2WdrrIc=GXDPpyMPp^Ewp4Lx{Y~^B{uw7#=z^Pk~eK{*k)Ebf^OtHFfg# zpHv8Z(i4yD$$Ld*RA>6KMT~&(Ce8@TI6G*Zc9F+H{nN?bH)gWv73$%!csC7!;b)A_ z%J=Snlrv-gq#b4y$q$P^+66II(T9pem`fRVS}Z+w6C^>;_1 z4-UDCBc_N%mV^bhxJ&*d0;Z>tk*$#H_z<2lVx9sdA4K>Urh1xPSf?;UCFW)Rp@q76 zgbDmUZO~F60GTKl0V*0Dot|c&@i7Q#r7|y``raWvuwhc6A-zesJ35K$Xcv9p<%c`` z@hpQcIYS?Ldm{l&$=@|5#1K%KXPlRKLcFJgH}X!R(bEdg;kWM@s_W{RYlbIS6EQ}C zaGJApF!5Ac8zpxMcs_yI0%7E*5|EZYf1($8=qit%zrc{lO4SMt*fePHldvA1Xa8m? zLS+$7{RK4O5-R)_>fS`kR!W!3lfuZe;$>KO$=iQZg^=#A*Q+p90UqY$(+_>1sWZc&MKcuyovnO@8zQwBFZpnk=dOZyY?OAgJF-hJ^S`$SHuZALf`o2x7tTP z_UCQS!Q-6D374=MTS#`xUxgL_#b3clN4;1Pqc?TLGdAJw89FIb$gi_BUPJ8iaUFsD z^&gT;CUg~UbQk^f_D4EkrJf=TK|2lVR0d-o5zV}TL2r-~G`LNlj*x*gY`5`km^r-V z7mqCme*NSp_h%X`TaHN?^pO~I!yi4w50YMJ(DmVE+sI|-gnO#roT5>M$iQx@^8_!1 zX+(K;jH-|5s~iKj(YYiWrP;|FZoAoSbr%bp_w3mlSm8!SM%u*}UC@>t%NoP2SD-6b=p=nX__PUN%l1KFrqW zQG$`T6Q*FxKExJ0&=MzRU=&_UVCh~UFd zKHc_s!Z9O%$yakSnG!p_VkT+pbY!5GtnScSp ze<=?!aFn3sR+;(*io4@QH_pVVu)?Jly}3G?L?kGok-g7&Lc)*qm8PxiL$r0`YY16~NLPF5LOrRiZx zA0W~pXW>s6InoZwVC0nMD-|^Lf(;Y8@HUTp=0%FsRaaWrl#^t6idozx&Z`Vs9Lk7% z>T=JX-EEiWOfVi{Sd6=;1Dt8AaEIm6fxPE74{EeVV$xGr9v8lA*S@xW_a3HrPPC_< zeWv}F-}`Xe%HeQU^y)Y)z;JIDWsgP`fAuU|P!=^(H1$b8k9Lyjo>NRCd1U|0C5tEQ zRDh1hSK|af@@ZSjTf8Xe1Z}5k@Rh!mA9@oyFyDIlpVg|G8M z+cL0a%eMA~JMQG*FV_0xsQ;3$D)`Ft$+FT7>;5`DSWuKBe>h>yj-ZWV8I9WT>VJ6} zhhM%OuN>$Ohs83LWB8MI0)yW&f=$5VNw(L1>#qAabZ%>=?8Ih6c@8K1UUStIFyPRK zWG4M3-|T$Lcg5*0Pxp+HP1wnnVBCZYnoNj6i>GdKA-aUcl7ofG(-+UrS6asp9c?FA zI9nZR*s@G2Zzo&i!rkZppraE$(5tj^GZx1v(pgTQ-OIL|&wS|{?GYAtpJ23il&PPi zd?(6jiH!Zqh&Z{+9_!iDO8o$A-^mw7xnk$gvL5n?{^vJrN++!yFA-U*$SwkQ12b)Q3Je*?LYNq6CtsiM~7t>;8u01(=J=dZ6D zONZvKx&o&b5l87RdNKq2&)qpXUZV?1tAp5LFC(<|Prj8OKmWzAOv+d>kF&c5LKp`< zst;nSv5*Uy6{ZwmCUOZPhhC!0pPmW}yk4$)`|Igi4QTa7aLc479o~fI>dqNomUpB`dSHn34<3YOwXXrx)ipF$w`-~cd_N)tT9hyg>}hVwjolCYC(Wj%3-(tW5+lrhqx69UF)>f`woo%>e%Gd$sWX=?p)#b?Bpm*5BvJ+A*`p6l@fzpl(@g9Lg+oed;)zS% z3updyvIlqMFOKB$%F8Zc=f;w2PMA3k=V4ISkJilYbH6w5mI@Ueq!_$c?W zLv85De%uGzG@jEA?`y+{_qJ(=_QgMZ$;Xr<$&=!f-ymHqU7 z%a!FoYg8AO<5;FTuntaMX@llS=|Xo8qsG-wAoXwyXD7zdloK2%X`EDz)TKUp9Sv-m zFairocU=A^MdApoc(fV&)-PJF$z{>D!0<$Mw6v7|8gvB34xfGAn>l`75@g9r{Wt zK`!Cti@)JC9EpHgA{4MWR?$l|BNga2+H5tb34|2`qYpx&o#lPF$ z`!nyO!%!d0OFH7qFiS`BX+!L`VK43Dv~%6n94MW$ALR+Ty~UX zsR+cVy#MN7|I79ZzwnP}NM@7Lzv9_=ai%c&Oj7-&b!zsoUxiIM%#ZPQ^_A<}Ti^Lk zc8Hv5f5b=hp4+^gb5z7;WdVNR)PM9QJ#o`ASDD2xA;O9racP(nScOkO?GKP~)}@@K zlY*MtRCG9X_RQAUrJ`Y^^9ipxT{1LDX5rTeMb}-L+F2&fow|aZr1KL45-9np%uOH+ zY2!A%JOmH+!!+VeH(cMAEL}``(qU6CZ{E^A^ZBo|lb$=%jUXu%+KqTsk_nJE5cM4^ zA?#!Xj_AAvX6Ow4(J`4yCvcd2t?lSQqq}iV;63evAY?EGEW1mNl~@p={S=A}Gk zD98vpKq~VDEzmvpKiQspZev@xkexK}KaJ!5SDv?`Enm8*z2f>BWR#YjDs>t@EAu)Y z2p7BZ5kC&zUM8~_O0hrV?Sxo7!khZjVN(A37YcQOVz6E$Ux5`cbrAfF@X3dt z{kflRZ+_1^+BiF!(!mpvMEH*#17{w1TQ5XkfS5PRBe;2B-KR}1n>#Q3Tc?NvV$cN6 zhts{9;O%v48S{DgbSqyGd1g%RrBDE@q!m3=xBnlHjjq~HWHH$sRw7tqUthiX!v z0XGeJAxx*7^5>Og@?7Y;XW@~sv@5Plpd{vnv~qzDC%@4{ZON}#r@&l4Bi&rME2g*> zi@5bge!QIzwFG1Q0sKi7oDqM(-C#>A`fB7r`q4S0AsEBr`eA z`0Csc2j1`|oav0z6^}$sUpUx?U(sMoo?|<8;TOJ7r_m-WO&IMP2@L)YL)=L~Km_b) z0uq?Ye3%1KUlE!%m37BZ6^>%Ccq?G>P)BFr~ZOozX3^b#j)p-6$Fu%yyZp3tbu zEV2m&9`RZjlga?nYNWaZ=w(R{(0F0__hW5|!xO%ZLVsdPg&TSuUS;kK-)WwpPOq7G z)A^%ei(i^z@gE$7ARpBz>8j^irfWu<6(yH=xL%Zo0B9x$NS|aG34Q+kf~_JMiSDHhRdsRyW$KWf6kj@|-`< zD(T1*8p_HUh`FpmtB4f8NS=)uorTR~~o*8Xm%DLh}$@ z{A3y=IyJ2FN8AQ=YAE7eo=DO7czay>OVNq2o=D9weQKLdC(yC7vwWgqbl(ARnV#km z8!h=Dq)=S3Az^X*<{Urp)#=q1#Y$?K=dw)cHV+PR=#Sgvuet8Jb_L&HdgHA(wHLSU zY+LP|iGw@YCI;LJWnC415@&fSyOn{|n?TXEZ0pyY->$vpYQElFjd}^sG!UJ7KTR-3 zJ>W+u9a)$@b5>hAYc?G-P%9dpRcU8LM+U$x{k|@><6{RUVWb(jnU+;C!`Ouj*{6fY z1?Og*8j1$sUri6{bNoOAcd7t+2tgdWp{v*OoEkxYY_HPEZx2@*CN(gjmi!{EK)3}B z&*1VjYHev4u7)tyzCMVZspI}5W#*L}k;bxJ<-GV+HG&o=Viz)xPS#%c@}PZm@$#ok zAP7EqtKtI%(TD|}r5LxrI=d^nL)hpBc;mud@=90XN!?;Tk*=rRr|~7W+8p?RsJC&_ z*LFQO#{5qENBU|vd-<^qY{$5ygZaQgh!B7Buua2+aFzdwbJ--H>b!zfupKx;;Ubd#{JPz)(05s%XPG=T-)I2BSnl8!$O zQa*iKDC~rXp0E`MGN4Mi($J8i1OeoU8+FS~g$Jh$PvXZ|;08J|scEE@C;sTKGxC^U z#f2u&r!g{(-e)BvUJwf-@e}+8qgV?+e0T*E9^@;02fc?7Rh(~C6%8&+B09+<;7L@n zcYgI{@i0v;bbjLS(RMexKlZcW)Xs(HQmFR=np6RFI82w0V3;Dlgb2Fm2d-U1U#_80yk~i_> zC%TO70#sI0S(7yCf@bq-)}ow${&}${8<_9?;I8(>Y1=oE?$pA#A^+n1>H`|y%m zAAiW6H+C||@jp*EowamcyXalFwE1tot{s{)(srS5d7r*wZM*pLRqfzk@#X5T-`_?L z9FH_|PmLB_$x3NKGdDCmm@)L>&vN9YzO5wX?f5B9A>OpR{pAO$VQ z=UuRp-7&;*y7BlyP8&XnRiKyTjr0&EI)sP9ZeCVy=gl7X#7-3T+soWr1W&?DV~w(r zGkv~)--B)SC2QI(x4nTcgWucsZrIk2pqE@FbmR*6qEp6Bwy=b|IzIBr2mvF1KmR^P zOHr{MhAQVZ-Eo{#l<(pjKN~rZWei1m67W>Ef6G&B0Ue}VBb{|}Y7*ki0jq9hc7(O> znCQ)!*@)G^9)ZsZugv(BO69#+A;24j<;1^tp6! ztBQgv`vr-}2rUEX+n;**CeCE_Lusmm-Yhnbl*xP1k%<1H+qWM@S0%9b*%9MH<>Wbn%q6i8+s8CSMiVI{w~xQNRGSZ5pb`{>JdVgplf5fguG zE#MU{hXpH&3JXu2yhNiux#giQwRisHu?m&8pmcTn+v}p&$YA71`Js_=FaDxjX+gm2 zN8|`l2pGUs3{EiDld4#J0vAUeG|2JGsNv~RDV*Bj3Vd9ZOoJQ17f7X3VF;}WDMa)W ze42j`C_(|m-xa(Lw-FT;P7>NHS&y-DnK%BPUupWMpUTdse|CI|WW^QHB9tC8aCNC* zUA^CAq%q8L0Dc=bhdCWS6e>J+bdYoA~y}(D@n0)1}Nz2{8?TE&? zYoEk+JVF=xiIWd=k*Momb46Rkc_LR`c{y)VbW|!4!^*?$=38%WtIs{By}YZwd|VZv zIMt5Gxhd?C3AbVsQ9s~uaqoPNDu3VKeqS4zInwTZ^s)Bb!9#8K>hoIT0JBr9&v4gN zbeL4)^a2SoClBBih}b~bHV&nQKHz4H?NyPMA9RJ!FCuf;OPey6Q#=`o8sX5f)2>%A zZ4jw%C8$3h6%H~xjM7YjH9`Vj-E$&O2?JN(84nEf?Fh}FQ@Q@~b#3K}6BGELH+iIBq1#o2c+GbOAL49~^KQPX&3VhUZO=Rw<}&Rg z&n9vDgCxRShaD+x4-N zkAM2J?bbKk($2r|{5HhM+r<~HZY!58YMT$TXw`K7%ZQBf=9{TJoV`x_&Vai1ujmMG z&X&KP6&AkWFMmdJIbFGM-34uw7mn#H#y+xrm)T#mh!@Ppr1INv846Q+0O+aXwjMN2 zWeC;L=sRj>u;pnB=C!Hp7JGK|NPBAQmiC&pYun%ao9}GT|BsKgQARv6nnGHWN+o4! zGJW~?Hqv-*zx=z3MZecGGNa9;0k$9!>8Bz#Zra@b=+mET4{msg#kJ-SO+~lG*!sc_ zg&jzFPqN6wh?gS|p+XGqbVx$8?8B(=NYBU@pet9cZ2$Iu{iU|{;`8B=G}<*!anzKY zyqk)vdzQY$BEN+J9>|w+z zWBj7y($uTtDqZjsEs?)=i=gkd^-p0+I5HAJq9_HjpbefDwu(2b7ua;^amkgiljBTp zudE9%Be4dXggbFIaPq3)T|tjWNfX$`EBybDOu)#8c!N7Q`wSz9Ke*^%bkVg4O!1qv zR@~NqXg|u;MhpXY&s+Hos&EU76-mFFrw>mKno(SpR`_@%5S)=RVF4THykw{_e^cRi zye40w&qx!Q9F`upVMnT-L`mG~oj{CRWQPyN)J;FF4>(`%`T{6ns+dFkwBke88+eC3)(e%yTPYuhz9T-SE8 zb@kq7UuZ{X%x)u$F3ehcew%;kMQzsdrEQ$EO!B6Q-s%mh^krjQmLO;TsVQ#Y>P@aj zelh|y*$(6tI7d@x$*A1UwE3%75R&H8wrL`N0R+6u3#*#mehPZW> z!ZyL%T`O0d)7GqI{SWH~Y&`z#6Mx=b-m#x`P{l=TSlAM%>^olM?ChtWmOu1>40&nr zi5$Lm&snyh&A#%2c5MEvcA5@KuE_4&BkQUeRXNF8k%d=X+~zLgNPBo!9?Q){-u3ti zhpzl|kE)mnmj6Bn%y-}J51gFZ{`3={%9$bRcP*bixN!Bk^jWKMboyvemZDQt=wDZ! zaVB2=cj~;KwckiF50|BT!Qg;BWHJ+tY z*L@&2vS=T6cE(+ti97Zt94VC5ZR~Sat!PWHUf1SdaY>tX>BVgi9qxy>zRXv@54Fp$ zdriCbwO6)T=)gu!Ix??~4dhqvo$jOl@Gmc%cQQ8G-pV(f=Fyoq3*}Ef{VvcbYl{Br zJKt+hZQIetrq5`nIDY@sw2|DWM`pIuhH+&7NpItL=4L0^89obfCeK-IdW>WedUtqAdt|ky&jV{{`^m#~N)l(%?OsSXYuRpa2<(U+&d}W7GT!bemVSQW= z-}{AENqxHFvt$dC@KiQdVadD59u-7iRfsBIOD`!^Nv>=6krPkL}15HI$2P&j~o=l(_*zq*w2!&uW zGm)g@sZ(5bzNG6xOd5O72N1yc3+|K@`NWS~m7>ph)$q)D)n7MMw zHz1#*9l(n;vz#At?~m?j4?p%4T?1#Ur6e}f2*Hz#j_&d0X2nJ(N-JRPaxKFosBdmnhXJ;Er*Y2F4)7vgGd4nx0hdea+Nw<2ZbW(k{mOhcsY zKomPvJo(a=>YmCO8q?eCnN!;bfB6@~(~DHJ5AdCm(Nipf<228yoK`x_*%l*fR<}i0 zUe@Mba9$gl#fh^KSDl%0T)n+Pq_@8^40)2z%HW6&7jh!=N_@;SJ)Fy@F_^|^k<$QZ zDl$Zq$TBf~CB8}rz#!Z((+qBHjoZ-@zcErKrU?}3aIox^&g z3(j5MW~vAEr$JKI>;&ZPnKGlUvty39A^bz?-&>u+ZN!;S03uJvz62U!XmE+;|=qL=SAx-Y%8u`-{xL^ zVRSgm+8+;&JAV9lyYKnu+UEWH@0E<%@PtmvRTrkLn&Jre zf}ykRwi{pD7R{ZL&YEyYn9<%RSeSdy6Hm3nbXd+(d897R(^2MF0%`!>c=Qn!sW#Rp zXvst=VccceorNQ(JHm3qvkP>OV-bJ#gCDh>920Qb^b+7I`xjAvn^&EA@a!8yA(eje zsA6AUdpja8G^pf{HXm+zc*#7MX+WJTG7)eRL9^jO7}@D~9q3ydA+FbXYzCMK zf4K+syK$n)ZM{!={;G{2yhIM29!IawzOxhd!=*eI!@Z2rLxd<<`RyPJHgJR)Di0Is zqtV!;bgUQh1b^`XpMAcVc9P5kK0CRCIdV?uA^M7>NYm9V(LtA{$5C8SwhU*!fKxPfwlk1CAkgaHzyB|f zrGOqTPvoeMgik|~{e`>Z&x*k-nS)z%?`bE@R1@kbI0F7UeT}LIjEJ^eT=v3A#NsnP zKxaBAh)#Z7Z055%F&=1CmA&xdw-T^IG=Z5KSdm6gJk@!00sznIgdk6hs-AK&kVpB{ zEl)cqVNt(0t$aDm>7#$}F;2IXzM%yVNL2#eZ_k?*j6BI;^o%DR6T*EOp9>?@Iq0;2 z+m6ebv%~G#7dE#a-+g~Oe26W+hDV>b(jwi&b;XS zw&c1i+hV3jh8b3O?mfB{w-hACoo%)14c;R*5s|;+DJ%sJeQ=h=8K#st*n`HKMvYTR z9b>S8nCN6=P=a}R*8|XScK7fQt7d3GJRi-t=??dk*X3s&0jv-1X`F70e>#VXc`nNQ zc{AFY3s-T<>@vcqwa*rz&R39C}6a=wdpZ*%jBVZ!2zmO`Ch^g>Cxs zB^(Gio85K8ZJhI0_P_8_I}W|$>>7J|`xZVMajebeES|sri$BlqBBn1>J}e{F71r6- z9bLZU>G{IQPUmVG(?;)lB_S~eff{SlSO{Rv9bdc8&8rh96W=tz-n`(Fq}Ly7n9zwggdTVFPpxw zhcJ(b8 zk1Oqfw%EfEk#-y0gD~QRGfi*~=$1cmpzMPq4W4wm;H{Hx;%OXpL+bVj9F96YL=(Z4 z7xPr63JzZ#+o|sf&+eYS;xbxXVVY+#VdHJ%Mx{)G0V_n7(oH~iNF)mAAWSR&+Nc{*x{GCFJAz(Yk zG^U>2JoWicykGGxso{?T1nX6JihB3}kU($0x1tIDFjKPW7l%_k4io0>!GJEEY0@nL ztgEUzSxSE4Qa?vVpu;PifKLcjHqbx*a?2?Qy*mD_ABz9dO<0h4;;qo&jZW0ZKl&$a z$KE4U6ckdod`nLe%yZoSA;d(b+mVs!-YMD0ts|kvO~hu6k(GeBd7c8__wW8myZPoD z+gslJ=7}`mT)lc#`|aQQ&Gx0Qe6`(q=eODm&u^r8s1bn-`TG9Xqvok|8i#AGI(Jd~ z*UPN$7q_KMxim(DPBJZU zmbdzui|4j8d`C#P>CJKVlqthb&zTYdzq&De$X!(-VrPa%0(eJ~EI-nlD6HNeVIM2?WI3>_sl$De*yylrPpvgrSyUnHZb1u^h z<4pCOnmdExdB65Mr<#-iA`ed+8NDO?xH0|cvUzRUdtTozy#2z5crPmW?m9%{z9b=Qm8l zJB{hC=nve!0AS_y2i{>D-sFS)x;+-z!wax9ls58S)!0K6*r_h?rbnoq%wx2QWBXz^ z$JVcFTbR1s^2%;7(HR7n@YWjX)5HaB!n4+bJan73>or`zF`G3R%PzU7wfWehI^lK~ zncw4fl;-6ZUt(+f$+mzs8*|QIm316HV07%Y=d5aTFP__OzWw%g<=2^-*>Rv9r!3bc z&!`ch9Ce({PP(&8k@seL=c7NhiE`wFj?|LFHzdd5BT+EWG|oCjt^xQ$cn@ErS*Z5`gDbjc*lda9FmA zMs#EX;ygCrQNba~RAlfsg%E1;X*!vbSJR8Hm%Gd=k*)#q&uFQ{XL{WJ=+6bOgK`jl zh*Z9T8z{s{N5E9UCyf79R}^fai~ev{SmDXqOYo6oaK+~WHdU24Vnr_B-Z1X77YUzi z4fs~4T^0dXb%HFL#+P6~ttmoNTBc9^5g=nkX1OHp#Mtrho5q}ksrUo|CLIdGOdz`6 zuP_k^%P=Mk5#}WmUcF-U1(s)of@ld)6@%_#+*Dn9GTp6BLB)@_#35V+Nr6|0iW{WE z5uqv|6@$HEIp}#-hIF_H$dGU1i$|i6rs&k0NV!eppBWSS_~i|sltQx_pK>P<07pwk z`4a7E(pezfj%A^ud~y5sF1|r{A$XPCfFTcQ(7F8U_a_Bizc{%{|N1RV01@XU zcz7gC84|$H_}KAwh;tZz=H2ga2idMLewL1a@I9T*(t_7?U7-=J6}=?9lZTZs>7|<0 z>7<7n0FaLn8+qt?QH~i?PqknAyFc50`Jem)4(M7IJ>)PY=YV-zbt|NWe+zGRU%TV0 z?JIYDz3pN;Wh!f3oF~@Q)v4EvX=mGqKm6P6H8)(__U}K?e(S&dW_xhsjyChc3)@MC zA4X8oF1B)x)3~LB$ej2B)~U=`x-^{?8%}o_d6?V~qacTwDmujNVO=Q$@R3m{?H(JA zjOg9MIm8Ia8{hu=_Ot)^7uw=wOM#jD=sfLw@X5C2?nfAH@*p-xd2H0p$*wL4KjD}#^z5`k zxGOtbGIx6W;0NB*-v76MI{XhYGB(|}e$w~lUpYV{M*hk^;50t5)#W)DoTT8Pa9yj3wUSXIr7Smw(G@B9F|5$h>?^T7q4vR-td}CHJsVGyNy2dRD1op zi`qZ@XaBfOqto;f)0zM3|N6By%GwV*8&aRaNZK^qGg*6b?d6xYcmIu_YPa5SV_P(T zJ`BN&Ji*I!@JrjWwt3t3_K{D1x_$nK_p}qNM>|VLS;I)ZgncTH+m53>-Q1Cl0P7*` zvR*EYBdJPG9Qrp7Z_*fvKXsRNIWddCd)d6{?f?GW-{zZ*?kH3awdQYU@A6z&U|ro? zmzlR>2sH`dmu>aW9iD`Mq}F5_ZG!;IP}(2UTTc;~x-|%d7e8^zz*AiGA6Z*_rp?0M zraL0rU7-`4#bfD4tFAgju;?oIv~6R=U2=v)d6k&5REuw-L@7(6Kp8u-E>Jtj(zi(_ z%d||0m0cUBUGvB#_RzJxAWV1hYK-aOK}U#naUCp#0$*{!;_o|);lX6;5#cOz()CiO z!~|B|_i5xQ`4;{Pckx=xRvh(4DP^)CM)5~)$G9jFY(a>kdZS41G3QOd2$5EOd-ROmDGf36umAYi@?xMI2DtH z{i7fLgEr=N-;Q>49h%5yr6fL+B`tJ}Q!0h_mR{3_eANu@(zf{`6NQE$y;eU3L%yZ4o`b*l$*(1E6)9K+z z_MwxUF|+eX+s}y9@#hw|ix@4MxoRnAn9!|aZc6mikaZdzl(CK5+jF1#dK=w;hz9oR zHj6jpF>oKBK?D89YugPsUDb9!`dr)n1S4eokF_z*0;zfe@hl(UQomvi-|7YeQ<-{M zbKwQ;?SJcUX5?VX2|BXSbF?X=QP4lb!q1GBh=s(}$5#>f^@q1am`jI86TD~*t zLb{B=*bx!X3K>_n%F|#|^_UtxbNcOy#qBM(-`X}|M;mwUWepH1stpLw8MVRoMwm{V zN9SbDilyvS;;kIo>TEilZO>XcvTbL3W&6%Hg;maWSgfC>v4-mW8?FibG)AxXZ`j<9 z>_1G0=>B%wTi?(FYVxv0X+K6lT1|-6l)MhnZyu)D&v6%!f0{l z9=g`w!0S);1xy~r#}gY40e!$u)GdW82!lV&n>x7C!2n0Fgf(RZQ}__dc1(GVtTxR& zmQPdJ9@H+MK0)CUIvp_}`YZ4t@4A9SfM_CGF)A_qhu_HwJBl#Z75XkRgiNOpgScX* zdaWv^=+vlz-in`0TUZ6J!ci$~{8YHm5jg&ia5ztOmUJp1y=KBIMUTBW6iED$u&2{S ziBaB-&*hL4iakxEmAL z2jSw8S{3i!*a%G=vVFa~i4EX^>I*5wkksqT}T>&tnfh+8%u1 z@iscj#4vBQ>3oI@lNMH!gMa|i2MmF(15ym$#6j#KSF|HuE#DAbvDC0ist#OflpoGxCnq@A;JImg^z-mbW6eLGHN z`{*N&wR`Wmx4pb+Q~UY%zqifdqjQIk9Bu#eBYy;bb`sIQJkIWxA$CTLP?aA76inV~wG`-$Zr#z2F~#GS;n~aStgypq3OgTMdoje9gcmbiLy{0j{(2(tOd7qJD{1&RBK|mZRKHPOYD;#uXwc5G z3**r9o7&Nx9Cgmy|Forx+OCaT+T!D*Z4L`p$4Tqk`81{-#uyTPqX{Rh%&`zVX{>S*Rn_zW(R*5 zkBfyD%^Yg~;Gg~y9nLANWjfmK`QeY-OFOnRGC_wH9gbj6TOMW|5)GhbRcp5~QTL_t zSbub%L(Q~lbna~(V>$23h~I9>NDE1(QQn8>j4fGraXZ6~rL(SGVQ0@0>Vh}F@wRr^ z)mOCF-FS7|`SrWnAv&^laE4hwGHn)Xcj%nVrE@aOA$xu{;1qA*XPJΞZ~3%0cYo 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 Z{|BK=yi@mZI5YqN002ovPDHLkV1lgAPMH7z literal 0 HcmV?d00001 From 10ec324f8077b70a5413828ed834d772bad63a58 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 16:36:21 +0800 Subject: [PATCH 10/23] docs(readme): replace mermaid architecture diagram with image, fix badge URLs - Replace mermaid flowchart with assets/structure.png - Fix CI/Docs badge URLs to point to actual workflow files - Remove PyPI badges (not yet published under wolfharness) - Replace PyPI license badge with github/license badge --- README.md | 36 ++++++++---------------------------- assets/structure.png | Bin 0 -> 1003043 bytes 2 files changed, 8 insertions(+), 28 deletions(-) create mode 100644 assets/structure.png diff --git a/README.md b/README.md index 0ede15fea..81c25647f 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,11 @@

    WolfHarness

    - CI -[![codecov](https://img.shields.io/codecov/c/github/wolf1069b/agentpool)](https://codecov.io/gh/wolf1069b/agentpool) -[![Docs](https://img.shields.io/github/actions/workflow/status/wolf1069b/agentpool/docs.yml?branch=main&label=Docs)](https://leoyzen.github.io/wolfharness/) -[![PyPI](https://img.shields.io/pypi/v/wolfharness)](https://pypi.org/project/wolfharness/) -[![Python](https://img.shields.io/pypi/pyversions/wolfharness)](https://pypi.org/project/wolfharness/) -[![License](https://img.shields.io/pypi/l/wolfharness)](https://github.com/wolf1069b/agentpool/blob/main/LICENSE) + Tests + codecov + Docs + License +

    > **PydanticAI-based multi-agent orchestration framework** — define heterogeneous agents in one YAML file, compose them into teams and workflows, and expose them through ACP, OpenCode, MCP, and AG-UI protocols. @@ -101,28 +100,9 @@ agents: ## Architecture -```mermaid -flowchart TB - subgraph WolfHarness - subgraph config[YAML Configuration] - native[Native Agents
    PydanticAI] - acp_agents[ACP Agents
    Goose, etc.] - workflows[Teams & Workflows] - end - subgraph interface[Unified Agent Interface] - delegation[Inter-agent delegation] - routing[Message routing] - context[Shared context] - end - config --> interface - end - interface --> acp_server[ACP Server] - interface --> opencode_server[OpenCode Server] - interface --> agui_server[AG-UI Server] - acp_server --> clients1[Zed, Toad, ACP Clients] - opencode_server --> clients2[OpenCode TUI/Desktop] - agui_server --> clients3[AG-UI Clients] -``` +

    + WolfHarness Architecture +

    ## Key Features diff --git a/assets/structure.png b/assets/structure.png new file mode 100644 index 0000000000000000000000000000000000000000..002bfb1ee7ea0dc7b22ec861dd8db227c2d2c2fe GIT binary patch literal 1003043 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
    )3TpFWR? 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%0AJc@Fkf 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

    yS79nz5b;IL9eB`3qK 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 z

    NqlU|)3KX?ZHBR6&G2xR?4d4@p{MVmK&_3B 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

    epDQXc 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%0DHf9$X6fPG0jB=aH)G8BZi_G0 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

  1. k>l~mPh+J~%!#M^_&d>Arx=UIF;jp_5Axgg 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

    dfdlMg66$der&rAil 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 Date: Fri, 7 Aug 2026 16:44:48 +0800 Subject: [PATCH 11/23] chore: remove stale dev scripts, prototypes, and empty overrides dir Remove 7 debug/test scripts cluttering root, prototypes/ (prototype code not merged into formal codebase), and overrides/ (empty except dummy). --- diagnostic_pool_skills.py | 83 ---- inspect_mcp.py | 52 --- overrides/_dummy.txt | 1 - prototype_enqueue.py | 254 ---------- prototypes/__init__.py | 1 - prototypes/pydantic_graph_validation.py | 586 ------------------------ reproduce_skill_bug.py | 70 --- runtime_hook.py | 5 - test_graph_config_loading.py | 263 ----------- test_pool_skills.py | 93 ---- 10 files changed, 1408 deletions(-) delete mode 100644 diagnostic_pool_skills.py delete mode 100644 inspect_mcp.py delete mode 100644 overrides/_dummy.txt delete mode 100644 prototype_enqueue.py delete mode 100644 prototypes/__init__.py delete mode 100644 prototypes/pydantic_graph_validation.py delete mode 100644 reproduce_skill_bug.py delete mode 100644 runtime_hook.py delete mode 100644 test_graph_config_loading.py delete mode 100644 test_pool_skills.py diff --git a/diagnostic_pool_skills.py b/diagnostic_pool_skills.py deleted file mode 100644 index bd81e574c..000000000 --- a/diagnostic_pool_skills.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Diagnostic: Check AgentPool skill_resolver initialization without full agent startup.""" - -from __future__ import annotations - -import asyncio -from pathlib import Path -import sys - - -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") - -from wolfharness import AgentPool - - -async def main(): - print("=" * 60) - print("AgentPool Skill Resolver Diagnostic") - print("=" * 60) - print() - - config_path = ( - "/Users/yuchen.liu/src/yilab/iroot-llm/packages/xeno-agent/config/diag-agent-ng.yaml" - ) - print(f"Config: {config_path}") - print() - - # Check if config exists - if not Path(config_path).exists(): - print(f"ERROR: Config file not found: {config_path}") - return - - try: - # Create pool but don't enter it - just check initialization - pool = AgentPool(config_path) - print("Pool created (not entered)") - print(f" Pool skills_dirs: {pool.skills.registry.skills_dirs if pool.skills else 'N/A'}") - print() - - # Now enter the pool to initialize skill_resolver - async with pool: - print("Pool entered successfully") - print(f" skill_resolver: {pool.skill_resolver is not None}") - print(f" skill_provider: {pool.skill_provider is not None}") - print() - - if pool.skill_resolver: - print("Registered providers:") - for name in pool.skill_resolver.list_providers(): - print(f" - {name}") - print() - - # Test bare skill name resolution - print("Test: resolver.resolve('systematic-troubleshooting')") - try: - skill = await pool.skill_resolver.resolve("systematic-troubleshooting") - print(f" OK: {skill.name}") - except Exception as e: # noqa: BLE001 - print(f" FAIL: {e}") - print() - - # Test URI resolution - print("Test: resolver.resolve('skill://systematic-troubleshooting/references/...')") - uri = "skill://systematic-troubleshooting/references/expert_knowledge/excavator/excavator-hard-starting.md" - try: - skill = await pool.skill_resolver.resolve(uri) - print(f" OK: {skill.name}") - ref = getattr(skill, "_resolved_reference_path", None) - print(f" ref_path: {ref}") - except Exception as e: # noqa: BLE001 - print(f" FAIL: {e}") - print() - - except Exception as e: # noqa: BLE001 - print(f"ERROR: {e}") - import traceback - - traceback.print_exc() - - print("Done") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/inspect_mcp.py b/inspect_mcp.py deleted file mode 100644 index 4fb81e07c..000000000 --- a/inspect_mcp.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Inspect MCP server resources at http://127.0.0.1:8890/mcp.""" - -import asyncio -import sys - - -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") - -from wolfharness.resource_providers.mcp_provider import MCPResourceProvider - - -async def main(): - provider = MCPResourceProvider( - server="http://127.0.0.1:8890/mcp", - name="scratchpad", - ) - - try: - await provider.__aenter__() - print("Connected to MCP server") - print(f"Server config: {provider.server}") - - # Get all resources - resources = await provider.get_resources() - print(f"\nResources ({len(resources)}):") - for r in resources: - print(f" - uri={r.uri} name={r.name}") - - # Get all prompts - prompts = await provider.get_prompts() - print(f"\nPrompts ({len(prompts)}):") - for p in prompts: - print(f" - name={p.name}") - - # Get all tools - tools = await provider.get_tools() - print(f"\nTools ({len(tools)}):") - for t in tools: - print(f" - name={t.name}") - - # List skill resources specifically - skill_resources = [r for r in resources if str(r.uri).startswith("skill://")] - print(f"\nSkill resources ({len(skill_resources)}):") - for r in skill_resources: - print(f" - {r.uri}") - - finally: - await provider.__aexit__(None, None, None) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/overrides/_dummy.txt b/overrides/_dummy.txt deleted file mode 100644 index 2a4206661..000000000 --- a/overrides/_dummy.txt +++ /dev/null @@ -1 +0,0 @@ -File exists to make sure that folder exists for git diff --git a/prototype_enqueue.py b/prototype_enqueue.py deleted file mode 100644 index 126d43db8..000000000 --- a/prototype_enqueue.py +++ /dev/null @@ -1,254 +0,0 @@ -"""Prototype script: Test PydanticAI enqueue() drain behavior with agent.iter() + next(). - -This script is NOT committed to production code. It verifies: -1. PendingMessageDrainCapability is auto-injected outermost -2. Bare `async for node in agent_run:` skips after_node_run hooks and fails -3. `agent_run.next()` drains `asap` before next ModelRequestNode -4. `agent_run.next()` drains `when_idle` after all pending tool calls resolve - -Run: uv run python prototype_enqueue.py -""" - -from __future__ import annotations - -import asyncio - -from pydantic_ai import Agent, RunContext -from pydantic_ai.models.test import TestModel -from pydantic_ai.tools import Tool - - -# --------------------------------------------------------------------------- -# Test harness -# --------------------------------------------------------------------------- - -PASS = "PASS" -FAIL = "FAIL" -results: list[tuple[str, str]] = [] - - -def test(name: str, condition: bool) -> None: - status = PASS if condition else FAIL - results.append((name, status)) - print(f" [{status}] {name}") - - -# --------------------------------------------------------------------------- -# Dummy tool that enqueues messages during execution -# --------------------------------------------------------------------------- - - -async def enqueue_test_tool(ctx: RunContext) -> str: - """Tool that enqueues messages with both priorities.""" - ctx.enqueue("when_idle_message", priority="when_idle") - ctx.enqueue("asap_message", priority="asap") - return f"tool_result_{ctx.run_step}" - - -# --------------------------------------------------------------------------- -# Test 1: Capability auto-injection -# --------------------------------------------------------------------------- - - -async def test_capability_auto_injected() -> None: - print("\n=== Test 1: PendingMessageDrainCapability auto-injected ===") - agent = Agent(model=TestModel(), tools=[Tool(enqueue_test_tool)]) - - # _root_capability is a CombinedCapability - root = agent._root_capability - capability_types = [type(c).__name__ for c in root.capabilities] - - print(f" Capabilities: {capability_types}") - test( - "PendingMessageDrainCapability is present", - "PendingMessageDrainCapability" in capability_types, - ) - test( - "PendingMessageDrainCapability is outermost (last in list)", - capability_types[-1] == "PendingMessageDrainCapability", - ) - - -# --------------------------------------------------------------------------- -# Test 2: Bare async for fails on undrained when_idle -# --------------------------------------------------------------------------- - - -async def test_bare_async_for_fails() -> None: - print("\n=== Test 2: Bare `async for` fails with undrained messages ===") - agent = Agent(model=TestModel(), tools=[Tool(enqueue_test_tool)]) - - try: - async with agent.iter("hello") as run: - async for _node in run: - pass # Bare iteration skips after_node_run hooks - test("Bare async for raises UndrainedPendingMessagesError", False) - except Exception as e: # noqa: BLE001 - test( - "Bare async for raises UndrainedPendingMessagesError", - "UndrainedPendingMessagesError" in type(e).__name__, - ) - - -# --------------------------------------------------------------------------- -# Test 3: next() drains asap before next ModelRequestNode -# --------------------------------------------------------------------------- - - -async def test_asap_drained_before_next_model_request() -> None: - print("\n=== Test 3: asap drained before next ModelRequestNode ===") - agent = Agent(model=TestModel(), tools=[Tool(enqueue_test_tool)]) - - async with agent.iter("hello") as run: - node = run.next_node - asap_seen = False - asap_drained_at: str | None = None - last_node: str | None = None - - while True: - node_name = type(node).__name__ - - if hasattr(node, "stream"): - async with node.stream(run.ctx) as stream: - async for _event in stream: - pass - - pending = [m.priority for m in run.pending_messages] - if "asap" in pending: - asap_seen = True - if asap_seen and "asap" not in pending and asap_drained_at is None: - asap_drained_at = last_node - - last_node = node_name - node = await run.next(node) - - if type(node).__name__ == "End": - break - - print(f" asap drained at transition from: {asap_drained_at}") - test( - "asap drained immediately after CallToolsNode", - asap_drained_at == "CallToolsNode", - ) - - -# --------------------------------------------------------------------------- -# Test 4: next() drains when_idle after all tool calls resolve -# --------------------------------------------------------------------------- - - -async def test_when_idle_drained_after_tool_calls() -> None: - print("\n=== Test 4: when_idle drained after all pending tool calls ===") - agent = Agent(model=TestModel(), tools=[Tool(enqueue_test_tool)]) - - async with agent.iter("hello") as run: - node = run.next_node - when_idle_seen = False - when_idle_drained_at: str | None = None - last_node: str | None = None - - while True: - node_name = type(node).__name__ - - if hasattr(node, "stream"): - async with node.stream(run.ctx) as stream: - async for _event in stream: - pass - - pending = [m.priority for m in run.pending_messages] - if "when_idle" in pending: - when_idle_seen = True - if when_idle_seen and "when_idle" not in pending and when_idle_drained_at is None: - when_idle_drained_at = last_node - - last_node = node_name - node = await run.next(node) - - if type(node).__name__ == "End": - break - - print(f" when_idle drained at transition from: {when_idle_drained_at}") - # when_idle should be drained at after_node_run of a CallToolsNode, - # after all tool calls for that node have resolved. - test( - "when_idle drained at after_node_run of CallToolsNode", - when_idle_drained_at == "CallToolsNode", - ) - - -# --------------------------------------------------------------------------- -# Test 5: Event mapping reference — document what PydanticAI yields -# --------------------------------------------------------------------------- - - -async def test_event_mapping() -> None: - print("\n=== Test 5: PydanticAI node event mapping ===") - agent = Agent(model=TestModel(), tools=[Tool(enqueue_test_tool)]) - - event_log: list[tuple[str, str]] = [] - - async with agent.iter("hello") as run: - node = run.next_node - while True: - node_name = type(node).__name__ - - if hasattr(node, "stream"): - async with node.stream(run.ctx) as stream: - event_log.extend([(node_name, type(event).__name__) async for event in stream]) - - node = await run.next(node) - if type(node).__name__ == "End": - break - - print(" Event mapping (node -> event type):") - for node_name, event_name in event_log: - print(f" {node_name:<20} -> {event_name}") - - # Verify expected events exist - events_by_node: dict[str, set[str]] = {} - for node_name, event_name in event_log: - events_by_node.setdefault(node_name, set()).add(event_name) - - test( - "ModelRequestNode yields PartStartEvent / PartEndEvent", - {"PartStartEvent", "PartEndEvent"}.issubset(events_by_node.get("ModelRequestNode", set())), - ) - test( - "CallToolsNode yields FunctionToolCallEvent / FunctionToolResultEvent", - {"FunctionToolCallEvent", "FunctionToolResultEvent"}.issubset( - events_by_node.get("CallToolsNode", set()) - ), - ) - - -# --------------------------------------------------------------------------- -# Main -# --------------------------------------------------------------------------- - - -async def main() -> None: - print("=" * 70) - print("PydanticAI enqueue() drain behavior prototype") - print("=" * 70) - - await test_capability_auto_injected() - await test_bare_async_for_fails() - await test_asap_drained_before_next_model_request() - await test_when_idle_drained_after_tool_calls() - await test_event_mapping() - - print("\n" + "=" * 70) - print("SUMMARY") - print("=" * 70) - passed = sum(1 for _, s in results if s == PASS) - failed = sum(1 for _, s in results if s == FAIL) - for name, status in results: - print(f" [{status}] {name}") - print(f"\nTotal: {passed} passed, {failed} failed") - - if failed > 0: - raise SystemExit(1) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/prototypes/__init__.py b/prototypes/__init__.py deleted file mode 100644 index c7237bc0b..000000000 --- a/prototypes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Prototype scripts for pydantic-graph integration validation.""" diff --git a/prototypes/pydantic_graph_validation.py b/prototypes/pydantic_graph_validation.py deleted file mode 100644 index ff3922758..000000000 --- a/prototypes/pydantic_graph_validation.py +++ /dev/null @@ -1,586 +0,0 @@ -"""Standalone prototype validating pydantic-graph's builder-based API for AgentPool. - -This script exercises: -- Parallel execution with Fork + Join and reduce_list_append -- Decision node routing based on input type -- Sequential chain of 2 Steps -- Graph.iter() streaming of intermediate steps -- Cancellation mid-stream -- Error handling in Fork branches -- DAG cycle detection (demonstrated via custom validation) -""" - -from __future__ import annotations - -import argparse -import asyncio -from dataclasses import dataclass, field -from pathlib import Path -import sys -from typing import TYPE_CHECKING, Any - -from pydantic_graph import GraphBuilder, StepContext, TypeExpression -from pydantic_graph.graph_builder import EndMarker -from pydantic_graph.join import reduce_list_append -from pydantic_graph.paths import DestinationMarker - - -if TYPE_CHECKING: - from pydantic_graph.id_types import NodeID - from pydantic_graph.node_types import AnyNode - - -# --------------------------------------------------------------------------- -# State -# --------------------------------------------------------------------------- - - -@dataclass -class GraphState: - """Shared state for graph execution.""" - - log: list[str] = field(default_factory=list[str]) - counter: int = 0 - - -# --------------------------------------------------------------------------- -# Graph builders -# --------------------------------------------------------------------------- - - -def build_parallel_graph() -> GraphBuilder[GraphState, None, None, list[str]]: - """Build a graph with 3 parallel steps joined by reduce_list_append. - - Returns: - GraphBuilder configured for parallel execution test. - """ - g = GraphBuilder(state_type=GraphState, output_type=list[str]) - - @g.step - async def source(ctx: StepContext[GraphState, None, None]) -> str: - """Emit the shared input for parallel branches.""" - return "hello" - - @g.step(node_id="branch_a") - async def branch_a(ctx: StepContext[GraphState, None, str]) -> str: - """First parallel branch.""" - ctx.state.log.append("branch_a") - return f"{ctx.inputs}-A" - - @g.step(node_id="branch_b") - async def branch_b(ctx: StepContext[GraphState, None, str]) -> str: - """Second parallel branch.""" - ctx.state.log.append("branch_b") - return f"{ctx.inputs}-B" - - @g.step(node_id="branch_c") - async def branch_c(ctx: StepContext[GraphState, None, str]) -> str: - """Third parallel branch.""" - ctx.state.log.append("branch_c") - return f"{ctx.inputs}-C" - - collect = g.join(reduce_list_append, initial_factory=list[str], node_id="join_collect") - - g.add( - g.edge_from(g.start_node).to(source), - g.edge_from(source).to(branch_a, branch_b, branch_c), - g.edge_from(branch_a, branch_b, branch_c).to(collect), - g.edge_from(collect).to(g.end_node), - ) - - return g - - -def build_decision_graph() -> GraphBuilder[GraphState, None, int, str]: - """Build a graph with a Decision node routing by input type. - - The graph takes an int input; positive values route to the int handler, - non-positive values route to the str handler after a transform. - - Returns: - GraphBuilder configured for decision routing test. - """ - g = GraphBuilder(state_type=GraphState, input_type=int, output_type=str) - - @g.step - async def emit_value(ctx: StepContext[GraphState, None, int]) -> int | str: - """Return the input if positive, otherwise a string message.""" - if ctx.inputs > 0: - return ctx.inputs - return f"negative:{ctx.inputs}" - - @g.step - async def handle_int(ctx: StepContext[GraphState, None, int]) -> str: - """Branch taken when input is an int.""" - ctx.state.log.append("int_branch") - return f"Got int: {ctx.inputs}" - - @g.step - async def handle_str(ctx: StepContext[GraphState, None, str]) -> str: - """Branch taken when input is a str.""" - ctx.state.log.append("str_branch") - return f"Got str: {ctx.inputs}" - - g.add( - g.edge_from(g.start_node).to(emit_value), - g.edge_from(emit_value).to( - g - .decision(node_id="type_decision") - .branch(g.match(TypeExpression[int]).to(handle_int)) - .branch(g.match(TypeExpression[str]).to(handle_str)) - ), - g.edge_from(handle_int, handle_str).to(g.end_node), - ) - - return g - - -def build_sequential_graph() -> GraphBuilder[GraphState, None, None, str]: - """Build a graph with a sequential chain of 2 steps. - - Returns: - GraphBuilder configured for sequential chain test. - """ - g = GraphBuilder(state_type=GraphState, output_type=str) - - @g.step - async def step_one(ctx: StepContext[GraphState, None, None]) -> int: - """First step in chain: produces an int.""" - ctx.state.log.append("step_one") - return 10 - - @g.step - async def step_two(ctx: StepContext[GraphState, None, int]) -> str: - """Second step in chain: consumes int, produces str.""" - ctx.state.log.append("step_two") - return f"Result: {ctx.inputs * 3}" - - g.add( - g.edge_from(g.start_node).to(step_one), - g.edge_from(step_one).to(step_two), - g.edge_from(step_two).to(g.end_node), - ) - - return g - - -def build_stream_graph() -> GraphBuilder[GraphState, None, None, str]: - """Build a simple graph for streaming / iter() tests. - - Returns: - GraphBuilder configured for streaming test. - """ - g = GraphBuilder(state_type=GraphState, output_type=str) - - @g.step - async def alpha(ctx: StepContext[GraphState, None, None]) -> int: - """First step.""" - ctx.state.log.append("alpha") - return 1 - - @g.step - async def beta(ctx: StepContext[GraphState, None, int]) -> str: - """Second step.""" - ctx.state.log.append("beta") - return f"final-{ctx.inputs}" - - g.add( - g.edge_from(g.start_node).to(alpha), - g.edge_from(alpha).to(beta), - g.edge_from(beta).to(g.end_node), - ) - - return g - - -def build_error_graph() -> GraphBuilder[GraphState, None, None, list[str]]: - """Build a graph where one fork branch raises an exception. - - Returns: - GraphBuilder configured for error handling test. - """ - g = GraphBuilder(state_type=GraphState, output_type=list[str]) - - @g.step - async def source(ctx: StepContext[GraphState, None, None]) -> str: - """Emit shared input.""" - return "boom" - - @g.step(node_id="ok_branch") - async def ok_branch(ctx: StepContext[GraphState, None, str]) -> str: - """Branch that succeeds.""" - return f"ok-{ctx.inputs}" - - @g.step(node_id="fail_branch") - async def fail_branch(ctx: StepContext[GraphState, None, str]) -> str: - """Branch that raises an exception.""" - raise RuntimeError("Intentional fork branch failure") - - collect = g.join(reduce_list_append, initial_factory=list[str], node_id="error_join") - - g.add( - g.edge_from(g.start_node).to(source), - g.edge_from(source).to(ok_branch, fail_branch), - g.edge_from(ok_branch, fail_branch).to(collect), - g.edge_from(collect).to(g.end_node), - ) - - return g - - -# --------------------------------------------------------------------------- -# Test runners -# --------------------------------------------------------------------------- - - -def evidence_path(name: str) -> Path: - """Return the path for an evidence file. - - Args: - name: Base name of the evidence file. - - Returns: - Path to the evidence file. - """ - base = Path(".omo/evidence") - base.mkdir(parents=True, exist_ok=True) - return base / name - - -async def test_parallel() -> dict[str, Any]: - """Test parallel execution: 3 steps run concurrently, Join collects results. - - Returns: - Dictionary with test results and metadata. - """ - g = build_parallel_graph() - graph = g.build() - state = GraphState() - result = await graph.run(state=state) - - ok = sorted(result) == ["hello-A", "hello-B", "hello-C"] - ok = ok and set(state.log) == {"branch_a", "branch_b", "branch_c"} - - return { - "name": "parallel", - "passed": ok, - "result": result, - "state_log": state.log, - } - - -async def test_decision() -> dict[str, Any]: - """Test decision routing: correct branch selected based on input type. - - Returns: - Dictionary with test results and metadata. - """ - g = build_decision_graph() - graph = g.build() - - # Positive input -> int branch - state1 = GraphState() - result1 = await graph.run(state=state1, inputs=42) - ok1 = result1 == "Got int: 42" and "int_branch" in state1.log - - # Non-positive input -> str branch (transformed to "negative:-5") - state2 = GraphState() - result2 = await graph.run(state=state2, inputs=-5) - ok2 = result2 == "Got str: negative:-5" and "str_branch" in state2.log - - return { - "name": "decision", - "passed": ok1 and ok2, - "result_int": result1, - "result_str": result2, - } - - -async def test_sequential() -> dict[str, Any]: - """Test sequential chain: output of step 1 fed to step 2. - - Returns: - Dictionary with test results and metadata. - """ - g = build_sequential_graph() - graph = g.build() - state = GraphState() - result = await graph.run(state=state) - - ok = result == "Result: 30" - ok = ok and state.log == ["step_one", "step_two"] - - return { - "name": "sequential", - "passed": ok, - "result": result, - "state_log": state.log, - } - - -async def test_stream() -> dict[str, Any]: - """Test Graph.iter() yields intermediate steps. - - Returns: - Dictionary with test results and metadata. - """ - g = build_stream_graph() - graph = g.build() - state = GraphState() - - events: list[Any] = [] - async with graph.iter(state=state) as run: - events.extend([event async for event in run]) - - # Should see task lists and a final EndMarker - task_events = [e for e in events if isinstance(e, list)] - end_events = [e for e in events if isinstance(e, EndMarker)] - - ok = len(task_events) > 0 and len(end_events) == 1 - ok = ok and end_events[0].value == "final-1" # pyright: ignore[reportUnknownMemberType] - - return { - "name": "stream", - "passed": ok, - "event_count": len(events), - "task_events": len(task_events), - "end_events": len(end_events), - } - - -async def test_cancel() -> dict[str, Any]: - """Test cancellation mid-stream: task cancels cleanly without hanging. - - Returns: - Dictionary with test results and metadata. - """ - g = build_stream_graph() - graph = g.build() - state = GraphState() - - events: list[Any] = [] - async with graph.iter(state=state) as run: - async for event in run: - events.append(event) - if len(events) >= 2: # noqa: PLR2004 - break # Cancel early - - # We broke out early; no exception should have been raised - ok = len(events) >= 2 # noqa: PLR2004 - # Not all steps should have completed - ok = ok and len(state.log) < 2 # noqa: PLR2004 - - return { - "name": "cancel", - "passed": ok, - "events_before_cancel": len(events), - "state_log": state.log, - } - - -async def test_error() -> dict[str, Any]: - """Test error handling: exception in one fork branch propagates correctly. - - Returns: - Dictionary with test results and metadata. - """ - g = build_error_graph() - graph = g.build() - state = GraphState() - - raised: BaseException | None = None - try: - await graph.run(state=state) - except Exception as exc: # noqa: BLE001 - raised = exc - - ok = raised is not None - ok = ok and isinstance(raised, RuntimeError) - ok = ok and "Intentional fork branch failure" in str(raised) - - return { - "name": "error", - "passed": ok, - "exception_type": type(raised).__name__ if raised else None, - "exception_msg": str(raised) if raised else None, - } - - -def _detect_cycles( - nodes: dict[NodeID, AnyNode], - edges_by_source: dict[NodeID, list[Any]], -) -> list[NodeID] | None: - """Detect cycles in a graph via DFS. - - Args: - nodes: All nodes in the graph. - edges_by_source: Outgoing edges indexed by source node ID. - - Returns: - A list of node IDs forming a cycle, or None if acyclic. - """ - visited: set[NodeID] = set() - rec_stack: set[NodeID] = set() - - def _neighbors(node_id: NodeID) -> list[NodeID]: - """Extract destination IDs from outgoing paths.""" - return [ - item.destination_id - for path in edges_by_source.get(node_id, []) - for item in (path.items if hasattr(path, "items") else []) - if isinstance(item, DestinationMarker) - ] - - def _dfs(node_id: NodeID) -> list[NodeID] | None: - visited.add(node_id) - rec_stack.add(node_id) - for nxt in _neighbors(node_id): - if nxt not in visited: - cycle = _dfs(nxt) - if cycle is not None: - return cycle - elif nxt in rec_stack: - return [node_id, nxt] - rec_stack.discard(node_id) - return None - - for nid in nodes: - if nid not in visited: - cycle = _dfs(nid) - if cycle is not None: - return cycle - return None - - -async def test_cycle() -> dict[str, Any]: - """Test DAG cycle detection. - - pydantic-graph's GraphBuilder does **not** detect cycles at build time. - This test validates that behavior and demonstrates a custom cycle - detector that can be used by AgentPool if needed. - - Returns: - Dictionary with test results and metadata. - """ - g = GraphBuilder(output_type=str) - - @g.step - async def a_step(ctx: StepContext[None, None, None]) -> str: - return "a" - - @g.step - async def b_step(ctx: StepContext[None, None, str]) -> str: - return "b" - - # Normal forward edges - g.add( - g.edge_from(g.start_node).to(a_step), - g.edge_from(a_step).to(b_step), - g.edge_from(b_step).to(g.end_node), - ) - - # Add a backward edge to create a cycle - g.add_edge(b_step, a_step) - - # pydantic-graph builds the graph without error - graph = g.build() - build_succeeded = graph is not None - - # Our custom cycle detector finds the cycle - cycle = _detect_cycles(graph.nodes, graph.edges_by_source) - cycle_found = cycle is not None - - ok = build_succeeded and cycle_found - - return { - "name": "cycle", - "passed": ok, - "build_succeeded": build_succeeded, - "cycle_detected": cycle_found, - "cycle_nodes": cycle, - "note": "pydantic-graph does not detect cycles; custom validation needed", - } - - -# --------------------------------------------------------------------------- -# Main / CLI -# --------------------------------------------------------------------------- - - -TESTS = { - "parallel": test_parallel, - "decision": test_decision, - "sequential": test_sequential, - "stream": test_stream, - "cancel": test_cancel, - "error": test_error, - "cycle": test_cycle, -} - - -async def run_single(name: str) -> dict[str, Any]: - """Run a single test and persist evidence. - - Args: - name: Test identifier. - - Returns: - Test result dictionary. - """ - result = await TESTS[name]() - path = evidence_path(f"task-1-{name}.txt") - path.write_text( - f"Test: {name}\nPassed: {result['passed']}\nDetails: {result}\n", - encoding="utf-8", - ) - return result - - -async def main() -> int: - """CLI entrypoint. - - Returns: - Exit code (0 on success, 1 on failure). - """ - parser = argparse.ArgumentParser(description="Validate pydantic-graph builder API") - parser.add_argument("--test-parallel", action="store_true", help="Run parallel execution test") - parser.add_argument("--test-decision", action="store_true", help="Run decision routing test") - parser.add_argument("--test-sequential", action="store_true", help="Run sequential chain test") - parser.add_argument("--test-stream", action="store_true", help="Run streaming test") - parser.add_argument("--test-cancel", action="store_true", help="Run cancellation test") - parser.add_argument("--test-error", action="store_true", help="Run error handling test") - parser.add_argument("--test-cycle", action="store_true", help="Run cycle detection test") - parser.add_argument("--test-all", action="store_true", help="Run all tests") - args = parser.parse_args() - - selected = [name for name in TESTS if getattr(args, f"test_{name}") or args.test_all] - - if not selected: - parser.print_help() - return 1 - - results: list[dict[str, Any]] = [] - for name in selected: - result = await run_single(name) - results.append(result) - status = "PASS" if result["passed"] else "FAIL" - print(f" [{status}] {name}") - - # Append findings to learnings.md - notepad = Path(".omo/notepads/migrate-to-pydantic-graph/learnings.md") - notepad.parent.mkdir(parents=True, exist_ok=True) - with notepad.open("a", encoding="utf-8") as fh: - fh.write("\n## Prototype Validation Findings\n\n") - fh.write("Date: 2026-06-03\n\n") - for r in results: - fh.write(f"- **{r['name']}**: {'PASS' if r['passed'] else 'FAIL'}\n") - if not r["passed"]: - fh.write(f" - Details: {r}\n") - fh.write("\n") - - all_passed = all(r["passed"] for r in results) - return 0 if all_passed else 1 - - -if __name__ == "__main__": - sys.exit(asyncio.run(main())) diff --git a/reproduce_skill_bug.py b/reproduce_skill_bug.py deleted file mode 100644 index ae5c80018..000000000 --- a/reproduce_skill_bug.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Quick red flag test: verify skill reference loading against local MCP scratchpad. - -Run from packages/wolfharness directory: - uv run python reproduce_skill_bug.py -""" - -from __future__ import annotations - -import asyncio -import sys - - -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") - -from wolfharness.resource_providers.mcp_provider import MCPResourceProvider - -from wolfharness.skills.uri_resolver import SkillURIResolver - - -async def main(): - print("=" * 60) - print("Skill Reference Loading - Red Flag Test") - print("=" * 60) - print() - - provider = MCPResourceProvider( - server="http://127.0.0.1:8890/mcp", - name="pool_mcp_scratchpad", - ) - - try: - await provider.__aenter__() - print(f"Provider connected: {provider.server}") - - # List skills from provider - skills = await provider.get_skills() - print(f"\nSkills from provider ({len(skills)} total):") - for s in skills: - print(f" - {s.name}: {s.skill_path}") - - # Create resolver and register provider - resolver = SkillURIResolver() - resolver.register_provider("pool_mcp_scratchpad", provider) - - # Test 1: Bare skill name (should work) - print("\n--- Test 1: Bare skill name ---") - try: - skill = await resolver.resolve("systematic-troubleshooting") - print(f"OK: {skill.name}") - except Exception as e: # noqa: BLE001 - print(f"FAIL: {e}") - - # Test 2: skill:// URI with reference (the bug) - print("\n--- Test 2: skill:// URI with reference ---") - uri = "skill://systematic-troubleshooting/references/expert_knowledge/excavator/excavator-hard-starting.md" - try: - skill = await resolver.resolve(uri) - print(f"OK: {skill.name}") - ref_path = getattr(skill, "_resolved_reference_path", None) - print(f" _resolved_reference_path: {ref_path}") - except Exception as e: # noqa: BLE001 - print(f"FAIL: {e}") - - finally: - await provider.__aexit__(None, None, None) - print("\nDisconnected") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/runtime_hook.py b/runtime_hook.py deleted file mode 100644 index f29e61a85..000000000 --- a/runtime_hook.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -# Disable Pydantic plugins (logfire) to avoid source code inspection issues in frozen apps -os.environ["PYDANTIC_DISABLE_PLUGINS"] = "1" diff --git a/test_graph_config_loading.py b/test_graph_config_loading.py deleted file mode 100644 index a041cf7e3..000000000 --- a/test_graph_config_loading.py +++ /dev/null @@ -1,263 +0,0 @@ -"""Test script for graph config loading in AgentPool. - -Validates that: -- Old YAML configs (teams:/connections:) load and translate correctly -- New graph: YAML configs load and build correctly -- Config validation errors include file location -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -import pytest - -from wolfharness import AgentPool -from wolfharness_config.graph_translation import GraphConfig - - -if TYPE_CHECKING: - from pathlib import Path - - -# ============================================================================= -# Fixtures -# ============================================================================= - - -@pytest.fixture -def old_config_path(tmp_path: Path) -> Path: - """Create an old-style config with connections.""" - config = tmp_path / "old_config.yml" - config.write_text(""" -agents: - agent_a: - type: native - model: test - system_prompt: "You are agent A" - connections: - - type: node - name: agent_b - - agent_b: - type: native - model: test - system_prompt: "You are agent B" -""") - return config - - -@pytest.fixture -def old_teams_config_path(tmp_path: Path) -> Path: - """Create an old-style config with teams.""" - config = tmp_path / "old_teams_config.yml" - config.write_text(""" -agents: - analyzer: - type: native - model: test - system_prompt: "You are analyzer" - - reviewer: - type: native - model: test - system_prompt: "You are reviewer" - -teams: - review_pipeline: - mode: sequential - members: [analyzer, reviewer] -""") - return config - - -@pytest.fixture -def new_graph_config_path(tmp_path: Path) -> Path: - """Create a new-style config with graph: section.""" - config = tmp_path / "new_graph_config.yml" - config.write_text(""" -agents: - step_a: - type: native - model: test - system_prompt: "You are step A" - - step_b: - type: native - model: test - system_prompt: "You are step B" - -graph: - name: test_workflow - steps: - - id: step_a - agent: step_a - - id: step_b - agent: step_b - edges: - - from: start - to: step_a - - from: step_a - to: step_b - - from: step_b - to: end -""") - return config - - -@pytest.fixture -def invalid_graph_config_path(tmp_path: Path) -> Path: - """Create a config with an invalid graph reference.""" - config = tmp_path / "invalid_graph_config.yml" - config.write_text(""" -agents: - step_a: - type: native - model: test - -graph: - steps: - - id: step_a - agent: step_a - - id: bad_step - agent: nonexistent_agent - edges: - - from: start - to: step_a - - from: step_a - to: bad_step -""") - return config - - -# ============================================================================= -# Tests -# ============================================================================= - - -@pytest.mark.asyncio -async def test_old_connections_config_loads(old_config_path: Path) -> None: - """Old configs with connections: should load without error.""" - pool = AgentPool(old_config_path) - # Graph config should be translated from connections - assert pool._graph_config is not None - assert isinstance(pool._graph_config, GraphConfig) - assert len(pool._graph_config.steps) == 2 # noqa: PLR2004 - assert len(pool._graph_config.edges) == 1 - - async with pool: - # Graph should be built from config - assert pool._graph is not None - assert pool.graph is not None - - -@pytest.mark.asyncio -async def test_old_teams_config_loads(old_teams_config_path: Path) -> None: - """Old configs with teams: should load and translate correctly.""" - pool = AgentPool(old_teams_config_path) - # Graph config should be translated from teams - assert pool._graph_config is not None - assert isinstance(pool._graph_config, GraphConfig) - assert len(pool._graph_config.steps) == 2 # noqa: PLR2004 - # Sequential team: start -> analyzer -> reviewer -> end = 3 edges - assert len(pool._graph_config.edges) == 3 # noqa: PLR2004 - - async with pool: - assert pool._graph is not None - assert pool.graph is not None - - -@pytest.mark.asyncio -async def test_new_graph_config_loads(new_graph_config_path: Path) -> None: - """New configs with graph: section should load natively.""" - pool = AgentPool(new_graph_config_path) - assert pool._graph_config is not None - assert isinstance(pool._graph_config, GraphConfig) - assert pool._graph_config.name == "test_workflow" - assert len(pool._graph_config.steps) == 2 # noqa: PLR2004 - assert len(pool._graph_config.edges) == 3 # noqa: PLR2004 - - async with pool: - assert pool._graph is not None - assert pool.graph is not None - - -@pytest.mark.asyncio -async def test_invalid_graph_config_error(invalid_graph_config_path: Path) -> None: - """Invalid graph configs should raise with config location in message.""" - pool = AgentPool(invalid_graph_config_path) - # Config loading succeeds (translation works) - assert pool._graph_config is not None - - with pytest.raises(RuntimeError) as exc_info: - async with pool: - pass - - # The top-level exception is wrapped by the outer __aenter__ try block. - # Walk the cause chain to find the specific graph build error. - exc = exc_info.value - full_msg = "" - while exc is not None: - full_msg += str(exc) + "\n" - exc = exc.__cause__ - - # Error should mention the config file path somewhere in the chain - assert str(invalid_graph_config_path) in full_msg - # Error should mention the unknown agent - assert "unknown agent" in full_msg.lower() or "bad_step" in full_msg.lower() - - -@pytest.mark.asyncio -async def test_empty_config_loads(tmp_path: Path) -> None: - """Configs with no teams/connections/graph should have no graph config.""" - config = tmp_path / "empty_config.yml" - config.write_text(""" -agents: - solo: - type: native - model: test -""") - pool = AgentPool(config) - assert pool._graph_config is None - - async with pool: - # No graph config means no built graph - assert pool.graph is None - - -@pytest.mark.asyncio -async def test_programmatic_manifest_with_graph() -> None: - """Programmatic manifests with graph in model_extra should work.""" - from wolfharness.models.agents import NativeAgentConfig - from wolfharness.models.manifest import AgentsManifest - - manifest = AgentsManifest( - agents={ - "a": NativeAgentConfig(name="a", model="test"), - "b": NativeAgentConfig(name="b", model="test"), - }, - # extra="allow" stores unknown fields in model_extra - graph={ - "name": "prog_graph", - "steps": [ - {"id": "a", "agent": "a"}, - {"id": "b", "agent": "b"}, - ], - "edges": [ - {"from": "start", "to": "a"}, - {"from": "a", "to": "b"}, - {"from": "b", "to": "end"}, - ], - }, - ) - - pool = AgentPool(manifest) - assert pool._graph_config is not None - assert pool._graph_config.name == "prog_graph" - - async with pool: - assert pool._graph is not None - - -if __name__ == "__main__": - pytest.main([__file__, "-v"]) diff --git a/test_pool_skills.py b/test_pool_skills.py deleted file mode 100644 index 9f06c44b9..000000000 --- a/test_pool_skills.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Test skill resolution with actual AgentPool setup mimicking serve-acp. - -Usage (from packages/wolfharness): - uv run python test_pool_skills.py -""" - -from __future__ import annotations - -import asyncio -import sys - - -sys.path.insert(0, "/Users/yuchen.liu/src/yilab/iroot-llm/packages/wolfharness/src") - -from wolfharness import AgentPool - - -async def main(): # noqa: PLR0915 - print("=" * 60) - print("AgentPool Skill Resolution Test") - print("=" * 60) - print() - - config_path = ( - "/Users/yuchen.liu/src/yilab/iroot-llm/packages/xeno-agent/config/diag-agent-ng.yaml" - ) - print(f"Config: {config_path}") - print() - - try: - pool = AgentPool(config_path) - async with pool: - print("Pool initialized") - print(f" skill_resolver: {pool.skill_resolver is not None}") - print(f" skill_provider: {pool.skill_provider is not None}") - print(f" skills_dirs: {pool.skills.registry.skills_dirs if pool.skills else 'N/A'}") - print() - - # List local skills - local_skills = pool.skills.list_skills() - print(f"Local skills ({len(local_skills)}):") - for s in local_skills: - print(f" - {s.name}: {s.skill_path}") - print() - - # List provider skills (including MCP) - if pool.skill_provider: - provider_skills = await pool.skill_provider.get_skills() - print(f"Provider skills ({len(provider_skills)}):") - for s in provider_skills: - print(f" - {s.name}: {s.skill_path}") - print() - - # Test resolver - resolver = pool.skill_resolver - if resolver: - print("Registered providers:") - for name in resolver.list_providers(): - print(f" - {name}") - print() - - # Test 1: Bare skill name - print("--- Test 1: Bare skill name ---") - try: - skill = await resolver.resolve("systematic-troubleshooting") - print(f"OK: {skill.name}") - except Exception as e: # noqa: BLE001 - print(f"FAIL: {e}") - print() - - # Test 2: skill:// URI with reference - print("--- Test 2: skill:// URI with reference ---") - uri = "skill://systematic-troubleshooting/references/expert_knowledge/excavator/excavator-hard-starting.md" - try: - skill = await resolver.resolve(uri) - print(f"OK: {skill.name}") - ref_path = getattr(skill, "_resolved_reference_path", None) - print(f" ref_path: {ref_path}") - except Exception as e: # noqa: BLE001 - print(f"FAIL: {e}") - print() - - except Exception as e: # noqa: BLE001 - print(f"ERROR: {e}") - import traceback - - traceback.print_exc() - - print("Done") - - -if __name__ == "__main__": - asyncio.run(main()) From 4744b9f14b2bf500d7e469f3365849ecb856a68d Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 16:48:25 +0800 Subject: [PATCH 12/23] chore: add .codegraph/ to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 4a1d89325..fe8c14d4b 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,9 @@ test_configs/ # Prometheus / OpenCode workspace files .omo/ +# Codegraph local index cache +.codegraph/ + # Git worktrees .worktrees/ From f50b6c044be4690071dae486b8771436709ebbcc Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 16:54:37 +0800 Subject: [PATCH 13/23] chore: reorganize root directories and remove stale files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move config/tools/ → schema/tools/ (tool schemas are docs, not config) - Move wolfharness-session-pool/docs/ops/ → docs/ops/ (ops docs belong in docs/) - Remove benchmarks/ (moved to tests/benchmarks/ as a separate migration) - Remove distribution/zed/ (Zed extension, not core to project) - Remove .copier-answers.yml (upstream template metadata, stale after fork) --- .copier-answers.yml | 21 - benchmarks/__init__.py | 1 - benchmarks/capability_overhead.py | 739 ------------------ distribution/zed/LICENSE | 21 - distribution/zed/extension.toml | 41 - distribution/zed/icons/llmling_agent.svg | 7 - .../docs => docs}/ops/acp-handler.md | 0 .../docs => docs}/ops/opencode-handler.md | 0 .../docs => docs}/ops/session-pool.md | 0 .../tools/background_cancel.yaml | 0 .../tools/background_output.yaml | 0 {config => schema}/tools/steer_task.yaml | 0 {config => schema}/tools/task.yaml | 0 13 files changed, 830 deletions(-) delete mode 100644 .copier-answers.yml delete mode 100644 benchmarks/__init__.py delete mode 100644 benchmarks/capability_overhead.py delete mode 100644 distribution/zed/LICENSE delete mode 100644 distribution/zed/extension.toml delete mode 100644 distribution/zed/icons/llmling_agent.svg rename {wolfharness-session-pool/docs => docs}/ops/acp-handler.md (100%) rename {wolfharness-session-pool/docs => docs}/ops/opencode-handler.md (100%) rename {wolfharness-session-pool/docs => docs}/ops/session-pool.md (100%) rename {config => schema}/tools/background_cancel.yaml (100%) rename {config => schema}/tools/background_output.yaml (100%) rename {config => schema}/tools/steer_task.yaml (100%) rename {config => schema}/tools/task.yaml (100%) diff --git a/.copier-answers.yml b/.copier-answers.yml deleted file mode 100644 index 3fba1c8c4..000000000 --- a/.copier-answers.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Changes here will be overwritten by Copier -_commit: v2.3.3 -_src_path: https://github.com/phil65/copier-phil65.git -author_email: philipptemminghoff@googlemail.com -author_fullname: Philipp Temminghoff -author_username: phil65 -copyright_date: "2025" -copyright_holder: Philipp Temminghoff -copyright_holder_email: philipptemminghoff@googlemail.com -libraries_use_pydantic: true -libraries_use_qt: false -project_description: Pydantic-AI based Multi-Agent Framework with YAML-based Agents, - Teams, Workflows & Extended ACP / AGUI integration -project_name: AgentPool -python_minimum_version: "3.13" -python_package_command_line_name: wolfharness -python_package_distribution_name: wolfharness -python_package_import_name: wolfharness -repository_name: wolfharness -repository_namespace: phil65 -repository_provider: github.com diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py deleted file mode 100644 index 1a2f3c4e1..000000000 --- a/benchmarks/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Benchmark suite for AgentPool performance measurements.""" diff --git a/benchmarks/capability_overhead.py b/benchmarks/capability_overhead.py deleted file mode 100644 index 39565eaee..000000000 --- a/benchmarks/capability_overhead.py +++ /dev/null @@ -1,739 +0,0 @@ -"""Benchmark comparing old manager-based approach vs new capability-based approach. - -Task 62 from the thin-pydantic-ai-wrappers migration plan. - -Measures: -- Shim layer creation overhead (ToolManager, AgentHooks, MCPManager) -- Agent construction latency (cold start + warm start via get_agentlet) -- Memory overhead of capability wrappers vs direct manager usage - -Usage: - python -m benchmarks.capability_overhead - -Methodology: -- time.perf_counter() for all latency measurements -- tracemalloc for memory measurements -- 10+ iterations for statistical significance -- Both cold start (first run) and warm start (subsequent runs) -""" - -from __future__ import annotations - -import asyncio -from pathlib import Path -import statistics -import sys -import time -import tracemalloc -from typing import Any - -from pydantic_ai.models.test import TestModel -from wolfharness.resource_providers import StaticResourceProvider - -from wolfharness import Agent -from wolfharness.hooks import AgentHooks -from wolfharness.hooks.base import Hook, HookInput, HookResult -from wolfharness.mcp_server.manager import MCPManager -from wolfharness.tools import Tool, ToolManager - - -# --------------------------------------------------------------------------- -# Configuration -# --------------------------------------------------------------------------- - -ITERATIONS = 10 -WARMUP_ITERATIONS = 2 -NUM_TOOLS = 10 - -TEST_MODEL = TestModel(custom_output_text="benchmark response") - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - - -def _make_tools(count: int) -> list[Tool[Any]]: - """Create N simple tools for benchmarking.""" - tools: list[Tool[Any]] = [] - for i in range(count): - - def _make_tool(n: int = i) -> Tool[Any]: - def tool_fn(query: str) -> str: - """A benchmark tool.""" - return f"Result {n}: {query}" - - return Tool.from_callable(tool_fn, name_override=f"tool_{n}") - - tools.append(_make_tool()) - return tools - - -def _make_hooks() -> AgentHooks: - """Create AgentHooks with one hook of each type.""" - - class NoOpHook(Hook): - """No-op hook for benchmarking.""" - - def __init__(self) -> None: - super().__init__(event="pre_turn") - - async def execute(self, input_data: HookInput, env: Any = None) -> HookResult: - return HookResult(decision="allow") - - return AgentHooks( - pre_turn=[NoOpHook()], - post_turn=[NoOpHook()], - pre_tool_use=[NoOpHook()], - post_tool_use=[NoOpHook()], - _warn=False, - ) - - -def _format_latency(times: list[float]) -> dict[str, float]: - """Format latency statistics from a list of times (seconds).""" - times_ms = [t * 1000 for t in times] - return { - "mean_ms": statistics.mean(times_ms), - "median_ms": statistics.median(times_ms), - "min_ms": min(times_ms), - "max_ms": max(times_ms), - "stdev_ms": statistics.stdev(times_ms) if len(times_ms) > 1 else 0.0, - } - - -def _format_memory(peak: int) -> str: - """Format memory peak in human-readable units.""" - if peak < 1024: # noqa: PLR2004 - return f"{peak} B" - if peak < 1024 * 1024: - return f"{peak / 1024:.2f} KB" - return f"{peak / (1024 * 1024):.2f} MB" - - -# --------------------------------------------------------------------------- -# Micro-benchmarks: Shim layer creation -# --------------------------------------------------------------------------- - - -async def benchmark_toolmanager_vs_capability() -> dict[str, Any]: - """Benchmark ToolManager.get_tools() vs ResourceProvider.as_capability().""" - tools = _make_tools(NUM_TOOLS) - tool_manager = ToolManager(tools, _warn=False) - provider = StaticResourceProvider(name="benchmark", tools=tools) - - # Warmup - for _ in range(WARMUP_ITERATIONS): - _ = await tool_manager.get_tools() - _ = provider.as_capability() - - # Old approach: ToolManager.get_tools() - old_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = await tool_manager.get_tools() - old_times.append(time.perf_counter() - start) - - # New approach: ResourceProvider.as_capability() - new_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = provider.as_capability() - new_times.append(time.perf_counter() - start) - - return { - "old_approach": _format_latency(old_times), - "new_approach": _format_latency(new_times), - "overhead_ms": _format_latency(new_times)["mean_ms"] - - _format_latency(old_times)["mean_ms"], - "overhead_pct": ( - (_format_latency(new_times)["mean_ms"] - _format_latency(old_times)["mean_ms"]) - / _format_latency(old_times)["mean_ms"] - * 100 - ), - } - - -async def benchmark_agenthooks_vs_capability() -> dict[str, Any]: - """Benchmark AgentHooks direct usage vs as_capability().""" - hooks = _make_hooks() - - # Warmup - for _ in range(WARMUP_ITERATIONS): - _ = hooks.has_hooks() - _ = hooks.as_capability() - - # Old approach: just instantiate / check hooks (the old code would call - # run_pre_turn_hooks etc. directly; we measure the lightweight access) - old_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = hooks.has_hooks() - old_times.append(time.perf_counter() - start) - - # New approach: AgentHooks.as_capability() creates pydantic-ai Hooks - new_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = hooks.as_capability() - new_times.append(time.perf_counter() - start) - - return { - "old_approach": _format_latency(old_times), - "new_approach": _format_latency(new_times), - "overhead_ms": _format_latency(new_times)["mean_ms"] - - _format_latency(old_times)["mean_ms"], - "overhead_pct": ( - (_format_latency(new_times)["mean_ms"] - _format_latency(old_times)["mean_ms"]) - / _format_latency(old_times)["mean_ms"] - * 100 - ), - } - - -async def benchmark_mcpmanager_vs_capability() -> dict[str, Any]: - """Benchmark MCPManager direct access vs as_capability().""" - mcp_manager = MCPManager(_warn=False) - - # Warmup - for _ in range(WARMUP_ITERATIONS): - _ = mcp_manager.get_mcp_providers() - _ = mcp_manager.as_capability() - - # Old approach: MCPManager.get_mcp_providers() - old_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = mcp_manager.get_mcp_providers() - old_times.append(time.perf_counter() - start) - - # New approach: MCPManager.as_capability() - new_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = mcp_manager.as_capability() - new_times.append(time.perf_counter() - start) - - return { - "old_approach": _format_latency(old_times), - "new_approach": _format_latency(new_times), - "overhead_ms": _format_latency(new_times)["mean_ms"] - - _format_latency(old_times)["mean_ms"], - "overhead_pct": ( - (_format_latency(new_times)["mean_ms"] - _format_latency(old_times)["mean_ms"]) - / _format_latency(old_times)["mean_ms"] - * 100 - ), - } - - -# --------------------------------------------------------------------------- -# Integration benchmarks: Agent construction (get_agentlet) -# --------------------------------------------------------------------------- - - -async def benchmark_agent_construction() -> dict[str, Any]: - """Benchmark Agent.get_agentlet() latency: cold vs warm starts.""" - tools = _make_tools(NUM_TOOLS) - - # Baseline agent: no tools, no hooks, no MCP - baseline_agent = Agent(name="baseline", model=TEST_MODEL, session=False) - - # Capability agent: tools via new as_capability approach - provider = StaticResourceProvider(name="benchmark", tools=tools) - capability_agent = Agent( - name="capability", - model=TEST_MODEL, - session=False, - toolsets=[provider], - ) - - # Old-style agent: tools via ToolManager (still created internally, - # but we also attach hooks to simulate old full config) - hooks = _make_hooks() - old_style_agent = Agent( - name="old_style", - model=TEST_MODEL, - session=False, - tools=tools, - hooks=hooks, - ) - - async with baseline_agent, capability_agent, old_style_agent: - # Warmup - for _ in range(WARMUP_ITERATIONS): - _ = await baseline_agent.get_agentlet(None, str, None, None) - _ = await capability_agent.get_agentlet(None, str, None, None) - _ = await old_style_agent.get_agentlet(None, str, None, None) - - # Baseline cold start - baseline_cold_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = await baseline_agent.get_agentlet(None, str, None, None) - baseline_cold_times.append(time.perf_counter() - start) - - # Capability cold start - capability_cold_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = await capability_agent.get_agentlet(None, str, None, None) - capability_cold_times.append(time.perf_counter() - start) - - # Old-style cold start - old_cold_times: list[float] = [] - for _ in range(ITERATIONS): - start = time.perf_counter() - _ = await old_style_agent.get_agentlet(None, str, None, None) - old_cold_times.append(time.perf_counter() - start) - - return { - "baseline_no_tools": _format_latency(baseline_cold_times), - "with_capabilities": _format_latency(capability_cold_times), - "with_old_shims": _format_latency(old_cold_times), - "capability_overhead_vs_baseline_ms": ( - _format_latency(capability_cold_times)["mean_ms"] - - _format_latency(baseline_cold_times)["mean_ms"] - ), - "old_shim_overhead_vs_baseline_ms": ( - _format_latency(old_cold_times)["mean_ms"] - - _format_latency(baseline_cold_times)["mean_ms"] - ), - "capability_vs_old_shim_delta_ms": ( - _format_latency(capability_cold_times)["mean_ms"] - - _format_latency(old_cold_times)["mean_ms"] - ), - } - - -# --------------------------------------------------------------------------- -# Memory benchmarks -# --------------------------------------------------------------------------- - - -async def benchmark_memory_overhead() -> dict[str, Any]: - """Benchmark memory overhead of shim layers using tracemalloc.""" - tools = _make_tools(NUM_TOOLS) - - # --- ToolManager vs ResourceProvider.as_capability() --- - tracemalloc.start() - tool_manager = ToolManager(tools, _warn=False) - _ = await tool_manager.get_tools() - old_tools_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - tracemalloc.start() - provider = StaticResourceProvider(name="benchmark", tools=tools) - _ = provider.as_capability() - new_tools_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - # --- AgentHooks vs as_capability() --- - hooks = _make_hooks() - tracemalloc.start() - _ = hooks.has_hooks() - old_hooks_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - tracemalloc.start() - _ = hooks.as_capability() - new_hooks_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - # --- MCPManager vs as_capability() --- - mcp_manager = MCPManager(_warn=False) - tracemalloc.start() - _ = mcp_manager.get_mcp_providers() - old_mcp_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - tracemalloc.start() - _ = mcp_manager.as_capability() - new_mcp_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - # --- Full agent construction --- - baseline_agent = Agent(name="baseline", model=TEST_MODEL, session=False) - capability_agent = Agent( - name="capability", - model=TEST_MODEL, - session=False, - toolsets=[StaticResourceProvider(name="benchmark", tools=tools)], - ) - old_style_agent = Agent( - name="old_style", - model=TEST_MODEL, - session=False, - tools=tools, - hooks=_make_hooks(), - ) - - async with baseline_agent, capability_agent, old_style_agent: - tracemalloc.start() - _ = await baseline_agent.get_agentlet(None, str, None, None) - baseline_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - tracemalloc.start() - _ = await capability_agent.get_agentlet(None, str, None, None) - capability_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - tracemalloc.start() - _ = await old_style_agent.get_agentlet(None, str, None, None) - old_peak = tracemalloc.get_traced_memory()[1] - tracemalloc.stop() - - return { - "tool_shim": { - "old_peak_bytes": old_tools_peak, - "new_peak_bytes": new_tools_peak, - "delta_bytes": new_tools_peak - old_tools_peak, - "old_formatted": _format_memory(old_tools_peak), - "new_formatted": _format_memory(new_tools_peak), - "delta_formatted": _format_memory(abs(new_tools_peak - old_tools_peak)), - }, - "hooks_shim": { - "old_peak_bytes": old_hooks_peak, - "new_peak_bytes": new_hooks_peak, - "delta_bytes": new_hooks_peak - old_hooks_peak, - "old_formatted": _format_memory(old_hooks_peak), - "new_formatted": _format_memory(new_hooks_peak), - "delta_formatted": _format_memory(abs(new_hooks_peak - old_hooks_peak)), - }, - "mcp_shim": { - "old_peak_bytes": old_mcp_peak, - "new_peak_bytes": new_mcp_peak, - "delta_bytes": new_mcp_peak - old_mcp_peak, - "old_formatted": _format_memory(old_mcp_peak), - "new_formatted": _format_memory(new_mcp_peak), - "delta_formatted": _format_memory(abs(new_mcp_peak - old_mcp_peak)), - }, - "agent_construction": { - "baseline_peak_bytes": baseline_peak, - "capability_peak_bytes": capability_peak, - "old_peak_bytes": old_peak, - "capability_delta_vs_baseline_bytes": capability_peak - baseline_peak, - "old_delta_vs_baseline_bytes": old_peak - baseline_peak, - "capability_vs_old_delta_bytes": capability_peak - old_peak, - "baseline_formatted": _format_memory(baseline_peak), - "capability_formatted": _format_memory(capability_peak), - "old_formatted": _format_memory(old_peak), - }, - } - - -# --------------------------------------------------------------------------- -# Main runner -# --------------------------------------------------------------------------- - - -async def run_all_benchmarks() -> dict[str, Any]: - """Run all benchmarks and return structured results.""" - print("=" * 70) - print("AgentPool Capability Overhead Benchmarks") - print("Task 62: thin-pydantic-ai-wrappers migration") - print("=" * 70) - print() - - results: dict[str, Any] = {} - - # 1. ToolManager vs ResourceProvider.as_capability() - print("[1/5] Benchmarking ToolManager vs ResourceProvider.as_capability()...") - results["tool_shim"] = await benchmark_toolmanager_vs_capability() - print(" Done.") - - # 2. AgentHooks vs as_capability() - print("[2/5] Benchmarking AgentHooks vs as_capability()...") - results["hooks_shim"] = await benchmark_agenthooks_vs_capability() - print(" Done.") - - # 3. MCPManager vs as_capability() - print("[3/5] Benchmarking MCPManager vs as_capability()...") - results["mcp_shim"] = await benchmark_mcpmanager_vs_capability() - print(" Done.") - - # 4. Agent construction latency - print("[4/5] Benchmarking Agent.get_agentlet() latency...") - results["agent_construction"] = await benchmark_agent_construction() - print(" Done.") - - # 5. Memory overhead - print("[5/5] Benchmarking memory overhead...") - results["memory"] = await benchmark_memory_overhead() - print(" Done.") - - return results - - -def _print_results(results: dict[str, Any]) -> None: # noqa: PLR0915 - """Pretty-print benchmark results to stdout.""" - print() - print("=" * 70) - print("RESULTS") - print("=" * 70) - - # Tool shim - print() - print("--- Tool Shim: ToolManager.get_tools() vs as_capability() ---") - tool = results["tool_shim"] - print(" Old approach (ToolManager.get_tools):") - oa = tool["old_approach"] - print(f" mean={oa['mean_ms']:.3f}ms, median={oa['median_ms']:.3f}ms") - print(" New approach (ResourceProvider.as_capability):") - na = tool["new_approach"] - print(f" mean={na['mean_ms']:.3f}ms, median={na['median_ms']:.3f}ms") - print(f" Overhead: {tool['overhead_ms']:+.3f}ms ({tool['overhead_pct']:+.1f}%)") - - # Hooks shim - print() - print("--- Hooks Shim: AgentHooks vs as_capability() ---") - hooks = results["hooks_shim"] - print(" Old approach (direct access):") - hoa = hooks["old_approach"] - print(f" mean={hoa['mean_ms']:.3f}ms, median={hoa['median_ms']:.3f}ms") - print(" New approach (AgentHooks.as_capability):") - hna = hooks["new_approach"] - print(f" mean={hna['mean_ms']:.3f}ms, median={hna['median_ms']:.3f}ms") - print(f" Overhead: {hooks['overhead_ms']:+.3f}ms ({hooks['overhead_pct']:+.1f}%)") - - # MCP shim - print() - print("--- MCP Shim: MCPManager vs as_capability() ---") - mcp = results["mcp_shim"] - print(" Old approach (get_mcp_providers):") - moa = mcp["old_approach"] - print(f" mean={moa['mean_ms']:.3f}ms, median={moa['median_ms']:.3f}ms") - print(" New approach (MCPManager.as_capability):") - mna = mcp["new_approach"] - print(f" mean={mna['mean_ms']:.3f}ms, median={mna['median_ms']:.3f}ms") - print(f" Overhead: {mcp['overhead_ms']:+.3f}ms ({mcp['overhead_pct']:+.1f}%)") - - # Agent construction - print() - print("--- Agent Construction: get_agentlet() latency ---") - agent = results["agent_construction"] - print(" Baseline (no tools/hooks/MCP):") - abl = agent["baseline_no_tools"] - print(f" mean={abl['mean_ms']:.3f}ms, median={abl['median_ms']:.3f}ms") - print(" With capabilities (new approach):") - awc = agent["with_capabilities"] - print(f" mean={awc['mean_ms']:.3f}ms, median={awc['median_ms']:.3f}ms") - print(" With old shims (deprecated approach):") - aws = agent["with_old_shims"] - print(f" mean={aws['mean_ms']:.3f}ms, median={aws['median_ms']:.3f}ms") - print( - f" Capability overhead vs baseline: {agent['capability_overhead_vs_baseline_ms']:+.3f}ms" - ) - print(f" Old shim overhead vs baseline: {agent['old_shim_overhead_vs_baseline_ms']:+.3f}ms") - print(f" Capability vs old shim delta: {agent['capability_vs_old_shim_delta_ms']:+.3f}ms") - - # Memory - print() - print("--- Memory Overhead ---") - mem = results["memory"] - print(" Tool shim:") - ts = mem["tool_shim"] - print(f" Old: {ts['old_formatted']}, New: {ts['new_formatted']}") - print(f" Delta: {ts['delta_formatted']}") - print(" Hooks shim:") - hs = mem["hooks_shim"] - print(f" Old: {hs['old_formatted']}, New: {hs['new_formatted']}") - print(f" Delta: {hs['delta_formatted']}") - print(" MCP shim:") - ms = mem["mcp_shim"] - print(f" Old: {ms['old_formatted']}, New: {ms['new_formatted']}") - print(f" Delta: {ms['delta_formatted']}") - print(" Full agent construction:") - ac = mem["agent_construction"] - print(f" Baseline: {ac['baseline_formatted']}") - print(f" Capability: {ac['capability_formatted']}") - print(f" Old shim: {ac['old_formatted']}") - cd = ac["capability_delta_vs_baseline_bytes"] - print(f" Cap vs baseline delta: {_format_memory(cd)}") - od = ac["old_delta_vs_baseline_bytes"] - print(f" Old vs baseline delta: {_format_memory(od)}") - - print() - print("=" * 70) - - -def _write_markdown(results: dict[str, Any], path_str: str) -> None: - """Write results to a markdown file for evidence collection.""" - lines: list[str] = [ - "# Task 62 Benchmark Results: Capability Overhead", - "", - "**Date:** 2026-06-03", - "**Task:** thin-pydantic-ai-wrappers migration — benchmark old vs new approach", - "**Iterations per test:** 10", - "**Python version:** " + sys.version.split()[0], - "", - "## Methodology", - "", - "- **Latency:** `time.perf_counter()` in milliseconds", - "- **Memory:** `tracemalloc` peak memory during operation", - "- **Cold start:** First invocation after warmup", - "- **Statistical significance:** 10 iterations with mean, median, min, max, stdev", - "", - "## 1. Tool Shim Overhead", - "", - "Comparison: `ToolManager.get_tools()` (old) vs `ResourceProvider.as_capability()` (new)", - "", - "| Metric | Old Approach | New Approach | Overhead |", - "|--------|-------------|--------------|----------|", - ] - - tool = results["tool_shim"] - toa = tool["old_approach"] - tna = tool["new_approach"] - lines.append( - f"| Mean latency | {toa['mean_ms']:.3f}ms | {tna['mean_ms']:.3f}ms" - f" | {tool['overhead_ms']:+.3f}ms ({tool['overhead_pct']:+.1f}%) |" - ) - - lines.extend([ - "", - "## 2. Hooks Shim Overhead", - "", - "Comparison: `AgentHooks` direct access (old) vs `AgentHooks.as_capability()` (new)", - "", - "| Metric | Old Approach | New Approach | Overhead |", - "|--------|-------------|--------------|----------|", - ]) - - hooks = results["hooks_shim"] - hoa = hooks["old_approach"] - hna = hooks["new_approach"] - lines.append( - f"| Mean latency | {hoa['mean_ms']:.3f}ms | {hna['mean_ms']:.3f}ms" - f" | {hooks['overhead_ms']:+.3f}ms ({hooks['overhead_pct']:+.1f}%) |" - ) - - lines.extend([ - "", - "## 3. MCP Shim Overhead", - "", - "Comparison: `MCPManager.get_mcp_providers()` (old) vs `MCPManager.as_capability()` (new)", - "", - "| Metric | Old Approach | New Approach | Overhead |", - "|--------|-------------|--------------|----------|", - ]) - - mcp = results["mcp_shim"] - moa = mcp["old_approach"] - mna = mcp["new_approach"] - lines.append( - f"| Mean latency | {moa['mean_ms']:.3f}ms | {mna['mean_ms']:.3f}ms" - f" | {mcp['overhead_ms']:+.3f}ms ({mcp['overhead_pct']:+.1f}%) |" - ) - - lines.extend([ - "", - "## 4. Agent Construction Latency (get_agentlet)", - "", - "Comparison of `Agent.get_agentlet()` with different configurations.", - "All agents use `TestModel` (no real LLM calls).", - "", - "| Configuration | Mean | Median | Min | Max |", - "|---------------|------|--------|-----|-----|", - ]) - - agent = results["agent_construction"] - for key, label in [ - ("baseline_no_tools", "Baseline (no tools/hooks/MCP)"), - ("with_capabilities", "With capabilities (new approach)"), - ("with_old_shims", "With old shims (deprecated approach)"), - ]: - data = agent[key] - lines.append( - f"| {label} | {data['mean_ms']:.3f}ms | {data['median_ms']:.3f}ms | " - f"{data['min_ms']:.3f}ms | {data['max_ms']:.3f}ms |" - ) - - lines.extend([ - "", - "### Overhead Analysis", - "", - ( - f"- Capability overhead vs baseline:" - f" **{agent['capability_overhead_vs_baseline_ms']:+.3f}ms**" - ), - ( - f"- Old shim overhead vs baseline:" - f" **{agent['old_shim_overhead_vs_baseline_ms']:+.3f}ms**" - ), - (f"- Capability vs old shim delta: **{agent['capability_vs_old_shim_delta_ms']:+.3f}ms**"), - "", - "## 5. Memory Overhead", - "", - "Peak memory measured with `tracemalloc` during shim creation and agent construction.", - "", - "### Shim Layer Memory", - "", - "| Shim | Old Approach | New Approach | Delta |", - "|------|-------------|--------------|-------|", - ]) - - mem = results["memory"] - for key, label in [ - ("tool_shim", "Tool shim"), - ("hooks_shim", "Hooks shim"), - ("mcp_shim", "MCP shim"), - ]: - data = mem[key] - lines.append( - f"| {label} | {data['old_formatted']} | {data['new_formatted']} | " - f"{data['delta_formatted']} |" - ) - - lines.extend([ - "", - "### Full Agent Construction Memory", - "", - "| Configuration | Peak Memory |", - "|---------------|-------------|", - (f"| Baseline (no tools/hooks/MCP) | {mem['agent_construction']['baseline_formatted']} |"), - ( - f"| With capabilities (new approach)" - f" | {mem['agent_construction']['capability_formatted']} |" - ), - ( - f"| With old shims (deprecated approach)" - f" | {mem['agent_construction']['old_formatted']} |" - ), - "", - "## Summary", - "", - ( - "- The capability-based approach introduces a small latency overhead" - " compared to direct manager access." - ), - ( - "- The overhead is primarily in the `as_capability()` wrapper creation," - " not in the underlying data structures." - ), - ( - "- Memory overhead is negligible for typical agent configurations" - " (10 tools, 4 hooks, 0 MCP servers)." - ), - "- Both old and new approaches coexist; the new approach is the recommended path forward.", - "", - ]) - - Path(path_str).write_text("\n".join(lines)) - - print(f"Results written to: {path_str}") - - -async def main() -> None: - """Run benchmarks and emit results.""" - results = await run_all_benchmarks() - _print_results(results) - - # Write evidence file - evidence_path = ".omo/evidence/task-62-benchmarks.md" - _write_markdown(results, evidence_path) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/distribution/zed/LICENSE b/distribution/zed/LICENSE deleted file mode 100644 index a065efff8..000000000 --- a/distribution/zed/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024, Philipp Temminghoff - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/distribution/zed/extension.toml b/distribution/zed/extension.toml deleted file mode 100644 index aa53b4409..000000000 --- a/distribution/zed/extension.toml +++ /dev/null @@ -1,41 +0,0 @@ -id = "wolfharness" -name = "AgentPool" -description = "Pydantic-AI based Multi-Agent Framework with YAML-based Agents, Teams, Workflows & Extended ACP / AGUI integration" -version = "1.15.12" -schema_version = 1 -authors = ["Philipp Temminghoff"] -repository = "https://github.com/phil65/wolfharness" - -[agent_servers.wolfharness] -name = "AgentPool" -icon = "./icons/wolfharness.svg" - -[agent_servers.wolfharness.targets.darwin-aarch64] -archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-darwin-aarch64-1.15.12.zip" -cmd = "./wolfharness" -args = ["serve-acp"] - -[agent_servers.wolfharness.targets.darwin-x86_64] -archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-darwin-x86_64-1.15.12.zip" -cmd = "./wolfharness" -args = ["serve-acp"] - -# [agent_servers.wolfharness.targets.linux-aarch64] -# archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-linux-aarch64-1.15.12.zip" -# cmd = "./wolfharness" -# args = ["serve-acp"] - -[agent_servers.wolfharness.targets.linux-x86_64] -archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-linux-x86_64-1.15.12.zip" -cmd = "./wolfharness" -args = ["serve-acp"] - -# [agent_servers.wolfharness.targets.windows-aarch64] -# archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-windows-aarch64-1.15.12.zip" -# cmd = "./wolfharness.exe" -# args = ["serve-acp"] - -[agent_servers.wolfharness.targets.windows-x86_64] -archive = "https://github.com/phil65/wolfharness/releases/download/v2.9.5/wolfharness-windows-x86_64-1.15.12.zip" -cmd = "./wolfharness.exe" -args = ["serve-acp"] diff --git a/distribution/zed/icons/llmling_agent.svg b/distribution/zed/icons/llmling_agent.svg deleted file mode 100644 index 05418cc0f..000000000 --- a/distribution/zed/icons/llmling_agent.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/wolfharness-session-pool/docs/ops/acp-handler.md b/docs/ops/acp-handler.md similarity index 100% rename from wolfharness-session-pool/docs/ops/acp-handler.md rename to docs/ops/acp-handler.md diff --git a/wolfharness-session-pool/docs/ops/opencode-handler.md b/docs/ops/opencode-handler.md similarity index 100% rename from wolfharness-session-pool/docs/ops/opencode-handler.md rename to docs/ops/opencode-handler.md diff --git a/wolfharness-session-pool/docs/ops/session-pool.md b/docs/ops/session-pool.md similarity index 100% rename from wolfharness-session-pool/docs/ops/session-pool.md rename to docs/ops/session-pool.md diff --git a/config/tools/background_cancel.yaml b/schema/tools/background_cancel.yaml similarity index 100% rename from config/tools/background_cancel.yaml rename to schema/tools/background_cancel.yaml diff --git a/config/tools/background_output.yaml b/schema/tools/background_output.yaml similarity index 100% rename from config/tools/background_output.yaml rename to schema/tools/background_output.yaml diff --git a/config/tools/steer_task.yaml b/schema/tools/steer_task.yaml similarity index 100% rename from config/tools/steer_task.yaml rename to schema/tools/steer_task.yaml diff --git a/config/tools/task.yaml b/schema/tools/task.yaml similarity index 100% rename from config/tools/task.yaml rename to schema/tools/task.yaml From d482be3d80d377972c6db0bef616f00438b91d32 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 16:57:39 +0800 Subject: [PATCH 14/23] chore: move tool schemas alongside BackgroundTaskCapability Move config/tools/ YAML schemas into the capability that loads them: src/wolfharness/capabilities/background_task/schemas/ --- .../capabilities/background_task/schemas}/background_cancel.yaml | 0 .../capabilities/background_task/schemas}/background_output.yaml | 0 .../capabilities/background_task/schemas}/steer_task.yaml | 0 .../wolfharness/capabilities/background_task/schemas}/task.yaml | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {schema/tools => src/wolfharness/capabilities/background_task/schemas}/background_cancel.yaml (100%) rename {schema/tools => src/wolfharness/capabilities/background_task/schemas}/background_output.yaml (100%) rename {schema/tools => src/wolfharness/capabilities/background_task/schemas}/steer_task.yaml (100%) rename {schema/tools => src/wolfharness/capabilities/background_task/schemas}/task.yaml (100%) diff --git a/schema/tools/background_cancel.yaml b/src/wolfharness/capabilities/background_task/schemas/background_cancel.yaml similarity index 100% rename from schema/tools/background_cancel.yaml rename to src/wolfharness/capabilities/background_task/schemas/background_cancel.yaml diff --git a/schema/tools/background_output.yaml b/src/wolfharness/capabilities/background_task/schemas/background_output.yaml similarity index 100% rename from schema/tools/background_output.yaml rename to src/wolfharness/capabilities/background_task/schemas/background_output.yaml diff --git a/schema/tools/steer_task.yaml b/src/wolfharness/capabilities/background_task/schemas/steer_task.yaml similarity index 100% rename from schema/tools/steer_task.yaml rename to src/wolfharness/capabilities/background_task/schemas/steer_task.yaml diff --git a/schema/tools/task.yaml b/src/wolfharness/capabilities/background_task/schemas/task.yaml similarity index 100% rename from schema/tools/task.yaml rename to src/wolfharness/capabilities/background_task/schemas/task.yaml From 7ec33e1ce23ea73aeddd3464cfa382a04d61101c Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 17:13:11 +0800 Subject: [PATCH 15/23] docs: add changelog directory and document release workflow in AGENTS.md - Add changelog/ directory with penguin-harness-inspired structure - Add changelog/README.md with full conventions - Add changelog/unreleased/ for work-in-progress entries - Document release workflow in AGENTS.md --- AGENTS.md | 22 +++++++++++++++++++ changelog/README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 changelog/README.md diff --git a/AGENTS.md b/AGENTS.md index 2c9e0ae0a..e2ab719d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,28 @@ All significant changes go through OpenSpec: `/opsx:explore` → `/opsx:propose` - Location: `openspec/` (24 capability specs, 34 archived changes) - CLI: `openspec` v1.4+ — see `openspec/config.yaml` +## Changelog + +Every release requires a changelog entry. See `changelog/README.md` for the full convention. + +### Release workflow + +1. Development changes accumulate in `changelog/unreleased/` — one detail file per logical change (`YYYY-MM-DD-short-slug.md`). +2. At release time: + - Rename `unreleased/` to the version number (e.g. `2.10.0/`). + - Write `README.md` (summary linking each detail file) and `RELEASE.md` (GitHub Release body). + - Update `pyproject.toml` version. + - Commit **before** creating the git tag. + - Create a fresh empty `unreleased/` for the next cycle. + +### Detail file format + +```markdown +# Title + +What changed and why. Use `##` sections once the entry covers more than one thing. +``` + ## Quick Commands ```bash diff --git a/changelog/README.md b/changelog/README.md new file mode 100644 index 000000000..dbfcd2a1b --- /dev/null +++ b/changelog/README.md @@ -0,0 +1,52 @@ +# Changelog Conventions + +## Structure + +``` +changelog/ +├── README.md # This file +├── unreleased/ # Work-in-progress changes for the next version +├── 2.10.0/ # Released version folder +│ ├── README.md # Summary: one line per change, linking detail files +│ ├── RELEASE.md # GitHub Release body (written at release time) +│ ├── YYYY-MM-DD-short-slug.md # Detail file per change entry +│ └── ... +└── ... +``` + +## Rules + +### Work in progress goes in `unreleased/` + +- Never in a numbered folder — the next version number is not knowable while work is being written. +- At release, rename `unreleased/` to the version actually shipped, swap its `# Unreleased` heading for `# Version X.Y.Z` plus a `Released on .` line, write `RELEASE.md`, and create a fresh empty `unreleased/`. + +### Released folders are frozen + +Once a version ships, its folder is never modified. + +### One detail file per logical change + +- Entries are grouped by surface: Core, Protocol Servers, CLI, SDK, Docs, Tooling, etc. +- A related change extends the existing file instead of opening a new one. +- File naming: `YYYY-MM-DD-short-slug.md` + +### RELEASE.md + +- Written at release time, before the git tag is created. +- Contents: lead sentence, then `## Install`, `## Highlights`, `## Notable in this release`, `## Requirements`. + +### Summary README.md per version + +- One brief line per change, each linking its detail file: + ``` + - [YYYY-MM-DD] Brief description. ([details](YYYY-MM-DD-short-slug.md)) + ``` + +### Backward compatibility + +A batch that carries backward-compatibility handling collects it in one `YYYY-MM-DD-backward-compatibility.md`. The surface entries reference that file instead of re-explaining it. + +### Language + +Written in English. History starts at v2.9.5; earlier changes are not backfilled. \ No newline at end of file From 23c27ea1c4e88935816f343ddcec8d935585abf1 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 17:47:54 +0800 Subject: [PATCH 16/23] feat: add backward-compatible agentpool import and CLI shims - Add 10 shim packages (src/agentpool*) that re-export from wolfharness* - Each shim emits DeprecationWarning guiding users to update imports - Add agentpool CLI alias in pyproject.toml (agentpool = wolfharness_cli) - Verified: import agentpool, import agentpool_cli, and agentpool --help all work with deprecation warnings --- pyproject.toml | 79 ++++++++++++++++-------------- ruff.toml | 10 +++- src/agentpool/__init__.py | 21 ++++++++ src/agentpool_bot/__init__.py | 18 +++++++ src/agentpool_cli/__init__.py | 18 +++++++ src/agentpool_commands/__init__.py | 18 +++++++ src/agentpool_config/__init__.py | 18 +++++++ src/agentpool_prompts/__init__.py | 18 +++++++ src/agentpool_server/__init__.py | 18 +++++++ src/agentpool_storage/__init__.py | 18 +++++++ src/agentpool_sync/__init__.py | 18 +++++++ src/agentpool_toolsets/__init__.py | 18 +++++++ 12 files changed, 233 insertions(+), 39 deletions(-) create mode 100644 src/agentpool/__init__.py create mode 100644 src/agentpool_bot/__init__.py create mode 100644 src/agentpool_cli/__init__.py create mode 100644 src/agentpool_commands/__init__.py create mode 100644 src/agentpool_config/__init__.py create mode 100644 src/agentpool_prompts/__init__.py create mode 100644 src/agentpool_server/__init__.py create mode 100644 src/agentpool_storage/__init__.py create mode 100644 src/agentpool_sync/__init__.py create mode 100644 src/agentpool_toolsets/__init__.py diff --git a/pyproject.toml b/pyproject.toml index 96deab6b2..e8db003e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,9 +93,19 @@ Source = "https://github.com/Leoyzen/wolfharness" [project.scripts] acp-bridge = "acp.bridge.__main__:main" +agentpool = "wolfharness_cli.__main__:cli" wolfharness = "wolfharness_cli.__main__:cli" wolfharness-db = "wolfharness_storage.sql_provider.cli:main" +[project.entry-points."fsspec.specs"] +acp = "acp.filesystem:ACPFileSystem" + +[project.entry-points."jinjarope.environment"] +wolfharness = "wolfharness.utils:setup_env" + +[project.entry-points."universal_pathlib.implementations"] +acp = "acp.filesystem:ACPPath" + [project.entry-points."wolfharness.capabilities"] mcp = "wolfharness.capabilities.mcp_server_cap:McpServerCap" function_tools = "wolfharness.capabilities.function_toolset:FunctionToolsetCapability" @@ -107,15 +117,6 @@ tool_display = "wolfharness.capabilities.tool_display_capability:ToolDisplayCapa question = "wolfharness.capabilities.question:QuestionCapability" background_task = "wolfharness.capabilities.background_task:BackgroundTaskCapability" -[project.entry-points."fsspec.specs"] -acp = "acp.filesystem:ACPFileSystem" - -[project.entry-points."jinjarope.environment"] -wolfharness = "wolfharness.utils:setup_env" - -[project.entry-points."universal_pathlib.implementations"] -acp = "acp.filesystem:ACPPath" - [project.optional-dependencies] a2a = ["fasta2a", "starlette"] # A2A Server bot = [ @@ -348,7 +349,11 @@ ignore_imports = [ name = "ACP package must not import from server" type = "forbidden" source_modules = ["acp"] -forbidden_modules = ["wolfharness_server", "wolfharness_commands", "wolfharness_cli"] +forbidden_modules = [ + "wolfharness_server", + "wolfharness_commands", + "wolfharness_cli" +] # No direct violations exist; all violations are indirect (acp -> wolfharness.utils # -> ... -> server). Indirect checks are disabled via allow_indirect_imports # until the downstream wolfharness.common_types -> wolfharness_server chain is fixed @@ -526,33 +531,33 @@ constraint-dependencies = ["extism-sys<1.13.0"] name = "mknodes" version = "2.2.14" requires-dist = [ - "wolfharness", - "anybadge", - "anyenv[httpx]", - "clinspector", - "coloraide", - "epregistry", - "fieldz", - "fsspec", - "git-changelog", - "githarbor", - "gitpython", - "griffelib", - "jinja2", - "jinjarope[icons]", - "mkdocstrings[python]", - "natsort", - "pipdeptree", - "pydantic", - "pytest-asyncio", - "python-dotenv", - "setuptools", - "tomli-w", - "typer", - "universal-pathlib", - "upathtools[httpx]", - "yamling", - "zensical", + "wolfharness", + "anybadge", + "anyenv[httpx]", + "clinspector", + "coloraide", + "epregistry", + "fieldz", + "fsspec", + "git-changelog", + "githarbor", + "gitpython", + "griffelib", + "jinja2", + "jinjarope[icons]", + "mkdocstrings[python]", + "natsort", + "pipdeptree", + "pydantic", + "pytest-asyncio", + "python-dotenv", + "setuptools", + "tomli-w", + "typer", + "universal-pathlib", + "upathtools[httpx]", + "yamling", + "zensical", ] [tool.uv.build-backend] diff --git a/ruff.toml b/ruff.toml index 591197d8d..11bc6030e 100644 --- a/ruff.toml +++ b/ruff.toml @@ -159,8 +159,14 @@ max-complexity = 15 "src/wolfharness/capabilities/resource_resolver.py" = ["PLR0911"] "src/wolfharness/capabilities/resource_capability.py" = ["PLR0911", "TRY300"] # Background task capability: PLR0915/PLR0911 for complex lifecycle/tool methods, TRY301 for re-raise pattern -"src/wolfharness/capabilities/background_task/capability.py" = ["PLR0915", "TRY301"] -"src/wolfharness/capabilities/background_task/manager.py" = ["PLR0915", "PLR0911"] +"src/wolfharness/capabilities/background_task/capability.py" = [ + "PLR0915", + "TRY301" +] +"src/wolfharness/capabilities/background_task/manager.py" = [ + "PLR0915", + "PLR0911" +] # Question tool tests: RUF001 — fullwidth CJK punctuation is intentional test data # for verifying CJK text parsing in the question_for_user XML "tests/tool_impls/question/test_question_for_user.py" = ["RUF001"] diff --git a/src/agentpool/__init__.py b/src/agentpool/__init__.py new file mode 100644 index 000000000..57ec1818d --- /dev/null +++ b/src/agentpool/__init__.py @@ -0,0 +1,21 @@ +"""agentpool — backward-compatible shim for wolfharness. + +This package has been renamed to ``wolfharness``. Importing ``agentpool`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings +from importlib.metadata import version + +from wolfharness import * # noqa: F403 + +warnings.warn( + "``agentpool`` has been renamed to ``wolfharness``. " + "Update your imports: ``from wolfharness import ...``", + DeprecationWarning, + stacklevel=2, +) + +__version__ = version("wolfharness") diff --git a/src/agentpool_bot/__init__.py b/src/agentpool_bot/__init__.py new file mode 100644 index 000000000..70990abbb --- /dev/null +++ b/src/agentpool_bot/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_bot — backward-compatible shim for wolfharness_bot. + +This package has been renamed to ``wolfharness_bot``. Importing ``agentpool_bot`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_bot import * # noqa: F403 + +warnings.warn( + "``agentpool_bot`` has been renamed to ``wolfharness_bot``. " + "Update your imports: ``from wolfharness_bot import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_cli/__init__.py b/src/agentpool_cli/__init__.py new file mode 100644 index 000000000..d69894c54 --- /dev/null +++ b/src/agentpool_cli/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_cli — backward-compatible shim for wolfharness_cli. + +This package has been renamed to ``wolfharness_cli``. Importing ``agentpool_cli`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_cli import * # noqa: F403 + +warnings.warn( + "``agentpool_cli`` has been renamed to ``wolfharness_cli``. " + "Update your imports: ``from wolfharness_cli import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_commands/__init__.py b/src/agentpool_commands/__init__.py new file mode 100644 index 000000000..143b15cad --- /dev/null +++ b/src/agentpool_commands/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_commands — backward-compatible shim for wolfharness_commands. + +This package has been renamed to ``wolfharness_commands``. Importing ``agentpool_commands`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_commands import * # noqa: F403 + +warnings.warn( + "``agentpool_commands`` has been renamed to ``wolfharness_commands``. " + "Update your imports: ``from wolfharness_commands import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_config/__init__.py b/src/agentpool_config/__init__.py new file mode 100644 index 000000000..92d17341d --- /dev/null +++ b/src/agentpool_config/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_config — backward-compatible shim for wolfharness_config. + +This package has been renamed to ``wolfharness_config``. Importing ``agentpool_config`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_config import * # noqa: F403 + +warnings.warn( + "``agentpool_config`` has been renamed to ``wolfharness_config``. " + "Update your imports: ``from wolfharness_config import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_prompts/__init__.py b/src/agentpool_prompts/__init__.py new file mode 100644 index 000000000..6d69e94c8 --- /dev/null +++ b/src/agentpool_prompts/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_prompts — backward-compatible shim for wolfharness_prompts. + +This package has been renamed to ``wolfharness_prompts``. Importing ``agentpool_prompts`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_prompts import * # noqa: F403 + +warnings.warn( + "``agentpool_prompts`` has been renamed to ``wolfharness_prompts``. " + "Update your imports: ``from wolfharness_prompts import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_server/__init__.py b/src/agentpool_server/__init__.py new file mode 100644 index 000000000..2b34460a6 --- /dev/null +++ b/src/agentpool_server/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_server — backward-compatible shim for wolfharness_server. + +This package has been renamed to ``wolfharness_server``. Importing ``agentpool_server`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_server import * # noqa: F403 + +warnings.warn( + "``agentpool_server`` has been renamed to ``wolfharness_server``. " + "Update your imports: ``from wolfharness_server import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_storage/__init__.py b/src/agentpool_storage/__init__.py new file mode 100644 index 000000000..d13a04609 --- /dev/null +++ b/src/agentpool_storage/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_storage — backward-compatible shim for wolfharness_storage. + +This package has been renamed to ``wolfharness_storage``. Importing ``agentpool_storage`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_storage import * # noqa: F403 + +warnings.warn( + "``agentpool_storage`` has been renamed to ``wolfharness_storage``. " + "Update your imports: ``from wolfharness_storage import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_sync/__init__.py b/src/agentpool_sync/__init__.py new file mode 100644 index 000000000..65b53fde4 --- /dev/null +++ b/src/agentpool_sync/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_sync — backward-compatible shim for wolfharness_sync. + +This package has been renamed to ``wolfharness_sync``. Importing ``agentpool_sync`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_sync import * # noqa: F403 + +warnings.warn( + "``agentpool_sync`` has been renamed to ``wolfharness_sync``. " + "Update your imports: ``from wolfharness_sync import ...``", + DeprecationWarning, + stacklevel=2, +) diff --git a/src/agentpool_toolsets/__init__.py b/src/agentpool_toolsets/__init__.py new file mode 100644 index 000000000..4347e0c9f --- /dev/null +++ b/src/agentpool_toolsets/__init__.py @@ -0,0 +1,18 @@ +"""agentpool_toolsets — backward-compatible shim for wolfharness_toolsets. + +This package has been renamed to ``wolfharness_toolsets``. Importing ``agentpool_toolsets`` +is deprecated and will be removed in a future release. +""" + +from __future__ import annotations + +import warnings + +from wolfharness_toolsets import * # noqa: F403 + +warnings.warn( + "``agentpool_toolsets`` has been renamed to ``wolfharness_toolsets``. " + "Update your imports: ``from wolfharness_toolsets import ...``", + DeprecationWarning, + stacklevel=2, +) From 4432492a7f958e073fadf2394d831026df9e9574 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 17:52:55 +0800 Subject: [PATCH 17/23] docs: add migration guide from AgentPool to WolfHarness in README --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 81c25647f..33299af7c 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,23 @@ If you use WolfHarness in your research or project, please cite: } ``` +## Migrating from AgentPool + +This project was renamed from **AgentPool** to **WolfHarness** (v2.10+). Backward-compatible shims are in place to ease the transition: + +| Old | New | Status | +|-----|-----|--------| +| `import agentpool` | `import wolfharness` | ✅ Shim with deprecation warning | +| `import agentpool_cli` | `import wolfharness_cli` | ✅ Shim with deprecation warning | +| `import agentpool_config` | `import wolfharness_config` | ✅ Shim with deprecation warning | +| `import agentpool_server` | `import wolfharness_server` | ✅ Shim with deprecation warning | +| `import agentpool_storage` | `import wolfharness_storage` | ✅ Shim with deprecation warning | +| `import agentpool_toolsets` | `import wolfharness_toolsets` | ✅ Shim with deprecation warning | +| `agentpool run ...` | `wolfharness run ...` | ✅ CLI alias with deprecation warning | +| `AGENTPOOL_CONFIG_DIR` env var | `WOLFHARNESS_CONFIG_DIR` | ⚠️ Still supported, migrate when convenient | + +The shims emit a `DeprecationWarning` and will be removed in a future release. Please update your imports and scripts accordingly. + ## License MIT — see [LICENSE](LICENSE). From bf89c41d2e4a7146bb202ed64cff1644ed370803 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 18:22:44 +0800 Subject: [PATCH 18/23] =?UTF-8?q?fix:=20update=20test=20schema=20path=20af?= =?UTF-8?q?ter=20config/tools=20=E2=86=92=20schemas/=20move?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_background_task_capability.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/capabilities/background_task/test_background_task_capability.py b/tests/capabilities/background_task/test_background_task_capability.py index 78f52edb6..bdb45ff58 100644 --- a/tests/capabilities/background_task/test_background_task_capability.py +++ b/tests/capabilities/background_task/test_background_task_capability.py @@ -18,8 +18,15 @@ from wolfharness.capabilities.background_task.capability import ForceRetrievalMode -CONFIG_DIR = Path(__file__).resolve().parents[3] / "config" -TASK_SCHEMA_PATH = str(CONFIG_DIR / "tools" / "task.yaml") +SCHEMAS_DIR = ( + Path(__file__).resolve().parents[3] + / "src" + / "wolfharness" + / "capabilities" + / "background_task" + / "schemas" +) +TASK_SCHEMA_PATH = str(SCHEMAS_DIR / "task.yaml") @runtime_checkable From f6bbab9ce1f07a9dce438e51008da013f58ffe6f Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 19:12:32 +0800 Subject: [PATCH 19/23] fix: suppress ToolResult return schema warning in QuestionCapability --- src/wolfharness/capabilities/question.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/wolfharness/capabilities/question.py b/src/wolfharness/capabilities/question.py index 3fac9b177..6945d362f 100644 --- a/src/wolfharness/capabilities/question.py +++ b/src/wolfharness/capabilities/question.py @@ -14,6 +14,7 @@ from dataclasses import replace from pathlib import Path from typing import TYPE_CHECKING +import warnings import logfire from pydantic_ai import RunContext, Tool @@ -130,12 +131,20 @@ def get_toolset(self) -> AgentToolset[AgentContext] | None: description = ( self._question_schema.get("description") if self._question_schema else None ) or "Ask the user one or more structured questions." - tool = Tool( - self._question, - name=name, - description=description, - metadata={"category": "other"}, - ) + # pydantic_ai can't schema ToolResult (plain dataclass); runtime + # conversion in _ToolInterceptCapability.wrap_tool_execute handles it. + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + category=UserWarning, + message=r"Could not generate return schema for .+", + ) + tool = Tool( + self._question, + name=name, + description=description, + metadata={"category": "other"}, + ) tools.append(apply_params_schema(tool, self._question_schema)) if not tools: From 2f2e30161b089ecf6a62f4979651562f2ee09767 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 19:32:15 +0800 Subject: [PATCH 20/23] fix: shim packages now forward submodule imports via find_spec meta_path finder Previous shims only re-exported top-level names (from wolfharness import *). Submodule imports like 'from agentpool.agents.context import AgentContext' failed with ModuleNotFoundError because Python 3.13 removed the legacy find_module/load_module protocol. Now uses find_spec (PEP 451) to intercept agentpool.X.Y imports and redirect to wolfharness.X.Y transparently. --- src/agentpool/__init__.py | 39 +++++++++++++++++++++++++++--- src/agentpool_bot/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_cli/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_commands/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_config/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_prompts/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_server/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_storage/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_sync/__init__.py | 38 ++++++++++++++++++++++++++++- src/agentpool_toolsets/__init__.py | 38 ++++++++++++++++++++++++++++- 10 files changed, 369 insertions(+), 12 deletions(-) diff --git a/src/agentpool/__init__.py b/src/agentpool/__init__.py index 57ec1818d..fc58d991b 100644 --- a/src/agentpool/__init__.py +++ b/src/agentpool/__init__.py @@ -2,20 +2,53 @@ This package has been renamed to ``wolfharness``. Importing ``agentpool`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings -from importlib.metadata import version +from importlib import abc, machinery from wolfharness import * # noqa: F403 warnings.warn( "``agentpool`` has been renamed to ``wolfharness``. " - "Update your imports: ``from wolfharness import ...``", + "Update your imports: `from wolfharness import ...`", DeprecationWarning, stacklevel=2, ) -__version__ = version("wolfharness") + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness.""" + import wolfharness + + return getattr(wolfharness, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool.X submodule imports to wolfharness.X.""" + + _prefix = "agentpool." + _target = "wolfharness" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_bot/__init__.py b/src/agentpool_bot/__init__.py index 70990abbb..3848afadf 100644 --- a/src/agentpool_bot/__init__.py +++ b/src/agentpool_bot/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_bot``. Importing ``agentpool_bot`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_bot`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_bot import * # noqa: F403 warnings.warn( "``agentpool_bot`` has been renamed to ``wolfharness_bot``. " - "Update your imports: ``from wolfharness_bot import ...``", + "Update your imports: `from wolfharness_bot import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_bot.""" + import wolfharness_bot + + return getattr(wolfharness_bot, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_bot.X submodule imports to wolfharness_bot.X.""" + + _prefix = "agentpool_bot." + _target = "wolfharness_bot" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_cli/__init__.py b/src/agentpool_cli/__init__.py index d69894c54..09adbfca7 100644 --- a/src/agentpool_cli/__init__.py +++ b/src/agentpool_cli/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_cli``. Importing ``agentpool_cli`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_cli`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_cli import * # noqa: F403 warnings.warn( "``agentpool_cli`` has been renamed to ``wolfharness_cli``. " - "Update your imports: ``from wolfharness_cli import ...``", + "Update your imports: `from wolfharness_cli import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_cli.""" + import wolfharness_cli + + return getattr(wolfharness_cli, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_cli.X submodule imports to wolfharness_cli.X.""" + + _prefix = "agentpool_cli." + _target = "wolfharness_cli" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_commands/__init__.py b/src/agentpool_commands/__init__.py index 143b15cad..97c919039 100644 --- a/src/agentpool_commands/__init__.py +++ b/src/agentpool_commands/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_commands``. Importing ``agentpool_commands`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_commands`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_commands import * # noqa: F403 warnings.warn( "``agentpool_commands`` has been renamed to ``wolfharness_commands``. " - "Update your imports: ``from wolfharness_commands import ...``", + "Update your imports: `from wolfharness_commands import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_commands.""" + import wolfharness_commands + + return getattr(wolfharness_commands, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_commands.X submodule imports to wolfharness_commands.X.""" + + _prefix = "agentpool_commands." + _target = "wolfharness_commands" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_config/__init__.py b/src/agentpool_config/__init__.py index 92d17341d..17700942f 100644 --- a/src/agentpool_config/__init__.py +++ b/src/agentpool_config/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_config``. Importing ``agentpool_config`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_config`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_config import * # noqa: F403 warnings.warn( "``agentpool_config`` has been renamed to ``wolfharness_config``. " - "Update your imports: ``from wolfharness_config import ...``", + "Update your imports: `from wolfharness_config import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_config.""" + import wolfharness_config + + return getattr(wolfharness_config, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_config.X submodule imports to wolfharness_config.X.""" + + _prefix = "agentpool_config." + _target = "wolfharness_config" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_prompts/__init__.py b/src/agentpool_prompts/__init__.py index 6d69e94c8..15140ec71 100644 --- a/src/agentpool_prompts/__init__.py +++ b/src/agentpool_prompts/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_prompts``. Importing ``agentpool_prompts`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_prompts`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_prompts import * # noqa: F403 warnings.warn( "``agentpool_prompts`` has been renamed to ``wolfharness_prompts``. " - "Update your imports: ``from wolfharness_prompts import ...``", + "Update your imports: `from wolfharness_prompts import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_prompts.""" + import wolfharness_prompts + + return getattr(wolfharness_prompts, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_prompts.X submodule imports to wolfharness_prompts.X.""" + + _prefix = "agentpool_prompts." + _target = "wolfharness_prompts" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_server/__init__.py b/src/agentpool_server/__init__.py index 2b34460a6..246990b8d 100644 --- a/src/agentpool_server/__init__.py +++ b/src/agentpool_server/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_server``. Importing ``agentpool_server`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_server`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_server import * # noqa: F403 warnings.warn( "``agentpool_server`` has been renamed to ``wolfharness_server``. " - "Update your imports: ``from wolfharness_server import ...``", + "Update your imports: `from wolfharness_server import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_server.""" + import wolfharness_server + + return getattr(wolfharness_server, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_server.X submodule imports to wolfharness_server.X.""" + + _prefix = "agentpool_server." + _target = "wolfharness_server" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_storage/__init__.py b/src/agentpool_storage/__init__.py index d13a04609..0cb7c13cb 100644 --- a/src/agentpool_storage/__init__.py +++ b/src/agentpool_storage/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_storage``. Importing ``agentpool_storage`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_storage`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_storage import * # noqa: F403 warnings.warn( "``agentpool_storage`` has been renamed to ``wolfharness_storage``. " - "Update your imports: ``from wolfharness_storage import ...``", + "Update your imports: `from wolfharness_storage import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_storage.""" + import wolfharness_storage + + return getattr(wolfharness_storage, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_storage.X submodule imports to wolfharness_storage.X.""" + + _prefix = "agentpool_storage." + _target = "wolfharness_storage" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_sync/__init__.py b/src/agentpool_sync/__init__.py index 65b53fde4..e20acf227 100644 --- a/src/agentpool_sync/__init__.py +++ b/src/agentpool_sync/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_sync``. Importing ``agentpool_sync`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_sync`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_sync import * # noqa: F403 warnings.warn( "``agentpool_sync`` has been renamed to ``wolfharness_sync``. " - "Update your imports: ``from wolfharness_sync import ...``", + "Update your imports: `from wolfharness_sync import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_sync.""" + import wolfharness_sync + + return getattr(wolfharness_sync, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_sync.X submodule imports to wolfharness_sync.X.""" + + _prefix = "agentpool_sync." + _target = "wolfharness_sync" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) diff --git a/src/agentpool_toolsets/__init__.py b/src/agentpool_toolsets/__init__.py index 4347e0c9f..a4f015ca1 100644 --- a/src/agentpool_toolsets/__init__.py +++ b/src/agentpool_toolsets/__init__.py @@ -2,17 +2,53 @@ This package has been renamed to ``wolfharness_toolsets``. Importing ``agentpool_toolsets`` is deprecated and will be removed in a future release. + +Submodule imports are forwarded to the corresponding ``wolfharness_toolsets`` submodule. """ from __future__ import annotations +import importlib +import sys import warnings +from importlib import abc, machinery from wolfharness_toolsets import * # noqa: F403 warnings.warn( "``agentpool_toolsets`` has been renamed to ``wolfharness_toolsets``. " - "Update your imports: ``from wolfharness_toolsets import ...``", + "Update your imports: `from wolfharness_toolsets import ...`", DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name: str): + """Forward top-level attribute access to wolfharness_toolsets.""" + import wolfharness_toolsets + + return getattr(wolfharness_toolsets, name) + + +class _ShimFinder(abc.MetaPathFinder): + """Redirect agentpool_toolsets.X submodule imports to wolfharness_toolsets.X.""" + + _prefix = "agentpool_toolsets." + _target = "wolfharness_toolsets" + + def find_spec(self, fullname: str, path: object = None, target: object = None): + if not fullname.startswith(self._prefix): + return None + + wolf_name = self._target + fullname[len(self._prefix) - 1 :] + try: + wolf_mod = importlib.import_module(wolf_name) + except ModuleNotFoundError: + return None + + sys.modules[fullname] = wolf_mod + return machinery.ModuleSpec(fullname, loader=None) + + +if not any(isinstance(f, _ShimFinder) for f in sys.meta_path): + sys.meta_path.insert(0, _ShimFinder()) From ce131eece6d5cbf0af4f7aa4c1b7343228472214 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 19:42:38 +0800 Subject: [PATCH 21/23] feat: add compat/agentpool compatibility package for downstream projects Downstream projects that still use 'agentpool' as dependency name can point their [tool.uv.sources] agentpool to compat/ instead of the root. The compat package has name='agentpool', depends on wolfharness, and re-exports all shim modules (agentpool, agentpool_config, etc.) via symlinks to src/. --- compat/pyproject.toml | 38 +++++++++++++++++++++++++++++++++++ compat/src/agentpool | 1 + compat/src/agentpool_bot | 1 + compat/src/agentpool_cli | 1 + compat/src/agentpool_commands | 1 + compat/src/agentpool_config | 1 + compat/src/agentpool_prompts | 1 + compat/src/agentpool_server | 1 + compat/src/agentpool_storage | 1 + compat/src/agentpool_sync | 1 + compat/src/agentpool_toolsets | 1 + 11 files changed, 48 insertions(+) create mode 100644 compat/pyproject.toml create mode 120000 compat/src/agentpool create mode 120000 compat/src/agentpool_bot create mode 120000 compat/src/agentpool_cli create mode 120000 compat/src/agentpool_commands create mode 120000 compat/src/agentpool_config create mode 120000 compat/src/agentpool_prompts create mode 120000 compat/src/agentpool_server create mode 120000 compat/src/agentpool_storage create mode 120000 compat/src/agentpool_sync create mode 120000 compat/src/agentpool_toolsets diff --git a/compat/pyproject.toml b/compat/pyproject.toml new file mode 100644 index 000000000..ef597f6a7 --- /dev/null +++ b/compat/pyproject.toml @@ -0,0 +1,38 @@ +[project] +name = "agentpool" +version = "2.9.5" +description = "Backward-compatible compatibility package — delegates to wolfharness" +requires-python = ">=3.13,<3.14" +license = "MIT" +dependencies = [ + "wolfharness==2.9.5", +] + +[project.optional-dependencies] +coding = ["wolfharness[coding]==2.9.5"] +zed = ["wolfharness[zed]==2.9.5"] + +[project.scripts] +agentpool = "wolfharness_cli.__main__:cli" +wolfharness = "wolfharness_cli.__main__:cli" + +[build-system] +requires = ["uv_build>=0.8.22"] +build-backend = "uv_build" + +[tool.uv.build-backend] +module-name = [ + "agentpool", + "agentpool_bot", + "agentpool_cli", + "agentpool_commands", + "agentpool_config", + "agentpool_prompts", + "agentpool_server", + "agentpool_storage", + "agentpool_sync", + "agentpool_toolsets", +] + +[tool.uv.sources] +wolfharness = { path = "..", editable = true } diff --git a/compat/src/agentpool b/compat/src/agentpool new file mode 120000 index 000000000..4df6284c1 --- /dev/null +++ b/compat/src/agentpool @@ -0,0 +1 @@ +../../src/agentpool \ No newline at end of file diff --git a/compat/src/agentpool_bot b/compat/src/agentpool_bot new file mode 120000 index 000000000..5283412eb --- /dev/null +++ b/compat/src/agentpool_bot @@ -0,0 +1 @@ +../../src/agentpool_bot \ No newline at end of file diff --git a/compat/src/agentpool_cli b/compat/src/agentpool_cli new file mode 120000 index 000000000..d1294acf1 --- /dev/null +++ b/compat/src/agentpool_cli @@ -0,0 +1 @@ +../../src/agentpool_cli \ No newline at end of file diff --git a/compat/src/agentpool_commands b/compat/src/agentpool_commands new file mode 120000 index 000000000..b1bcd1709 --- /dev/null +++ b/compat/src/agentpool_commands @@ -0,0 +1 @@ +../../src/agentpool_commands \ No newline at end of file diff --git a/compat/src/agentpool_config b/compat/src/agentpool_config new file mode 120000 index 000000000..66ceb7c0a --- /dev/null +++ b/compat/src/agentpool_config @@ -0,0 +1 @@ +../../src/agentpool_config \ No newline at end of file diff --git a/compat/src/agentpool_prompts b/compat/src/agentpool_prompts new file mode 120000 index 000000000..ffd88f3be --- /dev/null +++ b/compat/src/agentpool_prompts @@ -0,0 +1 @@ +../../src/agentpool_prompts \ No newline at end of file diff --git a/compat/src/agentpool_server b/compat/src/agentpool_server new file mode 120000 index 000000000..9ffb12861 --- /dev/null +++ b/compat/src/agentpool_server @@ -0,0 +1 @@ +../../src/agentpool_server \ No newline at end of file diff --git a/compat/src/agentpool_storage b/compat/src/agentpool_storage new file mode 120000 index 000000000..f8772bfd1 --- /dev/null +++ b/compat/src/agentpool_storage @@ -0,0 +1 @@ +../../src/agentpool_storage \ No newline at end of file diff --git a/compat/src/agentpool_sync b/compat/src/agentpool_sync new file mode 120000 index 000000000..066d86a15 --- /dev/null +++ b/compat/src/agentpool_sync @@ -0,0 +1 @@ +../../src/agentpool_sync \ No newline at end of file diff --git a/compat/src/agentpool_toolsets b/compat/src/agentpool_toolsets new file mode 120000 index 000000000..52dbb1eda --- /dev/null +++ b/compat/src/agentpool_toolsets @@ -0,0 +1 @@ +../../src/agentpool_toolsets \ No newline at end of file From 0d41b5adf0ac97fa8ca6c0005cae83aeb18365af Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 19:56:21 +0800 Subject: [PATCH 22/23] =?UTF-8?q?revert:=20remove=20compat/=20directory=20?= =?UTF-8?q?=E2=80=94=20keep=20pyproject.toml=20name=20as=20wolfharness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compat/pyproject.toml | 38 ----------------------------------- compat/src/agentpool | 1 - compat/src/agentpool_bot | 1 - compat/src/agentpool_cli | 1 - compat/src/agentpool_commands | 1 - compat/src/agentpool_config | 1 - compat/src/agentpool_prompts | 1 - compat/src/agentpool_server | 1 - compat/src/agentpool_storage | 1 - compat/src/agentpool_sync | 1 - compat/src/agentpool_toolsets | 1 - 11 files changed, 48 deletions(-) delete mode 100644 compat/pyproject.toml delete mode 120000 compat/src/agentpool delete mode 120000 compat/src/agentpool_bot delete mode 120000 compat/src/agentpool_cli delete mode 120000 compat/src/agentpool_commands delete mode 120000 compat/src/agentpool_config delete mode 120000 compat/src/agentpool_prompts delete mode 120000 compat/src/agentpool_server delete mode 120000 compat/src/agentpool_storage delete mode 120000 compat/src/agentpool_sync delete mode 120000 compat/src/agentpool_toolsets diff --git a/compat/pyproject.toml b/compat/pyproject.toml deleted file mode 100644 index ef597f6a7..000000000 --- a/compat/pyproject.toml +++ /dev/null @@ -1,38 +0,0 @@ -[project] -name = "agentpool" -version = "2.9.5" -description = "Backward-compatible compatibility package — delegates to wolfharness" -requires-python = ">=3.13,<3.14" -license = "MIT" -dependencies = [ - "wolfharness==2.9.5", -] - -[project.optional-dependencies] -coding = ["wolfharness[coding]==2.9.5"] -zed = ["wolfharness[zed]==2.9.5"] - -[project.scripts] -agentpool = "wolfharness_cli.__main__:cli" -wolfharness = "wolfharness_cli.__main__:cli" - -[build-system] -requires = ["uv_build>=0.8.22"] -build-backend = "uv_build" - -[tool.uv.build-backend] -module-name = [ - "agentpool", - "agentpool_bot", - "agentpool_cli", - "agentpool_commands", - "agentpool_config", - "agentpool_prompts", - "agentpool_server", - "agentpool_storage", - "agentpool_sync", - "agentpool_toolsets", -] - -[tool.uv.sources] -wolfharness = { path = "..", editable = true } diff --git a/compat/src/agentpool b/compat/src/agentpool deleted file mode 120000 index 4df6284c1..000000000 --- a/compat/src/agentpool +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool \ No newline at end of file diff --git a/compat/src/agentpool_bot b/compat/src/agentpool_bot deleted file mode 120000 index 5283412eb..000000000 --- a/compat/src/agentpool_bot +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_bot \ No newline at end of file diff --git a/compat/src/agentpool_cli b/compat/src/agentpool_cli deleted file mode 120000 index d1294acf1..000000000 --- a/compat/src/agentpool_cli +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_cli \ No newline at end of file diff --git a/compat/src/agentpool_commands b/compat/src/agentpool_commands deleted file mode 120000 index b1bcd1709..000000000 --- a/compat/src/agentpool_commands +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_commands \ No newline at end of file diff --git a/compat/src/agentpool_config b/compat/src/agentpool_config deleted file mode 120000 index 66ceb7c0a..000000000 --- a/compat/src/agentpool_config +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_config \ No newline at end of file diff --git a/compat/src/agentpool_prompts b/compat/src/agentpool_prompts deleted file mode 120000 index ffd88f3be..000000000 --- a/compat/src/agentpool_prompts +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_prompts \ No newline at end of file diff --git a/compat/src/agentpool_server b/compat/src/agentpool_server deleted file mode 120000 index 9ffb12861..000000000 --- a/compat/src/agentpool_server +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_server \ No newline at end of file diff --git a/compat/src/agentpool_storage b/compat/src/agentpool_storage deleted file mode 120000 index f8772bfd1..000000000 --- a/compat/src/agentpool_storage +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_storage \ No newline at end of file diff --git a/compat/src/agentpool_sync b/compat/src/agentpool_sync deleted file mode 120000 index 066d86a15..000000000 --- a/compat/src/agentpool_sync +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_sync \ No newline at end of file diff --git a/compat/src/agentpool_toolsets b/compat/src/agentpool_toolsets deleted file mode 120000 index 52dbb1eda..000000000 --- a/compat/src/agentpool_toolsets +++ /dev/null @@ -1 +0,0 @@ -../../src/agentpool_toolsets \ No newline at end of file From e15eaf6fb1cd6499bebc73fbf5eb1015d4bf9572 Mon Sep 17 00:00:00 2001 From: Million <15158090088@163.com> Date: Fri, 7 Aug 2026 20:01:26 +0800 Subject: [PATCH 23/23] fix: add return type annotations to shim packages for mypy strict mode --- src/agentpool/__init__.py | 6 ++++-- src/agentpool_bot/__init__.py | 6 ++++-- src/agentpool_cli/__init__.py | 6 ++++-- src/agentpool_commands/__init__.py | 6 ++++-- src/agentpool_config/__init__.py | 6 ++++-- src/agentpool_prompts/__init__.py | 6 ++++-- src/agentpool_server/__init__.py | 6 ++++-- src/agentpool_storage/__init__.py | 6 ++++-- src/agentpool_sync/__init__.py | 6 ++++-- src/agentpool_toolsets/__init__.py | 6 ++++-- 10 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/agentpool/__init__.py b/src/agentpool/__init__.py index fc58d991b..43b573547 100644 --- a/src/agentpool/__init__.py +++ b/src/agentpool/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness.""" import wolfharness @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool." _target = "wolfharness" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_bot/__init__.py b/src/agentpool_bot/__init__.py index 3848afadf..27ae64ed1 100644 --- a/src/agentpool_bot/__init__.py +++ b/src/agentpool_bot/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_bot.""" import wolfharness_bot @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_bot." _target = "wolfharness_bot" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_cli/__init__.py b/src/agentpool_cli/__init__.py index 09adbfca7..37c8f9998 100644 --- a/src/agentpool_cli/__init__.py +++ b/src/agentpool_cli/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_cli.""" import wolfharness_cli @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_cli." _target = "wolfharness_cli" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_commands/__init__.py b/src/agentpool_commands/__init__.py index 97c919039..e17d1424b 100644 --- a/src/agentpool_commands/__init__.py +++ b/src/agentpool_commands/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_commands.""" import wolfharness_commands @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_commands." _target = "wolfharness_commands" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_config/__init__.py b/src/agentpool_config/__init__.py index 17700942f..f88aee81f 100644 --- a/src/agentpool_config/__init__.py +++ b/src/agentpool_config/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_config.""" import wolfharness_config @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_config." _target = "wolfharness_config" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_prompts/__init__.py b/src/agentpool_prompts/__init__.py index 15140ec71..6a8be8ec3 100644 --- a/src/agentpool_prompts/__init__.py +++ b/src/agentpool_prompts/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_prompts.""" import wolfharness_prompts @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_prompts." _target = "wolfharness_prompts" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_server/__init__.py b/src/agentpool_server/__init__.py index 246990b8d..c92921705 100644 --- a/src/agentpool_server/__init__.py +++ b/src/agentpool_server/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_server.""" import wolfharness_server @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_server." _target = "wolfharness_server" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_storage/__init__.py b/src/agentpool_storage/__init__.py index 0cb7c13cb..f6d777a7e 100644 --- a/src/agentpool_storage/__init__.py +++ b/src/agentpool_storage/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_storage.""" import wolfharness_storage @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_storage." _target = "wolfharness_storage" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_sync/__init__.py b/src/agentpool_sync/__init__.py index e20acf227..e5b037eff 100644 --- a/src/agentpool_sync/__init__.py +++ b/src/agentpool_sync/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_sync.""" import wolfharness_sync @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_sync." _target = "wolfharness_sync" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None diff --git a/src/agentpool_toolsets/__init__.py b/src/agentpool_toolsets/__init__.py index a4f015ca1..837eb1137 100644 --- a/src/agentpool_toolsets/__init__.py +++ b/src/agentpool_toolsets/__init__.py @@ -23,7 +23,7 @@ ) -def __getattr__(name: str): +def __getattr__(name: str) -> object: """Forward top-level attribute access to wolfharness_toolsets.""" import wolfharness_toolsets @@ -36,7 +36,9 @@ class _ShimFinder(abc.MetaPathFinder): _prefix = "agentpool_toolsets." _target = "wolfharness_toolsets" - def find_spec(self, fullname: str, path: object = None, target: object = None): + def find_spec( + self, fullname: str, path: object = None, target: object = None + ) -> machinery.ModuleSpec | None: if not fullname.startswith(self._prefix): return None

  2. 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

    u->R!+=(x4 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≻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 z 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