polyrob 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agents/README.md +453 -0
- agents/__init__.py +164 -0
- agents/base_agent.py +743 -0
- agents/personality/character.py +119 -0
- agents/personality/character_manager.py +309 -0
- agents/personality/characters/rob.character.json +56 -0
- agents/personality/characters/trump.character.json +63 -0
- agents/personality/persona_render.py +78 -0
- agents/personality/persona_resolver.py +115 -0
- agents/prompt/__init__.py +58 -0
- agents/prompt/base_prompt.py +92 -0
- agents/prompt/system.py +417 -0
- agents/task/README_PATH_MANAGEMENT.md +141 -0
- agents/task/__init__.py +162 -0
- agents/task/agent/__init__.py +70 -0
- agents/task/agent/agent_state.py +408 -0
- agents/task/agent/async_delegation.py +274 -0
- agents/task/agent/autonomy_state.py +275 -0
- agents/task/agent/conversation.py +75 -0
- agents/task/agent/core/__init__.py +6 -0
- agents/task/agent/core/aux_metering.py +61 -0
- agents/task/agent/core/background_review.py +157 -0
- agents/task/agent/core/construction.py +1179 -0
- agents/task/agent/core/conversational_exit.py +49 -0
- agents/task/agent/core/correspondent_gate.py +227 -0
- agents/task/agent/core/curator.py +246 -0
- agents/task/agent/core/episodic_digest.py +103 -0
- agents/task/agent/core/error_recovery.py +480 -0
- agents/task/agent/core/history_io.py +257 -0
- agents/task/agent/core/llm_provisioning.py +287 -0
- agents/task/agent/core/llm_runner.py +547 -0
- agents/task/agent/core/logging_io.py +276 -0
- agents/task/agent/core/loop_detection.py +262 -0
- agents/task/agent/core/memory_prefetch.py +325 -0
- agents/task/agent/core/memory_writer.py +667 -0
- agents/task/agent/core/model_introspection.py +82 -0
- agents/task/agent/core/model_swap.py +177 -0
- agents/task/agent/core/next_action_internal.py +1331 -0
- agents/task/agent/core/output_validation.py +303 -0
- agents/task/agent/core/project_context.py +272 -0
- agents/task/agent/core/resources.py +93 -0
- agents/task/agent/core/result_processing.py +389 -0
- agents/task/agent/core/run_loop.py +661 -0
- agents/task/agent/core/safety_lifecycle.py +228 -0
- agents/task/agent/core/secret_guard.py +293 -0
- agents/task/agent/core/self_wake.py +280 -0
- agents/task/agent/core/session_metadata.py +104 -0
- agents/task/agent/core/step.py +781 -0
- agents/task/agent/core/step_execution.py +398 -0
- agents/task/agent/core/step_telemetry.py +222 -0
- agents/task/agent/core/test_aux_metering.py +50 -0
- agents/task/agent/core/test_billing_failover.py +68 -0
- agents/task/agent/core/test_fatal_error_classifier.py +38 -0
- agents/task/agent/core/test_judge_validation.py +275 -0
- agents/task/agent/core/test_model_swap.py +190 -0
- agents/task/agent/core/test_reflection_offload.py +54 -0
- agents/task/agent/core/turn_input.py +8 -0
- agents/task/agent/core/untrusted_wrap.py +103 -0
- agents/task/agent/core/user_ingress.py +228 -0
- agents/task/agent/hitl_manager.py +396 -0
- agents/task/agent/log_sanitize.py +36 -0
- agents/task/agent/message_manager/config.py +97 -0
- agents/task/agent/message_manager/service.py +758 -0
- agents/task/agent/message_manager/tests.py +305 -0
- agents/task/agent/message_manager/tool_call_builder.py +539 -0
- agents/task/agent/message_manager/tool_message_repair.py +364 -0
- agents/task/agent/message_manager/views.py +143 -0
- agents/task/agent/messages/__init__.py +8 -0
- agents/task/agent/messages/builders.py +309 -0
- agents/task/agent/messages/compactor.py +611 -0
- agents/task/agent/messages/context_references.py +271 -0
- agents/task/agent/messages/filters.py +333 -0
- agents/task/agent/messages/guidance.py +328 -0
- agents/task/agent/messages/persistence.py +448 -0
- agents/task/agent/messages/retrieval.py +506 -0
- agents/task/agent/messages/sqlite_persistence.py +85 -0
- agents/task/agent/messages/test_compaction_upgrade.py +349 -0
- agents/task/agent/messages/test_persistence_subdir_fix.py +28 -0
- agents/task/agent/messages/test_sqlite_persistence.py +54 -0
- agents/task/agent/messages/token_counter.py +532 -0
- agents/task/agent/orchestrator.py +776 -0
- agents/task/agent/profile_manager.py +105 -0
- agents/task/agent/profile_registry.py +173 -0
- agents/task/agent/prompts.py +1090 -0
- agents/task/agent/scenario_registry.py +226 -0
- agents/task/agent/service.py +509 -0
- agents/task/agent/session.py +984 -0
- agents/task/agent/skill_discovery.py +110 -0
- agents/task/agent/skill_frontmatter.py +94 -0
- agents/task/agent/skill_manager.py +1201 -0
- agents/task/agent/skill_store.py +627 -0
- agents/task/agent/skill_validation.py +56 -0
- agents/task/agent/skill_writer.py +492 -0
- agents/task/agent/sub_agent_manager.py +1137 -0
- agents/task/agent/test_session_disk_recovery.py +34 -0
- agents/task/agent/tests.py +513 -0
- agents/task/agent/tool_call_tracker.py +511 -0
- agents/task/agent/views.py +469 -0
- agents/task/config.py +432 -0
- agents/task/constants.py +1384 -0
- agents/task/flag_defaults.py +63 -0
- agents/task/goals/__init__.py +1 -0
- agents/task/goals/autonomy_marker.py +28 -0
- agents/task/goals/board.py +690 -0
- agents/task/goals/completion_judge.py +247 -0
- agents/task/goals/context.py +62 -0
- agents/task/goals/dispatcher.py +848 -0
- agents/task/goals/escalation.py +102 -0
- agents/task/goals/planner.py +114 -0
- agents/task/logging_config.py +155 -0
- agents/task/path.py +991 -0
- agents/task/robust_parse_config.py +400 -0
- agents/task/runtime/__init__.py +0 -0
- agents/task/runtime/run_as_session.py +130 -0
- agents/task/runtime_safety.py +308 -0
- agents/task/session/__init__.py +5 -0
- agents/task/session/browser_pool.py +130 -0
- agents/task/session/cleanup.py +612 -0
- agents/task/session/execution.py +397 -0
- agents/task/session/feed.py +200 -0
- agents/task/session/hitl_ingress.py +201 -0
- agents/task/session/hooks.py +83 -0
- agents/task/session/multi_agent.py +243 -0
- agents/task/session/workspace.py +91 -0
- agents/task/session_registry.py +91 -0
- agents/task/session_route.py +35 -0
- agents/task/sqlite_session_registry.py +261 -0
- agents/task/surface_config.py +185 -0
- agents/task/telemetry/__init__.py +58 -0
- agents/task/telemetry/event_log.py +180 -0
- agents/task/telemetry/formatters.py +642 -0
- agents/task/telemetry/manager.py +571 -0
- agents/task/telemetry/memory_events.py +66 -0
- agents/task/telemetry/self_events.py +53 -0
- agents/task/telemetry/sequence.py +160 -0
- agents/task/telemetry/service.py +1265 -0
- agents/task/telemetry/views.py +882 -0
- agents/task/templates.py +98 -0
- agents/task/test_aux_chain.py +151 -0
- agents/task/test_aux_router.py +41 -0
- agents/task/tests/test_parse_robustness.py +170 -0
- agents/task/tool_defaults.py +110 -0
- agents/task/utils.py +762 -0
- agents/task/utils_json.py +768 -0
- agents/task/utils_webview.py +86 -0
- agents/task/workspace_context.py +229 -0
- agents/task_agent_lite.py +2398 -0
- api/README.md +683 -0
- api/TASK_API_DOCS.md +151 -0
- api/__init__.py +0 -0
- api/a2a/__init__.py +37 -0
- api/a2a/agent_card.py +337 -0
- api/a2a/client.py +657 -0
- api/a2a/endpoints.py +375 -0
- api/a2a/models.py +268 -0
- api/a2a/streaming.py +419 -0
- api/a2a/task_handler.py +929 -0
- api/admin_endpoints.py +1584 -0
- api/app.py +923 -0
- api/auth_constants.py +28 -0
- api/auth_endpoints.py +402 -0
- api/auth_state.py +36 -0
- api/chat_via_task.py +42 -0
- api/conversation_manager.py +231 -0
- api/dependencies.py +186 -0
- api/eip8004_endpoints.py +452 -0
- api/hyperliquid_models.py +174 -0
- api/hyperliquid_routes.py +450 -0
- api/interfaces.py +207 -0
- api/jwt_middleware.py +83 -0
- api/kb/__init__.py +1 -0
- api/kb/endpoints.py +314 -0
- api/mcp_models.py +279 -0
- api/mcp_routes.py +438 -0
- api/middleware.py +526 -0
- api/models.py +176 -0
- api/openai_compat/__init__.py +0 -0
- api/openai_compat/model_map.py +25 -0
- api/openai_compat/models.py +59 -0
- api/openai_compat/router.py +140 -0
- api/payment_endpoints.py +444 -0
- api/payment_verification.py +138 -0
- api/polymarket_models.py +155 -0
- api/polymarket_routes.py +501 -0
- api/pricing_endpoints.py +89 -0
- api/session_routing.py +54 -0
- api/skill_endpoints.py +453 -0
- api/task_http_api.py +1856 -0
- api/webhooks.py +52 -0
- api/x402_endpoints.py +280 -0
- avatar/README.md +44 -0
- avatar/__init__.py +1 -0
- avatar/config/rob.json +7 -0
- avatar/mindprint.js +395 -0
- avatar/renders/rob.meta.json +20 -0
- avatar/renders/rob.png +0 -0
- avatar/studio.html +373 -0
- avatar/webview/avatar-live.js +44 -0
- cli/README.md +89 -0
- cli/__init__.py +0 -0
- cli/cli_surface.py +67 -0
- cli/commands/__init__.py +0 -0
- cli/commands/_bootstrap.py +49 -0
- cli/commands/_errors.py +53 -0
- cli/commands/chat.py +973 -0
- cli/commands/config.py +100 -0
- cli/commands/dashboard.py +103 -0
- cli/commands/doctor.py +222 -0
- cli/commands/email.py +174 -0
- cli/commands/gateway.py +346 -0
- cli/commands/goals.py +509 -0
- cli/commands/init.py +276 -0
- cli/commands/journey.py +30 -0
- cli/commands/kb.py +280 -0
- cli/commands/model.py +107 -0
- cli/commands/owner.py +431 -0
- cli/commands/pfp.py +232 -0
- cli/commands/run.py +414 -0
- cli/commands/serve.py +41 -0
- cli/commands/session.py +684 -0
- cli/commands/skill_install.py +786 -0
- cli/commands/skills.py +128 -0
- cli/commands/subagents.py +100 -0
- cli/commands/surface.py +81 -0
- cli/commands/telegram.py +185 -0
- cli/commands/todos.py +209 -0
- cli/commands/tools.py +102 -0
- cli/commands/update.py +316 -0
- cli/commands/whatsapp.py +175 -0
- cli/config_store.py +276 -0
- cli/gitignore.py +43 -0
- cli/inventory.py +46 -0
- cli/keys.py +101 -0
- cli/persona.py +17 -0
- cli/polyrob.py +137 -0
- cli/ui/__init__.py +64 -0
- cli/ui/activity.py +141 -0
- cli/ui/app.py +538 -0
- cli/ui/banner.py +233 -0
- cli/ui/blocks.py +534 -0
- cli/ui/bootstrap_notice.py +47 -0
- cli/ui/commands/__init__.py +41 -0
- cli/ui/commands/h_cron.py +64 -0
- cli/ui/commands/h_journey.py +164 -0
- cli/ui/commands/h_kb.py +121 -0
- cli/ui/commands/h_learn.py +100 -0
- cli/ui/commands/h_mcp.py +215 -0
- cli/ui/commands/h_self.py +113 -0
- cli/ui/commands/h_skills.py +314 -0
- cli/ui/commands/handlers.py +1672 -0
- cli/ui/commands/registry.py +426 -0
- cli/ui/dialog.py +425 -0
- cli/ui/event_registry.py +110 -0
- cli/ui/event_specs.py +45 -0
- cli/ui/events.py +433 -0
- cli/ui/identity.py +15 -0
- cli/ui/lifecycle.py +159 -0
- cli/ui/live_hooks.py +43 -0
- cli/ui/model_selector.py +532 -0
- cli/ui/persistent_loop.py +181 -0
- cli/ui/pick.py +113 -0
- cli/ui/plain_renderer.py +351 -0
- cli/ui/renderer.py +384 -0
- cli/ui/rich_renderer.py +533 -0
- cli/ui/secrets.py +115 -0
- cli/ui/state.py +376 -0
- cli/ui/statusbar.py +231 -0
- cli/ui/streaming.py +254 -0
- cli/ui/terminal_render.py +105 -0
- cli/ui/theme.py +132 -0
- cli/update/__init__.py +6 -0
- cli/update/context.py +121 -0
- cli/update/detect.py +176 -0
- cli/update/engine.py +95 -0
- cli/update/migrate_guarded.py +55 -0
- cli/update/process_guard.py +215 -0
- cli/update/runners.py +78 -0
- cli/update/snapshot.py +270 -0
- cli/update/versions.py +205 -0
- core/README.md +377 -0
- core/__init__.py +174 -0
- core/assets.py +87 -0
- core/async_bridge.py +76 -0
- core/autonomy_runtime.py +259 -0
- core/base_component.py +302 -0
- core/bootstrap.py +681 -0
- core/bot.py +310 -0
- core/config.py +833 -0
- core/constants.py +126 -0
- core/container.py +462 -0
- core/db_manifest.py +101 -0
- core/embedding.py +57 -0
- core/env.py +39 -0
- core/exceptions.py +287 -0
- core/flags.py +144 -0
- core/flags_catalog.py +345 -0
- core/home_migration.py +62 -0
- core/identity.py +118 -0
- core/initialization.py +1004 -0
- core/instance.py +537 -0
- core/interactive_gate.py +96 -0
- core/logging.py +436 -0
- core/owner_doc_writer.py +176 -0
- core/pairing.py +193 -0
- core/path_safety.py +20 -0
- core/paths.py +27 -0
- core/payment_config.py +21 -0
- core/permissions.py +322 -0
- core/runtime_config.py +96 -0
- core/runtime_paths.py +135 -0
- core/seams.py +35 -0
- core/secret_scan.py +25 -0
- core/secret_scrub.py +71 -0
- core/secrets.py +35 -0
- core/security_logging_filter.py +148 -0
- core/self_context_writer.py +333 -0
- core/self_evolution.py +294 -0
- core/session_context.py +47 -0
- core/sqlite_util.py +59 -0
- core/surfaces/__init__.py +16 -0
- core/surfaces/access.py +118 -0
- core/surfaces/binding.py +88 -0
- core/surfaces/bootstrap.py +78 -0
- core/surfaces/circuit.py +168 -0
- core/surfaces/continuity.py +77 -0
- core/surfaces/correspondents.py +228 -0
- core/surfaces/dispatcher.py +216 -0
- core/surfaces/envelopes.py +75 -0
- core/surfaces/gc.py +30 -0
- core/surfaces/idempotency.py +57 -0
- core/surfaces/inbound_webhook.py +124 -0
- core/surfaces/media.py +32 -0
- core/surfaces/message_router.py +91 -0
- core/surfaces/outbound_allowlist.py +80 -0
- core/surfaces/outbound_dispatcher.py +110 -0
- core/surfaces/outbound_mirror.py +37 -0
- core/surfaces/outbound_queue.py +107 -0
- core/surfaces/outbound_target.py +12 -0
- core/surfaces/owner_admin.py +29 -0
- core/surfaces/proactive.py +78 -0
- core/surfaces/progress.py +108 -0
- core/surfaces/rate_bucket.py +21 -0
- core/surfaces/registry.py +51 -0
- core/surfaces/rendering.py +53 -0
- core/surfaces/send_policy.py +14 -0
- core/surfaces/serialize.py +24 -0
- core/surfaces/session_chat_registry.py +112 -0
- core/surfaces/session_policy.py +60 -0
- core/surfaces/surface.py +288 -0
- core/surfaces/transcription.py +88 -0
- core/surfaces/voice_echo.py +26 -0
- core/surfaces/voice_guard.py +23 -0
- core/tickers.py +174 -0
- core/tool_catalog.py +192 -0
- core/version.py +75 -0
- core/wallet/__init__.py +5 -0
- core/wallet/agent_wallet.py +59 -0
- core/wallet/audit_sink.py +61 -0
- core/wallet/config.py +70 -0
- core/wallet/factory.py +87 -0
- core/wallet/policy.py +104 -0
- core/wallet/signer.py +51 -0
- cron/README.md +65 -0
- cron/__init__.py +0 -0
- cron/delivery.py +277 -0
- cron/digest.py +124 -0
- cron/jobs.py +207 -0
- cron/runner.py +297 -0
- cron/schedule.py +194 -0
- cron/scheduler.py +201 -0
- cron/service.py +57 -0
- cron/wake_gate.py +223 -0
- data/__init__.py +1 -0
- data/prompts/autov2_prompts.json +87 -0
- data/prompts/skills/browser-automation/SKILL.md +43 -0
- data/prompts/skills/coding-workflow/SKILL.md +36 -0
- data/prompts/skills/crypto-trading-safety/SKILL.md +53 -0
- data/prompts/skills/document-writing/SKILL.md +33 -0
- data/prompts/skills/email-comms/SKILL.md +33 -0
- data/prompts/skills/file-data-ops/SKILL.md +35 -0
- data/prompts/skills/hyperliquid-account-review/SKILL.md +48 -0
- data/prompts/skills/hyperliquid-market-data/SKILL.md +52 -0
- data/prompts/skills/hyperliquid-trading/SKILL.md +59 -0
- data/prompts/skills/lead-research/SKILL.md +86 -0
- data/prompts/skills/market-research-brief/SKILL.md +104 -0
- data/prompts/skills/person-analyzer/SKILL.md +124 -0
- data/prompts/skills/polymarket-market-research/SKILL.md +54 -0
- data/prompts/skills/polymarket-portfolio-review/SKILL.md +50 -0
- data/prompts/skills/polymarket-trading/SKILL.md +55 -0
- data/prompts/skills/presentation-creator/SKILL.md +36 -0
- data/prompts/skills/project-analyzer/SKILL.md +169 -0
- data/prompts/skills/rules.json +268 -0
- data/prompts/skills/secret-handling/SKILL.md +20 -0
- data/prompts/skills/skill-authoring/SKILL.md +31 -0
- data/prompts/skills/skill-security-review/SKILL.md +27 -0
- data/prompts/skills/social-discovery/SKILL.md +57 -0
- data/prompts/skills/task-planning/SKILL.md +33 -0
- data/prompts/skills/web-research/SKILL.md +39 -0
- data/prompts/skills/web-scraping/SKILL.md +73 -0
- data/prompts/skills/x-engagement/SKILL.md +69 -0
- data/prompts/system_prompts.json +4 -0
- data/subscription_channels.json +5 -0
- modules/README.md +503 -0
- modules/__init__.py +124 -0
- modules/auth/__init__.py +13 -0
- modules/auth/api_key_manager.py +157 -0
- modules/auth/identity_mapper.py +380 -0
- modules/auth/siwe_auth.py +273 -0
- modules/auth/tier_manager.py +173 -0
- modules/base_module.py +190 -0
- modules/credits/__init__.py +29 -0
- modules/credits/balance_manager.py +237 -0
- modules/credits/cost_utils.py +129 -0
- modules/credits/pricing.py +139 -0
- modules/credits/unified_ledger.py +148 -0
- modules/credits/usage_meter.py +310 -0
- modules/credits/usage_tracker.py +674 -0
- modules/database/__init__.py +33 -0
- modules/database/audit_log.py +322 -0
- modules/database/auth_tables.py +276 -0
- modules/database/connection.py +644 -0
- modules/database/connection_pool.py +305 -0
- modules/database/conversation_contexts.py +446 -0
- modules/database/database_manager.py +237 -0
- modules/database/hyperliquid.py +375 -0
- modules/database/polymarket.py +574 -0
- modules/database/user_mcp_servers.py +728 -0
- modules/database/user_profiles.py +562 -0
- modules/database/utils.py +107 -0
- modules/database/x402_tables.py +92 -0
- modules/eip8004/README.md +1069 -0
- modules/eip8004/__init__.py +65 -0
- modules/eip8004/contracts.py +498 -0
- modules/eip8004/models.py +271 -0
- modules/eip8004/registration.py +148 -0
- modules/eip8004/reputation.py +367 -0
- modules/eip8004/validation.py +277 -0
- modules/llm/__init__.py +97 -0
- modules/llm/adapters.py +963 -0
- modules/llm/anthropic_client.py +831 -0
- modules/llm/available_models.py +106 -0
- modules/llm/brain_scrubber.py +137 -0
- modules/llm/cache_hints.py +162 -0
- modules/llm/deepseek_client.py +562 -0
- modules/llm/gemini_client.py +1367 -0
- modules/llm/llm_client.py +1139 -0
- modules/llm/llm_client_registry.py +188 -0
- modules/llm/llm_factory.py +237 -0
- modules/llm/llm_manager.py +1036 -0
- modules/llm/messages.py +374 -0
- modules/llm/model_registry.py +1935 -0
- modules/llm/nvidia_client.py +125 -0
- modules/llm/openai_client.py +800 -0
- modules/llm/openrouter_client.py +969 -0
- modules/llm/profiles.py +178 -0
- modules/llm/test_anthropic_cache_capture.py +35 -0
- modules/llm/test_cached_pricing_derivation.py +24 -0
- modules/llm/think_scrubber.py +444 -0
- modules/llm/token_counter.py +268 -0
- modules/memory/__init__.py +35 -0
- modules/memory/backend_factory.py +83 -0
- modules/memory/cache_manager.py +186 -0
- modules/memory/episodic.py +108 -0
- modules/memory/local_vector_memory_provider.py +552 -0
- modules/memory/memory_manager.py +225 -0
- modules/memory/models.py +312 -0
- modules/memory/provider.py +210 -0
- modules/memory/registry.py +205 -0
- modules/memory/sqlite_memory_provider.py +767 -0
- modules/memory/task/__init__.py +52 -0
- modules/memory/task/compaction_manager.py +157 -0
- modules/memory/task/context_retriever.py +809 -0
- modules/memory/task/hierarchical_memory.py +1002 -0
- modules/memory/task/lexical_retriever.py +71 -0
- modules/memory/task/null_context_manager.py +213 -0
- modules/memory/task/phase_manager.py +625 -0
- modules/memory/task/reflection_service.py +157 -0
- modules/memory/task/semantic_retriever.py +218 -0
- modules/memory/task/task_context_manager.py +1165 -0
- modules/memory/task/test_forgetting_engages.py +24 -0
- modules/memory/task/test_reflection_llm.py +39 -0
- modules/memory/task/test_threat_scan.py +53 -0
- modules/memory/task/threat_scan.py +90 -0
- modules/memory/test_sqlite_memory_provider.py +106 -0
- modules/memory/user_profile_manager.py +527 -0
- modules/payments/__init__.py +11 -0
- modules/payments/deposit_monitor.py +478 -0
- modules/payments/price_oracle.py +70 -0
- modules/payments/treasury_sweeper.py +401 -0
- modules/payments/wallet_generator.py +145 -0
- modules/pfp/__init__.py +1 -0
- modules/pfp/config.py +92 -0
- modules/pfp/mesh.py +565 -0
- modules/pfp/push.py +69 -0
- modules/pfp/renderer.py +85 -0
- modules/pfp/store.py +82 -0
- modules/skills/__init__.py +1 -0
- modules/skills/skill_usage.py +248 -0
- modules/transcription/__init__.py +24 -0
- modules/transcription/base.py +20 -0
- modules/transcription/faster_whisper_transcriber.py +55 -0
- modules/x402/README.md +249 -0
- modules/x402/__init__.py +22 -0
- modules/x402/invoicing.py +416 -0
- modules/x402/middleware.py +364 -0
- modules/x402/settlement_watcher.py +145 -0
- modules/x402/x402_integration.py +344 -0
- polyrob-0.5.0.dist-info/METADATA +482 -0
- polyrob-0.5.0.dist-info/RECORD +745 -0
- polyrob-0.5.0.dist-info/WHEEL +5 -0
- polyrob-0.5.0.dist-info/entry_points.txt +2 -0
- polyrob-0.5.0.dist-info/licenses/LICENSE +21 -0
- polyrob-0.5.0.dist-info/top_level.txt +12 -0
- surfaces/README.md +70 -0
- surfaces/__init__.py +0 -0
- surfaces/email/__init__.py +0 -0
- surfaces/email/dedup.py +44 -0
- surfaces/email/harness.py +187 -0
- surfaces/email/inbound.py +159 -0
- surfaces/email/seed.py +60 -0
- surfaces/email/surface.py +105 -0
- surfaces/telegram/__init__.py +0 -0
- surfaces/telegram/dedup.py +83 -0
- surfaces/telegram/harness.py +886 -0
- surfaces/telegram/inbound.py +145 -0
- surfaces/telegram/interactive_tools.py +49 -0
- surfaces/telegram/markdown.py +347 -0
- surfaces/telegram/rate_limit.py +167 -0
- surfaces/telegram/surface.py +187 -0
- surfaces/telegram/voice.py +52 -0
- surfaces/whatsapp/__init__.py +0 -0
- surfaces/whatsapp/client.py +51 -0
- surfaces/whatsapp/harness.py +62 -0
- surfaces/whatsapp/inbound.py +132 -0
- surfaces/whatsapp/surface.py +64 -0
- surfaces/whatsapp/window.py +34 -0
- tools/README.md +578 -0
- tools/__init__.py +424 -0
- tools/alchemy/__init__.py +5 -0
- tools/alchemy/alchemy_tool.py +389 -0
- tools/anysite/__init__.py +17 -0
- tools/anysite/client.py +88 -0
- tools/anysite/tool.py +89 -0
- tools/base_tool.py +468 -0
- tools/browser/__init__.py +22 -0
- tools/browser/actions.py +118 -0
- tools/browser/browser.py +1457 -0
- tools/browser/browser_manager.py +690 -0
- tools/browser/context.py +1748 -0
- tools/browser/playwright_utils.py +137 -0
- tools/browser/views.py +56 -0
- tools/code_exec/SANDBOX_SECURITY.md +144 -0
- tools/code_exec/__init__.py +178 -0
- tools/code_exec/backend.py +73 -0
- tools/code_exec/backends/__init__.py +1 -0
- tools/code_exec/backends/docker.py +779 -0
- tools/code_exec/backends/local_subprocess.py +154 -0
- tools/code_exec/env_policy.py +45 -0
- tools/code_exec/result.py +42 -0
- tools/code_exec/sandbox_guard.py +59 -0
- tools/code_exec/tool.py +227 -0
- tools/coding/__init__.py +50 -0
- tools/coding/edit.py +107 -0
- tools/coding/search.py +113 -0
- tools/coding/tool.py +353 -0
- tools/collabland/__init__.py +5 -0
- tools/collabland/collabland_tool.py +471 -0
- tools/controller/__init__.py +0 -0
- tools/controller/_helpers.py +165 -0
- tools/controller/action_registration.py +1765 -0
- tools/controller/approval.py +244 -0
- tools/controller/approval_interactive.py +90 -0
- tools/controller/delegation.py +195 -0
- tools/controller/execution.py +797 -0
- tools/controller/execution_context.py +141 -0
- tools/controller/hooks.py +135 -0
- tools/controller/introspection.py +344 -0
- tools/controller/mcp_registrar.py +190 -0
- tools/controller/message_send.py +31 -0
- tools/controller/registry/__init__.py +4 -0
- tools/controller/registry/schema_generators.py +395 -0
- tools/controller/registry/schema_sanitizer.py +406 -0
- tools/controller/registry/service.py +1266 -0
- tools/controller/registry/views.py +151 -0
- tools/controller/service.py +409 -0
- tools/controller/tool_management.py +334 -0
- tools/controller/types.py +28 -0
- tools/controller/views.py +450 -0
- tools/cronjob_tools.py +166 -0
- tools/crypto_trade_gate.py +59 -0
- tools/descriptors.py +452 -0
- tools/dom/__init__.py +0 -0
- tools/dom/history_tree_processor/service.py +244 -0
- tools/dom/history_tree_processor/view.py +99 -0
- tools/dom/service.py +471 -0
- tools/dom/views.py +346 -0
- tools/email_tool.py +403 -0
- tools/exceptions.py +169 -0
- tools/filesystem.py +1038 -0
- tools/filesystem_docproc.py +331 -0
- tools/filesystem_pdf.py +755 -0
- tools/git/__init__.py +42 -0
- tools/git/tool.py +258 -0
- tools/github/__init__.py +33 -0
- tools/github/client.py +94 -0
- tools/github/tool.py +237 -0
- tools/goal_tools.py +309 -0
- tools/hyperliquid/__init__.py +56 -0
- tools/hyperliquid/models.py +233 -0
- tools/hyperliquid/service.py +1680 -0
- tools/knowledge_ingest.py +950 -0
- tools/mcp/README.md +535 -0
- tools/mcp/__init__.py +52 -0
- tools/mcp/catalog.py +115 -0
- tools/mcp/config.py +337 -0
- tools/mcp/mcp_tool.py +1415 -0
- tools/mcp/param_coercion.py +289 -0
- tools/mcp/protocol.py +1520 -0
- tools/mcp/rate_limit.py +50 -0
- tools/mcp/security.py +424 -0
- tools/mcp/self_install.py +111 -0
- tools/mcp/server_manager.py +829 -0
- tools/mcp/subscriptions.py +72 -0
- tools/mcp/user_mcp_service.py +899 -0
- tools/mcp/validation_tracker.py +89 -0
- tools/mcp/views.py +346 -0
- tools/oauth/__init__.py +17 -0
- tools/oauth/manager.py +75 -0
- tools/oauth/provider.py +69 -0
- tools/oauth/providers/__init__.py +1 -0
- tools/oauth/providers/generic_oauth2.py +96 -0
- tools/perplexity_tool.py +280 -0
- tools/polymarket/__init__.py +41 -0
- tools/polymarket/clob_adapter.py +76 -0
- tools/polymarket/models.py +182 -0
- tools/polymarket/service.py +2082 -0
- tools/self_env/__init__.py +61 -0
- tools/self_env/tool.py +274 -0
- tools/shell/__init__.py +80 -0
- tools/shell/backend_pool.py +118 -0
- tools/shell/discipline.py +62 -0
- tools/shell/executor.py +157 -0
- tools/shell/loopback_allow.py +87 -0
- tools/shell/process_registry.py +113 -0
- tools/shell/process_tool.py +131 -0
- tools/shell/state.py +129 -0
- tools/shell/tool.py +151 -0
- tools/task_tool.py +637 -0
- tools/twitter_tool.py +2086 -0
- tools/user_directory.py +179 -0
- tools/web_fetch/__init__.py +3 -0
- tools/web_fetch/fetcher.py +144 -0
- tools/web_fetch/render.py +44 -0
- tools/web_fetch/tool.py +60 -0
- tools/x402/__init__.py +60 -0
- tools/x402/client.py +47 -0
- tools/x402/invoice_tool.py +143 -0
- tools/x402/real_client.py +250 -0
- tools/x402/service.py +131 -0
- utils/README.md +498 -0
- utils/__init__.py +46 -0
- utils/auth_utils.py +114 -0
- utils/bounded_collections.py +162 -0
- utils/circuit_breaker.py +389 -0
- utils/gif_utils.py +435 -0
- utils/markdown_utils.py +291 -0
- utils/message_utils.py +147 -0
- utils/metrics.py +36 -0
- utils/path_validator.py +227 -0
- utils/rate_limit_manager.py +399 -0
- utils/result_size.py +296 -0
- utils/time_utils.py +206 -0
- utils/user_utils.py +188 -0
- webview/README.md +111 -0
- webview/RENAME_AND_ALIGN_HANDOFF.md +200 -0
- webview/__init__.py +8 -0
- webview/activity.py +636 -0
- webview/owner_auth.py +108 -0
- webview/pages.py +448 -0
- webview/repair_sessions.py +399 -0
- webview/server.py +4323 -0
- webview/server_launcher.py +112 -0
- webview/static/css/activity.css +89 -0
- webview/static/css/chat.css +1772 -0
- webview/static/css/components.css +1190 -0
- webview/static/css/config-panel.css +651 -0
- webview/static/css/pages/admin.css +766 -0
- webview/static/css/pages/profile.css +231 -0
- webview/static/css/pages/settings.css +297 -0
- webview/static/css/pages/signin.css +316 -0
- webview/static/css/style.css +4250 -0
- webview/static/css/variables.css +147 -0
- webview/static/css/workspace-fullwidth-fix.css +109 -0
- webview/static/img/favicon.ico +0 -0
- webview/static/js/activity.js +394 -0
- webview/static/js/admin/activity.js +186 -0
- webview/static/js/admin/dashboard.js +163 -0
- webview/static/js/admin/user_detail.js +400 -0
- webview/static/js/admin/users.js +174 -0
- webview/static/js/admin/utils.js +214 -0
- webview/static/js/chat.js +3178 -0
- webview/static/js/config-panel.js +522 -0
- webview/static/js/constants.js +20 -0
- webview/static/js/error-handler.js +226 -0
- webview/static/js/ethers.min.js +1 -0
- webview/static/js/event-filter.js +137 -0
- webview/static/js/event-store.js +351 -0
- webview/static/js/file-attachments.js +374 -0
- webview/static/js/file-loader.js +163 -0
- webview/static/js/index.js +103 -0
- webview/static/js/performance-utils.js +196 -0
- webview/static/js/profile.js +396 -0
- webview/static/js/screenshot.js +386 -0
- webview/static/js/session.js +1527 -0
- webview/static/js/settings.js +1288 -0
- webview/static/js/sidebar-data.js +766 -0
- webview/static/js/sidebar-toggle.js +296 -0
- webview/static/js/socket.io.min.js +7 -0
- webview/static/js/stats.js +355 -0
- webview/static/js/ui-utils.js +420 -0
- webview/static/js/workspace.js +966 -0
- webview/stats_service.py +613 -0
- webview/templates/__init__.py +1 -0
- webview/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- webview/templates/activity.html +36 -0
- webview/templates/admin/activity.html +122 -0
- webview/templates/admin/dashboard.html +112 -0
- webview/templates/admin/user_detail.html +228 -0
- webview/templates/admin/users.html +105 -0
- webview/templates/autonomy.html +60 -0
- webview/templates/error.html +133 -0
- webview/templates/finance.html +90 -0
- webview/templates/identity.html +73 -0
- webview/templates/index.html +42 -0
- webview/templates/layout.html +125 -0
- webview/templates/memory.html +59 -0
- webview/templates/owner_login.html +15 -0
- webview/templates/profile.html +121 -0
- webview/templates/session.html +312 -0
- webview/templates/settings.html +534 -0
- webview/templates/sidebar.html +112 -0
- webview/templates/signin.html +213 -0
- webview/templates/status.html +14 -0
- webview/templates/system.html +45 -0
- webview/webgate.py +196 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool Call ID Tracking System.
|
|
3
|
+
|
|
4
|
+
This module provides robust tracking of tool call IDs throughout their lifecycle,
|
|
5
|
+
ensuring proper pairing of tool calls with their responses and preventing ID loss
|
|
6
|
+
during execution.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Dict, List, Optional, Set, Tuple, Any
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from threading import RLock
|
|
14
|
+
import json
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ToolCallRecord:
|
|
19
|
+
"""Record of a single tool call for tracking."""
|
|
20
|
+
id: str
|
|
21
|
+
name: str
|
|
22
|
+
args: Dict[str, Any]
|
|
23
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
24
|
+
status: str = "pending" # pending, executing, completed, failed
|
|
25
|
+
result: Optional[Any] = None
|
|
26
|
+
error: Optional[str] = None
|
|
27
|
+
normalized_call: Optional[Dict[str, Any]] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ToolCallTracker:
|
|
31
|
+
"""
|
|
32
|
+
Centralized tracker for tool call IDs and their lifecycle.
|
|
33
|
+
|
|
34
|
+
This replaces the fragile temporary attribute approach with a proper
|
|
35
|
+
state management system for tool calls.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, session_id: str, logger: Optional[logging.Logger] = None):
|
|
39
|
+
"""
|
|
40
|
+
Initialize the tracker.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
session_id: Session identifier for context
|
|
44
|
+
logger: Optional logger instance
|
|
45
|
+
"""
|
|
46
|
+
self.session_id = session_id
|
|
47
|
+
|
|
48
|
+
# Use consistent logger initialization pattern (matches Agent, Controller, Registry, etc.)
|
|
49
|
+
if logger:
|
|
50
|
+
self.logger = logger
|
|
51
|
+
else:
|
|
52
|
+
from agents.task.logging_config import get_task_logger
|
|
53
|
+
self.logger = get_task_logger("tool_call_tracker", session_id)
|
|
54
|
+
|
|
55
|
+
# Core tracking structures
|
|
56
|
+
self._active_calls: Dict[str, ToolCallRecord] = {}
|
|
57
|
+
self._completed_calls: Dict[str, ToolCallRecord] = {}
|
|
58
|
+
self._orphaned_calls: Set[str] = set()
|
|
59
|
+
|
|
60
|
+
# Step-based tracking for the current execution
|
|
61
|
+
self._current_step_calls: List[str] = []
|
|
62
|
+
self._step_history: List[List[str]] = []
|
|
63
|
+
|
|
64
|
+
# Thread safety with re-entrant lock for better async compatibility
|
|
65
|
+
self._lock = RLock()
|
|
66
|
+
|
|
67
|
+
# Statistics
|
|
68
|
+
self._stats = {
|
|
69
|
+
"total_calls": 0,
|
|
70
|
+
"completed_calls": 0,
|
|
71
|
+
"failed_calls": 0,
|
|
72
|
+
"orphaned_calls": 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# MCP validation-failure tracking (schema-injection policy) now lives in
|
|
76
|
+
# tools/mcp/ — that's MCP error policy, not tool-call ID lifecycle.
|
|
77
|
+
from tools.mcp.validation_tracker import MCPValidationTracker
|
|
78
|
+
self._mcp_validation = MCPValidationTracker(logger=self.logger)
|
|
79
|
+
|
|
80
|
+
# ID generation counter for globally unique IDs (Issue #7 fix)
|
|
81
|
+
self._id_counter = 0
|
|
82
|
+
|
|
83
|
+
self.logger.debug(f"ToolCallTracker initialized for session {session_id}")
|
|
84
|
+
|
|
85
|
+
def register_tool_calls(self, tool_calls: List[Dict[str, Any]]) -> List[str]:
|
|
86
|
+
"""
|
|
87
|
+
Register new tool calls for tracking.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
tool_calls: List of normalized tool call dictionaries
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
List of registered tool call IDs
|
|
94
|
+
"""
|
|
95
|
+
with self._lock:
|
|
96
|
+
registered_ids = []
|
|
97
|
+
|
|
98
|
+
for call in tool_calls:
|
|
99
|
+
call_id = call.get("id")
|
|
100
|
+
if not call_id:
|
|
101
|
+
self.logger.warning(f"Tool call missing ID: {call}")
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
# Create record
|
|
105
|
+
record = ToolCallRecord(
|
|
106
|
+
id=call_id,
|
|
107
|
+
name=call.get("name", "unknown"),
|
|
108
|
+
args=call.get("args", {}),
|
|
109
|
+
normalized_call=call
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Store in active calls
|
|
113
|
+
self._active_calls[call_id] = record
|
|
114
|
+
registered_ids.append(call_id)
|
|
115
|
+
self._current_step_calls.append(call_id)
|
|
116
|
+
self._stats["total_calls"] += 1
|
|
117
|
+
|
|
118
|
+
self.logger.debug(f"Registered tool call: {call_id} ({record.name})")
|
|
119
|
+
|
|
120
|
+
return registered_ids
|
|
121
|
+
|
|
122
|
+
def get_current_step_calls(self) -> List[str]:
|
|
123
|
+
"""
|
|
124
|
+
Get tool call IDs for the current step.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
List of tool call IDs in the current execution step
|
|
128
|
+
"""
|
|
129
|
+
with self._lock:
|
|
130
|
+
return self._current_step_calls.copy()
|
|
131
|
+
|
|
132
|
+
def get_normalized_calls(self, call_ids: Optional[List[str]] = None) -> List[Dict[str, Any]]:
|
|
133
|
+
"""
|
|
134
|
+
Get normalized tool calls by IDs.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
call_ids: Optional list of specific IDs (uses current step if not provided)
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
List of normalized tool call dictionaries
|
|
141
|
+
"""
|
|
142
|
+
with self._lock:
|
|
143
|
+
if call_ids is None:
|
|
144
|
+
call_ids = self._current_step_calls
|
|
145
|
+
|
|
146
|
+
normalized = []
|
|
147
|
+
for call_id in call_ids:
|
|
148
|
+
if call_id in self._active_calls:
|
|
149
|
+
record = self._active_calls[call_id]
|
|
150
|
+
if record.normalized_call:
|
|
151
|
+
normalized.append(record.normalized_call)
|
|
152
|
+
elif call_id in self._completed_calls:
|
|
153
|
+
record = self._completed_calls[call_id]
|
|
154
|
+
if record.normalized_call:
|
|
155
|
+
normalized.append(record.normalized_call)
|
|
156
|
+
|
|
157
|
+
return normalized
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def mark_completed(self, call_id: str, result: Any = None) -> bool:
|
|
161
|
+
"""
|
|
162
|
+
Mark a tool call as completed with optional result.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
call_id: Tool call ID
|
|
166
|
+
result: Optional execution result
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
True if successfully marked, False if not found
|
|
170
|
+
"""
|
|
171
|
+
with self._lock:
|
|
172
|
+
if call_id in self._active_calls:
|
|
173
|
+
record = self._active_calls[call_id]
|
|
174
|
+
record.status = "completed"
|
|
175
|
+
# M2: do NOT retain the full tool result. It's kept for the session's
|
|
176
|
+
# lifetime in _completed_calls but never read back (only status/name/
|
|
177
|
+
# timestamp are used) — on a long run that's an unbounded duplicate of
|
|
178
|
+
# every large tool output. The payload already lives in message history.
|
|
179
|
+
record.result = None
|
|
180
|
+
|
|
181
|
+
# Move to completed
|
|
182
|
+
self._completed_calls[call_id] = record
|
|
183
|
+
del self._active_calls[call_id]
|
|
184
|
+
|
|
185
|
+
self._stats["completed_calls"] += 1
|
|
186
|
+
self.logger.debug(f"Tool call {call_id} completed")
|
|
187
|
+
return True
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
def mark_failed(self, call_id: str, error: str) -> bool:
|
|
191
|
+
"""
|
|
192
|
+
Mark a tool call as failed with error message.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
call_id: Tool call ID
|
|
196
|
+
error: Error message
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
True if successfully marked, False if not found
|
|
200
|
+
"""
|
|
201
|
+
with self._lock:
|
|
202
|
+
if call_id in self._active_calls:
|
|
203
|
+
record = self._active_calls[call_id]
|
|
204
|
+
record.status = "failed"
|
|
205
|
+
record.error = error
|
|
206
|
+
|
|
207
|
+
# Move to completed (with failed status)
|
|
208
|
+
self._completed_calls[call_id] = record
|
|
209
|
+
del self._active_calls[call_id]
|
|
210
|
+
|
|
211
|
+
self._stats["failed_calls"] += 1
|
|
212
|
+
self.logger.warning(f"Tool call {call_id} failed: {error}")
|
|
213
|
+
return True
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def complete_step(self) -> None:
|
|
218
|
+
"""Complete the current step and start a new one."""
|
|
219
|
+
with self._lock:
|
|
220
|
+
if self._current_step_calls:
|
|
221
|
+
self._step_history.append(self._current_step_calls.copy())
|
|
222
|
+
self._current_step_calls.clear()
|
|
223
|
+
self.logger.debug(f"Completed step with {len(self._step_history[-1])} tool calls")
|
|
224
|
+
|
|
225
|
+
# PERSISTENCE: Auto-save state after each step with retry logic
|
|
226
|
+
try:
|
|
227
|
+
from agents.task.path import pm
|
|
228
|
+
import time
|
|
229
|
+
|
|
230
|
+
# Clean session ID before getting data dir
|
|
231
|
+
clean_session_id = pm().clean_session_id(self.session_id)
|
|
232
|
+
data_dir = pm().get_data_dir(clean_session_id)
|
|
233
|
+
state_file = data_dir / "tool_calls.json"
|
|
234
|
+
|
|
235
|
+
# Ensure directory exists
|
|
236
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
|
237
|
+
|
|
238
|
+
# Retry logic: 3 attempts with exponential backoff
|
|
239
|
+
max_retries = 3
|
|
240
|
+
for attempt in range(max_retries):
|
|
241
|
+
if self.save_to_file(state_file):
|
|
242
|
+
if attempt > 0:
|
|
243
|
+
self.logger.info(f"✓ Auto-saved tool call state to {state_file} (attempt {attempt + 1})")
|
|
244
|
+
else:
|
|
245
|
+
self.logger.debug(f"✓ Auto-saved tool call state to {state_file}")
|
|
246
|
+
break
|
|
247
|
+
else:
|
|
248
|
+
if attempt < max_retries - 1:
|
|
249
|
+
wait_time = 0.1 * (2 ** attempt) # 0.1s, 0.2s, 0.4s
|
|
250
|
+
self.logger.warning(f"Failed to save tool call state (attempt {attempt + 1}/{max_retries}), retrying in {wait_time}s...")
|
|
251
|
+
time.sleep(wait_time)
|
|
252
|
+
else:
|
|
253
|
+
self.logger.error(f"❌ Failed to auto-save tool call state after {max_retries} attempts: {state_file}")
|
|
254
|
+
except Exception as e:
|
|
255
|
+
self.logger.error(f"❌ Critical error during tool call state persistence: {e}", exc_info=True)
|
|
256
|
+
|
|
257
|
+
def save_to_file(self, filepath) -> bool:
|
|
258
|
+
"""Save tracker state to file.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
filepath: Path to save state
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
True if successful
|
|
265
|
+
"""
|
|
266
|
+
from pathlib import Path
|
|
267
|
+
with self._lock:
|
|
268
|
+
try:
|
|
269
|
+
state_data = {
|
|
270
|
+
'version': '1.0',
|
|
271
|
+
'session_id': self.session_id,
|
|
272
|
+
'timestamp': datetime.now().isoformat(),
|
|
273
|
+
'active_calls': {
|
|
274
|
+
call_id: {
|
|
275
|
+
'id': record.id,
|
|
276
|
+
'name': record.name,
|
|
277
|
+
'args': record.args,
|
|
278
|
+
'timestamp': record.timestamp.isoformat(),
|
|
279
|
+
'status': record.status,
|
|
280
|
+
'error': record.error
|
|
281
|
+
}
|
|
282
|
+
for call_id, record in self._active_calls.items()
|
|
283
|
+
},
|
|
284
|
+
'current_step_calls': self._current_step_calls,
|
|
285
|
+
'stats': self._stats
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
# Atomic write
|
|
289
|
+
import tempfile
|
|
290
|
+
import os
|
|
291
|
+
|
|
292
|
+
filepath = Path(filepath)
|
|
293
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
294
|
+
|
|
295
|
+
temp_fd, temp_path = tempfile.mkstemp(
|
|
296
|
+
dir=filepath.parent,
|
|
297
|
+
prefix='.tool_calls_',
|
|
298
|
+
suffix='.tmp'
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
with os.fdopen(temp_fd, 'w') as f:
|
|
303
|
+
json.dump(state_data, f, indent=2)
|
|
304
|
+
f.flush()
|
|
305
|
+
os.fsync(f.fileno())
|
|
306
|
+
|
|
307
|
+
os.replace(temp_path, str(filepath))
|
|
308
|
+
return True
|
|
309
|
+
|
|
310
|
+
except Exception:
|
|
311
|
+
if os.path.exists(temp_path):
|
|
312
|
+
os.unlink(temp_path)
|
|
313
|
+
raise
|
|
314
|
+
|
|
315
|
+
except Exception as e:
|
|
316
|
+
self.logger.error(f"Failed to save tool call state: {e}")
|
|
317
|
+
return False
|
|
318
|
+
|
|
319
|
+
def load_from_file(self, filepath) -> bool:
|
|
320
|
+
"""Load tracker state from file.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
filepath: Path to load from
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
True if successful
|
|
327
|
+
"""
|
|
328
|
+
from pathlib import Path
|
|
329
|
+
with self._lock:
|
|
330
|
+
try:
|
|
331
|
+
filepath = Path(filepath)
|
|
332
|
+
if not filepath.exists():
|
|
333
|
+
return False
|
|
334
|
+
|
|
335
|
+
with open(filepath, 'r') as f:
|
|
336
|
+
state_data = json.load(f)
|
|
337
|
+
|
|
338
|
+
# Validate
|
|
339
|
+
if state_data.get('version') != '1.0':
|
|
340
|
+
self.logger.warning(f"Version mismatch: {state_data.get('version')}")
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
if state_data.get('session_id') != self.session_id:
|
|
344
|
+
self.logger.warning(f"Session ID mismatch")
|
|
345
|
+
return False
|
|
346
|
+
|
|
347
|
+
# Restore active calls
|
|
348
|
+
for call_id, call_data in state_data.get('active_calls', {}).items():
|
|
349
|
+
record = ToolCallRecord(
|
|
350
|
+
id=call_data['id'],
|
|
351
|
+
name=call_data['name'],
|
|
352
|
+
args=call_data['args'],
|
|
353
|
+
timestamp=datetime.fromisoformat(call_data['timestamp']),
|
|
354
|
+
status=call_data['status'],
|
|
355
|
+
error=call_data.get('error')
|
|
356
|
+
)
|
|
357
|
+
self._active_calls[call_id] = record
|
|
358
|
+
|
|
359
|
+
# Restore current step
|
|
360
|
+
self._current_step_calls = state_data.get('current_step_calls', [])
|
|
361
|
+
|
|
362
|
+
# Restore stats
|
|
363
|
+
saved_stats = state_data.get('stats', {})
|
|
364
|
+
for key in self._stats:
|
|
365
|
+
if key in saved_stats:
|
|
366
|
+
self._stats[key] = saved_stats[key]
|
|
367
|
+
|
|
368
|
+
self.logger.info(f"Restored tool call tracker with {len(self._active_calls)} active calls")
|
|
369
|
+
return True
|
|
370
|
+
|
|
371
|
+
except Exception as e:
|
|
372
|
+
self.logger.error(f"Failed to load tool call state: {e}")
|
|
373
|
+
return False
|
|
374
|
+
|
|
375
|
+
def has_active_calls(self) -> bool:
|
|
376
|
+
"""
|
|
377
|
+
Check if there are any active (non-completed) tool calls.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
True if there are active calls
|
|
381
|
+
"""
|
|
382
|
+
with self._lock:
|
|
383
|
+
return len(self._active_calls) > 0
|
|
384
|
+
|
|
385
|
+
def has_call(self, call_id: str) -> bool:
|
|
386
|
+
"""
|
|
387
|
+
Check if a tool call ID exists in tracking.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
call_id: Tool call ID to check
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
True if call exists in active or completed calls
|
|
394
|
+
"""
|
|
395
|
+
with self._lock:
|
|
396
|
+
return call_id in self._active_calls or call_id in self._completed_calls
|
|
397
|
+
|
|
398
|
+
def get_call_record(self, call_id: str) -> Optional[ToolCallRecord]:
|
|
399
|
+
"""
|
|
400
|
+
Get complete record for a tool call.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
call_id: Tool call ID
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
ToolCallRecord or None if not found
|
|
407
|
+
"""
|
|
408
|
+
with self._lock:
|
|
409
|
+
if call_id in self._active_calls:
|
|
410
|
+
return self._active_calls[call_id]
|
|
411
|
+
elif call_id in self._completed_calls:
|
|
412
|
+
return self._completed_calls[call_id]
|
|
413
|
+
return None
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
417
|
+
"""
|
|
418
|
+
Get tracker statistics.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
Dictionary of statistics
|
|
422
|
+
"""
|
|
423
|
+
with self._lock:
|
|
424
|
+
return {
|
|
425
|
+
**self._stats.copy(),
|
|
426
|
+
"active_calls": len(self._active_calls),
|
|
427
|
+
"completed_calls_cached": len(self._completed_calls),
|
|
428
|
+
"orphaned_ids": len(self._orphaned_calls),
|
|
429
|
+
"total_steps": len(self._step_history)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
def register_call(self, call_id: str, tool_name: str, args: Dict[str, Any]) -> None:
|
|
433
|
+
"""
|
|
434
|
+
Simple interface to register a single tool call.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
call_id: Tool call ID
|
|
438
|
+
tool_name: Name of the tool being called
|
|
439
|
+
args: Arguments passed to the tool
|
|
440
|
+
"""
|
|
441
|
+
self.register_tool_calls([{
|
|
442
|
+
"id": call_id,
|
|
443
|
+
"name": tool_name,
|
|
444
|
+
"args": args
|
|
445
|
+
}])
|
|
446
|
+
|
|
447
|
+
def complete_call(self, call_id: str, result: Any = None) -> None:
|
|
448
|
+
"""
|
|
449
|
+
Simple interface to mark a tool call as completed.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
call_id: Tool call ID
|
|
453
|
+
result: Result of the tool execution
|
|
454
|
+
"""
|
|
455
|
+
self.mark_completed(call_id, result)
|
|
456
|
+
|
|
457
|
+
def reset(self) -> None:
|
|
458
|
+
"""Reset the tracker to initial state."""
|
|
459
|
+
with self._lock:
|
|
460
|
+
self._active_calls.clear()
|
|
461
|
+
self._completed_calls.clear()
|
|
462
|
+
self._orphaned_calls.clear()
|
|
463
|
+
self._current_step_calls.clear()
|
|
464
|
+
self._step_history.clear()
|
|
465
|
+
self._mcp_validation.reset()
|
|
466
|
+
self._stats = {
|
|
467
|
+
"total_calls": 0,
|
|
468
|
+
"completed_calls": 0,
|
|
469
|
+
"failed_calls": 0,
|
|
470
|
+
"orphaned_calls": 0
|
|
471
|
+
}
|
|
472
|
+
self.logger.info(f"ToolCallTracker reset for session {self.session_id}")
|
|
473
|
+
|
|
474
|
+
# ========== Issue #7 Fix: Centralized ID Generation ==========
|
|
475
|
+
|
|
476
|
+
def generate_call_id(self, tool_name: str = "call") -> str:
|
|
477
|
+
"""Generate a globally unique tool call ID.
|
|
478
|
+
|
|
479
|
+
Format: call_{session_prefix}_{counter}_{timestamp_suffix}
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
tool_name: Optional tool name for context
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
Unique tool call ID
|
|
486
|
+
"""
|
|
487
|
+
with self._lock:
|
|
488
|
+
self._id_counter += 1
|
|
489
|
+
# Use session prefix (first 8 chars) + counter + timestamp suffix
|
|
490
|
+
session_prefix = self.session_id[:8] if self.session_id else "unknown"
|
|
491
|
+
timestamp_suffix = datetime.now().strftime("%H%M%S")
|
|
492
|
+
return f"call_{session_prefix}_{self._id_counter}_{timestamp_suffix}"
|
|
493
|
+
|
|
494
|
+
# ========== Issue #1 Fix: MCP Validation Failure Tracking ==========
|
|
495
|
+
|
|
496
|
+
# MCP validation-failure policy is delegated to MCPValidationTracker
|
|
497
|
+
# (tools/mcp/validation_tracker.py). These thin wrappers preserve the
|
|
498
|
+
# public API for existing callers (tools/mcp/mcp_tool.py).
|
|
499
|
+
|
|
500
|
+
def track_mcp_validation_failure(self, server_name: str, tool_name: str) -> int:
|
|
501
|
+
"""Track an MCP validation failure; returns the current count."""
|
|
502
|
+
return self._mcp_validation.track_failure(server_name, tool_name)
|
|
503
|
+
|
|
504
|
+
def should_inject_mcp_schema(self, server_name: str, tool_name: str) -> bool:
|
|
505
|
+
"""True after repeated validation failures (inject full schema to help the LLM)."""
|
|
506
|
+
return self._mcp_validation.should_inject_schema(server_name, tool_name)
|
|
507
|
+
|
|
508
|
+
def clear_mcp_validation_failures(self, server_name: str, tool_name: str) -> None:
|
|
509
|
+
"""Clear validation failure count after successful execution."""
|
|
510
|
+
self._mcp_validation.clear_failures(server_name, tool_name)
|
|
511
|
+
|