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,809 @@
|
|
|
1
|
+
"""Context retriever for phase-based and semantic memory injection.
|
|
2
|
+
|
|
3
|
+
The ContextRetriever provides phase-filtered context for injection into
|
|
4
|
+
the agent's prompt using H-MEM position-indexed routing.
|
|
5
|
+
|
|
6
|
+
Key Enhancement (Phase 1):
|
|
7
|
+
Position-indexed lookup by phase name enables efficient hierarchical navigation.
|
|
8
|
+
phase_index[phase_name] → position → phase_memories[position]
|
|
9
|
+
|
|
10
|
+
Expected token usage:
|
|
11
|
+
- Session summary: ~100 tokens
|
|
12
|
+
- Current phase: ~300 tokens
|
|
13
|
+
- Semantic matches: ~100 tokens (cross-phase)
|
|
14
|
+
- Previous phases summary: ~100 tokens
|
|
15
|
+
- Recent steps: ~200 tokens
|
|
16
|
+
Total: ~700-900 tokens adaptive (based on semantic relevance)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Optional, List, Dict, Any
|
|
21
|
+
from .hierarchical_memory import HierarchicalMemory, PhaseMemory, Step
|
|
22
|
+
from modules.llm import count_tokens
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ContextRetriever:
|
|
28
|
+
"""Retrieves phase-based and semantic context for injection into agent prompts.
|
|
29
|
+
|
|
30
|
+
The ContextRetriever formats hierarchical memory into a concise context
|
|
31
|
+
string suitable for injection. It uses position-indexed lookup for efficient
|
|
32
|
+
access and supports cross-phase semantic search.
|
|
33
|
+
|
|
34
|
+
Enhancement (Phase 1):
|
|
35
|
+
Uses position indices for O(1) phase access instead of dict iteration.
|
|
36
|
+
phase_index mapping enables backward compatibility.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
memory: HierarchicalMemory instance
|
|
40
|
+
max_findings_per_phase: Maximum findings to include per phase
|
|
41
|
+
max_recent_steps: Maximum recent steps to include
|
|
42
|
+
include_completed_phases: Whether to include completed phases summary
|
|
43
|
+
semantic_retriever: Optional SemanticRetriever for cross-phase search
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
memory: HierarchicalMemory,
|
|
49
|
+
max_findings_per_phase: int = 15, # PHASE 1 FIX (Nov 4): Increased from 5 to 15
|
|
50
|
+
max_recent_steps: int = 50, # PHASE 1 FIX (Nov 4): Increased from 20 to 50 (show 50% of tracked 100)
|
|
51
|
+
include_completed_phases: bool = True,
|
|
52
|
+
semantic_retriever: Optional[Any] = None, # Still optional for backward compat
|
|
53
|
+
semantic_top_k: int = 50, # PHASE 1 FIX (Nov 4): Increased from 10 to 50 for better context
|
|
54
|
+
enable_cross_phase_search: bool = True, # Phase 2 feature flag
|
|
55
|
+
compress_recent_steps: bool = True # NEW (Jan 2026): Compress recent steps to save ~4K tokens
|
|
56
|
+
):
|
|
57
|
+
"""Initialize context retriever with MASSIVELY expanded H-MEM context window.
|
|
58
|
+
|
|
59
|
+
PHASE 1 FIX (Nov 4, 2025): Dramatically expanded for 200-1000+ step tasks!
|
|
60
|
+
|
|
61
|
+
Context Budget (SCALED FOR LONG TASKS):
|
|
62
|
+
- HierarchicalMemory NOW tracks 100 recent steps (hierarchical_memory.py:561)
|
|
63
|
+
- Shows 50 most recent steps (50% of tracked, covers last ~25-250 actions)
|
|
64
|
+
- More findings per phase: 15 (up from 5, 3x increase)
|
|
65
|
+
- More semantic matches: 50 (up from 10, 5x increase)
|
|
66
|
+
|
|
67
|
+
Token Impact:
|
|
68
|
+
- Before Phase 1: ~600-900 tokens (20 steps, 5 findings, 10 semantic)
|
|
69
|
+
- After Phase 1: ~4,000-6,000 tokens (50 steps, 15 findings, 50 semantic)
|
|
70
|
+
- Still <0.6% of 1M context window (5K tokens / 1M = 0.5%)
|
|
71
|
+
|
|
72
|
+
Rationale for 200-1000 Step Tasks:
|
|
73
|
+
- Complex tasks run 200-1000+ steps over hours
|
|
74
|
+
- Previous 20-step tracking = 2-10% coverage (SEVERE context loss!)
|
|
75
|
+
- New 100-step tracking + 50-step display = 10-50% coverage
|
|
76
|
+
- With 1M window: 6K tokens for H-MEM is TINY (0.6%), huge value
|
|
77
|
+
|
|
78
|
+
Why Not More?
|
|
79
|
+
- 100 tracked steps is sweet spot (memory vs coverage)
|
|
80
|
+
- 50 displayed steps balances detail vs noise
|
|
81
|
+
- Older context lives in phase summaries + semantic search
|
|
82
|
+
- Can increase further if needed (monitor utilization)
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
memory: HierarchicalMemory instance
|
|
86
|
+
max_findings_per_phase: Max findings to show per phase (default 15, was 5)
|
|
87
|
+
max_recent_steps: Max recent steps to show (default 50, was 20)
|
|
88
|
+
include_completed_phases: Include completed phases summary (default True)
|
|
89
|
+
semantic_retriever: Optional SemanticRetriever (recommended)
|
|
90
|
+
semantic_top_k: Number of semantic matches to retrieve (default 50, was 10)
|
|
91
|
+
enable_cross_phase_search: Enable hierarchical cross-phase search (default True)
|
|
92
|
+
"""
|
|
93
|
+
self.memory = memory
|
|
94
|
+
self.max_findings_per_phase = max_findings_per_phase
|
|
95
|
+
self.max_recent_steps = max_recent_steps
|
|
96
|
+
self.include_completed_phases = include_completed_phases
|
|
97
|
+
self.semantic_retriever = semantic_retriever
|
|
98
|
+
self.semantic_top_k = semantic_top_k
|
|
99
|
+
self.enable_cross_phase_search = enable_cross_phase_search
|
|
100
|
+
self.compress_recent_steps = compress_recent_steps # NEW (Jan 2026)
|
|
101
|
+
|
|
102
|
+
# FIX #5: Explicit warning when semantic search is disabled
|
|
103
|
+
if not semantic_retriever:
|
|
104
|
+
if enable_cross_phase_search:
|
|
105
|
+
logger.warning(
|
|
106
|
+
"⚠️ H-MEM SEMANTIC SEARCH DISABLED: SemanticRetriever not provided. "
|
|
107
|
+
"Cross-phase search will be limited to exact matches. "
|
|
108
|
+
"For best context retrieval, ensure the local embedding model is configured."
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
logger.info(
|
|
112
|
+
"ℹ️ Semantic search disabled by configuration (enable_cross_phase_search=False)."
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
logger.info(
|
|
116
|
+
f"✅ Semantic search enabled: cross-phase queries will use vector similarity "
|
|
117
|
+
f"(top_k={semantic_top_k})"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
logger.debug(
|
|
121
|
+
f"ContextRetriever initialized for session {memory.session_id} "
|
|
122
|
+
f"(semantic={'enabled' if semantic_retriever else 'disabled'}, "
|
|
123
|
+
f"cross_phase={'enabled' if enable_cross_phase_search else 'disabled'})"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def hierarchical_search(
|
|
127
|
+
self,
|
|
128
|
+
query: str,
|
|
129
|
+
top_k_phases: int = 2,
|
|
130
|
+
top_k_findings_per_phase: int = 3
|
|
131
|
+
) -> List[tuple]:
|
|
132
|
+
"""H-MEM hierarchical retrieval: Progressive layer-by-layer navigation.
|
|
133
|
+
|
|
134
|
+
Implements the core H-MEM algorithm from Section 3.2:
|
|
135
|
+
1. Semantic vector encoding of query
|
|
136
|
+
2. Similarity with Phase Layer -> top-k phases
|
|
137
|
+
3. For each selected phase: similarity with findings -> top-k findings
|
|
138
|
+
|
|
139
|
+
This enables efficient filtering without exhaustive search.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
query: Query text to search for
|
|
143
|
+
top_k_phases: Number of most relevant phases to select (default 2)
|
|
144
|
+
top_k_findings_per_phase: Findings to retrieve per phase (default 3)
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
List of (finding, phase_name, score) tuples, sorted by score
|
|
148
|
+
"""
|
|
149
|
+
if not self.semantic_retriever:
|
|
150
|
+
logger.warning("⚠️ Hierarchical search requires semantic_retriever")
|
|
151
|
+
return []
|
|
152
|
+
|
|
153
|
+
if not self.memory.phase_memories:
|
|
154
|
+
return []
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
# Step 1: Embed the query
|
|
158
|
+
from .semantic_retriever import SemanticRetriever
|
|
159
|
+
query_embedding = self.semantic_retriever.embed_text(query)
|
|
160
|
+
if query_embedding is None:
|
|
161
|
+
return []
|
|
162
|
+
|
|
163
|
+
# Step 2: Score all phases using position indices
|
|
164
|
+
phase_scores = []
|
|
165
|
+
for idx, phase_memory in enumerate(self.memory.phase_memories):
|
|
166
|
+
if not phase_memory.finding_embeddings:
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
# Calculate average similarity to phase (aggregate of findings)
|
|
170
|
+
similarities = []
|
|
171
|
+
for finding_emb in phase_memory.finding_embeddings:
|
|
172
|
+
if finding_emb and len(finding_emb) > 0:
|
|
173
|
+
import numpy as np
|
|
174
|
+
finding_emb_np = np.array(finding_emb)
|
|
175
|
+
sim = SemanticRetriever.cosine_similarity(
|
|
176
|
+
query_embedding,
|
|
177
|
+
finding_emb_np
|
|
178
|
+
)
|
|
179
|
+
similarities.append(sim)
|
|
180
|
+
|
|
181
|
+
if similarities:
|
|
182
|
+
avg_similarity = sum(similarities) / len(similarities)
|
|
183
|
+
phase_scores.append((idx, phase_memory.phase_name, avg_similarity))
|
|
184
|
+
|
|
185
|
+
# Sort phases by similarity
|
|
186
|
+
phase_scores.sort(key=lambda x: x[2], reverse=True)
|
|
187
|
+
|
|
188
|
+
# Step 3: Select top-k phases
|
|
189
|
+
top_phases = phase_scores[:top_k_phases]
|
|
190
|
+
|
|
191
|
+
logger.debug(
|
|
192
|
+
f"🔍 H-MEM Layer 2: Selected {len(top_phases)} phases from {len(phase_scores)} "
|
|
193
|
+
f"(top: {top_phases[0][1] if top_phases else 'none'})"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Step 4: For each selected phase, find top-k findings using position indices
|
|
197
|
+
all_findings = []
|
|
198
|
+
for phase_idx, phase_name, phase_score in top_phases:
|
|
199
|
+
phase_memory = self.memory.phase_memories[phase_idx]
|
|
200
|
+
|
|
201
|
+
# Use sub_memory_indices if available (H-MEM optimization)
|
|
202
|
+
if phase_memory.sub_memory_indices:
|
|
203
|
+
# Only search indexed findings
|
|
204
|
+
search_indices = phase_memory.sub_memory_indices
|
|
205
|
+
logger.debug(
|
|
206
|
+
f"🎯 Using position indices: {len(search_indices)} of "
|
|
207
|
+
f"{len(phase_memory.key_findings)} findings"
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
# Fallback: search all findings
|
|
211
|
+
search_indices = list(range(len(phase_memory.key_findings)))
|
|
212
|
+
|
|
213
|
+
finding_scores = []
|
|
214
|
+
for finding_idx in search_indices:
|
|
215
|
+
if finding_idx >= len(phase_memory.key_findings):
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
finding = phase_memory.key_findings[finding_idx]
|
|
219
|
+
if finding_idx < len(phase_memory.finding_embeddings):
|
|
220
|
+
finding_emb = phase_memory.finding_embeddings[finding_idx]
|
|
221
|
+
if finding_emb and len(finding_emb) > 0:
|
|
222
|
+
import numpy as np
|
|
223
|
+
finding_emb_np = np.array(finding_emb)
|
|
224
|
+
sim = SemanticRetriever.cosine_similarity(
|
|
225
|
+
query_embedding,
|
|
226
|
+
finding_emb_np
|
|
227
|
+
)
|
|
228
|
+
finding_scores.append((finding, phase_name, sim))
|
|
229
|
+
|
|
230
|
+
# Sort findings by similarity
|
|
231
|
+
finding_scores.sort(key=lambda x: x[2], reverse=True)
|
|
232
|
+
|
|
233
|
+
# Take top-k findings from this phase
|
|
234
|
+
top_findings = finding_scores[:top_k_findings_per_phase]
|
|
235
|
+
all_findings.extend(top_findings)
|
|
236
|
+
|
|
237
|
+
# FIX #4: Correct f-string conditional syntax
|
|
238
|
+
best_score = top_findings[0][2] if top_findings else 0
|
|
239
|
+
logger.debug(
|
|
240
|
+
f"📍 Phase '{phase_name}': {len(top_findings)} findings "
|
|
241
|
+
f"(best: {best_score:.2f})"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Sort all findings globally by score
|
|
245
|
+
all_findings.sort(key=lambda x: x[2], reverse=True)
|
|
246
|
+
|
|
247
|
+
logger.info(
|
|
248
|
+
f"✅ H-MEM hierarchical search: {len(all_findings)} findings "
|
|
249
|
+
f"from {len(top_phases)} phases"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
return all_findings
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
logger.error(f"❌ Hierarchical search failed: {e}")
|
|
256
|
+
return []
|
|
257
|
+
|
|
258
|
+
def get_context_injection(self, current_phase: Optional[str] = None, brain_state: Optional[Dict[str, Any]] = None) -> str:
|
|
259
|
+
"""Get formatted context for injection (phase-filtered).
|
|
260
|
+
|
|
261
|
+
This is the main method - returns ~600-750 token context string with
|
|
262
|
+
only relevant memories for current phase, plus semantically similar
|
|
263
|
+
findings from other phases.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
current_phase: Phase to get context for (if None, uses memory.current_phase)
|
|
267
|
+
brain_state: Optional brain state for semantic query extraction
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
Formatted context string ready for injection
|
|
271
|
+
"""
|
|
272
|
+
phase = current_phase or self.memory.current_phase
|
|
273
|
+
|
|
274
|
+
sections = []
|
|
275
|
+
|
|
276
|
+
# Layer 1: Session summary (~100 tokens)
|
|
277
|
+
sections.append(self._format_session_summary())
|
|
278
|
+
|
|
279
|
+
# Layer 2: Current phase memory (~300 tokens)
|
|
280
|
+
current_phase_context = self._format_current_phase(phase)
|
|
281
|
+
if current_phase_context:
|
|
282
|
+
sections.append(current_phase_context)
|
|
283
|
+
|
|
284
|
+
# Semantic search across all phases (~100-200 tokens) - Phase 2 enhancement
|
|
285
|
+
if self.enable_cross_phase_search and self.semantic_retriever and brain_state:
|
|
286
|
+
semantic_context = self._format_semantic_matches_hierarchical(brain_state, phase)
|
|
287
|
+
if not semantic_context:
|
|
288
|
+
# Hierarchical (embedding) path produced nothing — happens when no embedder
|
|
289
|
+
# is available (e.g. LexicalRetriever.embed_text returns None). Fall back to
|
|
290
|
+
# the flat search path so section-3 still works via lexical retrieval.
|
|
291
|
+
semantic_context = self._format_semantic_matches(brain_state, phase)
|
|
292
|
+
if semantic_context:
|
|
293
|
+
sections.append(semantic_context)
|
|
294
|
+
elif self.semantic_retriever and brain_state:
|
|
295
|
+
# Fallback to old method if cross_phase disabled
|
|
296
|
+
semantic_context = self._format_semantic_matches(brain_state, phase)
|
|
297
|
+
if semantic_context:
|
|
298
|
+
sections.append(semantic_context)
|
|
299
|
+
|
|
300
|
+
# Completed phases summary (~100 tokens) - BEFORE recent steps
|
|
301
|
+
if self.include_completed_phases:
|
|
302
|
+
completed_context = self._format_completed_phases()
|
|
303
|
+
if completed_context:
|
|
304
|
+
sections.append(completed_context)
|
|
305
|
+
|
|
306
|
+
# Layer 3: Recent steps (compressed: ~200 tokens, full: ~5000 tokens)
|
|
307
|
+
# OPTIMIZATION (Jan 2026): Use compressed format to save ~4000 tokens
|
|
308
|
+
recent_context = self._format_recent_steps(compress=self.compress_recent_steps)
|
|
309
|
+
if recent_context:
|
|
310
|
+
sections.append(recent_context)
|
|
311
|
+
|
|
312
|
+
return "\n\n".join(sections)
|
|
313
|
+
|
|
314
|
+
def _format_session_summary(self) -> str:
|
|
315
|
+
"""Format Layer 1: Session summary.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Formatted session summary string
|
|
319
|
+
"""
|
|
320
|
+
lines = [
|
|
321
|
+
"[HIERARCHICAL MEMORY - SESSION CONTEXT]",
|
|
322
|
+
"",
|
|
323
|
+
f"Session: {self.memory.session_id}",
|
|
324
|
+
f"Task: {self.memory.task}",
|
|
325
|
+
f"Progress: {self.memory.progress}",
|
|
326
|
+
f"Current Phase: {self.memory.current_phase}"
|
|
327
|
+
]
|
|
328
|
+
|
|
329
|
+
return "\n".join(lines)
|
|
330
|
+
|
|
331
|
+
def _format_current_phase(self, phase_name: str) -> Optional[str]:
|
|
332
|
+
"""Format Layer 2: Current phase memory with importance-weighted retrieval.
|
|
333
|
+
|
|
334
|
+
OPTIMIZATION (Nov 14, 2025): Smart finding selection
|
|
335
|
+
- Ranks findings by importance (not just recency)
|
|
336
|
+
- Shows most relevant findings first
|
|
337
|
+
- Better context quality
|
|
338
|
+
|
|
339
|
+
This is the KEY benefit - only inject current phase, not all phases!
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
phase_name: Phase to format
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
Formatted phase context, or None if phase doesn't exist
|
|
346
|
+
"""
|
|
347
|
+
# Position-indexed lookup via helper method
|
|
348
|
+
phase_memory = self.memory.get_phase_by_name(phase_name)
|
|
349
|
+
|
|
350
|
+
if not phase_memory:
|
|
351
|
+
return None
|
|
352
|
+
|
|
353
|
+
lines = [
|
|
354
|
+
f"[CURRENT PHASE: {phase_name.upper()}]",
|
|
355
|
+
""
|
|
356
|
+
]
|
|
357
|
+
|
|
358
|
+
# Phase summary
|
|
359
|
+
if phase_memory.summary:
|
|
360
|
+
lines.append(f"Summary: {phase_memory.summary}")
|
|
361
|
+
lines.append("")
|
|
362
|
+
|
|
363
|
+
# OPTIMIZATION: Smart finding selection by importance
|
|
364
|
+
if phase_memory.key_findings:
|
|
365
|
+
# Calculate importance scores for all findings
|
|
366
|
+
finding_scores = []
|
|
367
|
+
for i in range(len(phase_memory.key_findings)):
|
|
368
|
+
# Use existing importance or calculate it
|
|
369
|
+
if i < len(phase_memory.finding_importance):
|
|
370
|
+
importance = phase_memory.finding_importance[i]
|
|
371
|
+
else:
|
|
372
|
+
# Fallback: recency-based importance
|
|
373
|
+
recency_score = (i + 1) / len(phase_memory.key_findings)
|
|
374
|
+
importance = recency_score
|
|
375
|
+
|
|
376
|
+
finding_scores.append((i, importance, phase_memory.key_findings[i]))
|
|
377
|
+
|
|
378
|
+
# Sort by importance (descending)
|
|
379
|
+
finding_scores.sort(key=lambda x: x[1], reverse=True)
|
|
380
|
+
|
|
381
|
+
# Take top N by importance (not just last N)
|
|
382
|
+
top_findings = finding_scores[:self.max_findings_per_phase]
|
|
383
|
+
|
|
384
|
+
# Re-sort by index to maintain chronological order
|
|
385
|
+
top_findings.sort(key=lambda x: x[0])
|
|
386
|
+
|
|
387
|
+
lines.append(
|
|
388
|
+
f"Key Findings (top {len(top_findings)} by importance of {len(phase_memory.key_findings)} total):"
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
for rank, (idx, score, finding) in enumerate(top_findings, 1):
|
|
392
|
+
# FIX #17: Update access tracking when finding is retrieved
|
|
393
|
+
# This improves importance scoring for future retrievals
|
|
394
|
+
phase_memory.update_access(idx)
|
|
395
|
+
|
|
396
|
+
# Show importance indicator
|
|
397
|
+
importance_icon = "⭐" if score > 0.8 else "•"
|
|
398
|
+
lines.append(f" {importance_icon} {finding}")
|
|
399
|
+
|
|
400
|
+
return "\n".join(lines)
|
|
401
|
+
|
|
402
|
+
def _format_semantic_matches_hierarchical(
|
|
403
|
+
self,
|
|
404
|
+
brain_state: Dict[str, Any],
|
|
405
|
+
current_phase: str
|
|
406
|
+
) -> Optional[str]:
|
|
407
|
+
"""Format semantically similar findings using H-MEM hierarchical search (Phase 2).
|
|
408
|
+
|
|
409
|
+
Uses hierarchical retrieval to efficiently find relevant findings across
|
|
410
|
+
all phases using position indices.
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
brain_state: Brain state with 'memory', 'next', 'reasoning' fields
|
|
414
|
+
current_phase: Current phase name (for reference)
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
Formatted semantic matches string, or None if no matches
|
|
418
|
+
"""
|
|
419
|
+
try:
|
|
420
|
+
# Extract query from brain state
|
|
421
|
+
query_parts = []
|
|
422
|
+
if brain_state.get('memory'):
|
|
423
|
+
query_parts.append(brain_state['memory'])
|
|
424
|
+
if brain_state.get('next'):
|
|
425
|
+
query_parts.append(brain_state['next'])
|
|
426
|
+
if brain_state.get('reasoning'):
|
|
427
|
+
query_parts.append(brain_state['reasoning'])
|
|
428
|
+
|
|
429
|
+
if not query_parts:
|
|
430
|
+
return None
|
|
431
|
+
|
|
432
|
+
query = " ".join(query_parts)
|
|
433
|
+
|
|
434
|
+
# Use hierarchical search (H-MEM Phase 2)
|
|
435
|
+
matches = self.hierarchical_search(
|
|
436
|
+
query=query,
|
|
437
|
+
top_k_phases=2, # Search top 2 most relevant phases
|
|
438
|
+
top_k_findings_per_phase=3 # Get 3 findings per phase
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
if not matches:
|
|
442
|
+
return None
|
|
443
|
+
|
|
444
|
+
# Filter out findings from current phase (optional - may want to keep them)
|
|
445
|
+
# For now, show ALL matches including current phase for better context
|
|
446
|
+
# cross_phase_matches = [
|
|
447
|
+
# (finding, phase, score)
|
|
448
|
+
# for finding, phase, score in matches
|
|
449
|
+
# if phase != current_phase
|
|
450
|
+
# ]
|
|
451
|
+
|
|
452
|
+
# Format matches (including current phase)
|
|
453
|
+
lines = [
|
|
454
|
+
"[SEMANTICALLY RELATED MEMORIES (H-MEM Hierarchical Search)]",
|
|
455
|
+
""
|
|
456
|
+
]
|
|
457
|
+
|
|
458
|
+
for finding, phase, score in matches[:self.semantic_top_k]:
|
|
459
|
+
# Format: [phase] finding (similarity%)
|
|
460
|
+
similarity_pct = int(score * 100)
|
|
461
|
+
phase_marker = "📍" if phase == current_phase else "🔗"
|
|
462
|
+
lines.append(f"{phase_marker} [{phase}] {finding} ({similarity_pct}% match)")
|
|
463
|
+
|
|
464
|
+
logger.debug(
|
|
465
|
+
f"🔍 Hierarchical search: {len(matches)} findings "
|
|
466
|
+
f"({sum(1 for _, p, _ in matches if p != current_phase)} cross-phase)"
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
return "\n".join(lines)
|
|
470
|
+
|
|
471
|
+
except Exception as e:
|
|
472
|
+
logger.warning(f"⚠️ Hierarchical semantic search failed: {e}")
|
|
473
|
+
return None
|
|
474
|
+
|
|
475
|
+
def _format_semantic_matches(self, brain_state: Dict[str, Any], current_phase: str) -> Optional[str]:
|
|
476
|
+
"""Format semantically similar findings from other phases (legacy method).
|
|
477
|
+
|
|
478
|
+
Uses flat semantic search across all findings. This is the fallback
|
|
479
|
+
when hierarchical search is disabled.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
brain_state: Brain state with 'memory', 'next', 'reasoning' fields
|
|
483
|
+
current_phase: Current phase name (to exclude from results)
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
Formatted semantic matches string, or None if no matches
|
|
487
|
+
"""
|
|
488
|
+
try:
|
|
489
|
+
# Extract query from brain state
|
|
490
|
+
# Combine memory, next action, and reasoning for context
|
|
491
|
+
query_parts = []
|
|
492
|
+
if brain_state.get('memory'):
|
|
493
|
+
query_parts.append(brain_state['memory'])
|
|
494
|
+
if brain_state.get('next'):
|
|
495
|
+
query_parts.append(brain_state['next'])
|
|
496
|
+
if brain_state.get('reasoning'):
|
|
497
|
+
query_parts.append(brain_state['reasoning'])
|
|
498
|
+
|
|
499
|
+
if not query_parts:
|
|
500
|
+
return None
|
|
501
|
+
|
|
502
|
+
query = " ".join(query_parts)
|
|
503
|
+
|
|
504
|
+
# Get findings for search. Prefer the pre-embedded set (faster for
|
|
505
|
+
# SemanticRetriever); fall back to all findings so that LexicalRetriever
|
|
506
|
+
# (which does not need pre-stored embeddings) can still search.
|
|
507
|
+
findings_dict = self.memory.get_all_findings_with_embeddings()
|
|
508
|
+
if not findings_dict:
|
|
509
|
+
findings_dict = {
|
|
510
|
+
pm.phase_name: pm.key_findings[:]
|
|
511
|
+
for pm in self.memory.phase_memories
|
|
512
|
+
if pm.key_findings
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if not findings_dict:
|
|
516
|
+
return None
|
|
517
|
+
|
|
518
|
+
# Search for semantic matches
|
|
519
|
+
matches = self.semantic_retriever.search_similar(
|
|
520
|
+
query=query,
|
|
521
|
+
findings=findings_dict,
|
|
522
|
+
top_k=self.semantic_top_k
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
if not matches:
|
|
526
|
+
return None
|
|
527
|
+
|
|
528
|
+
# Filter out findings from current phase (already shown)
|
|
529
|
+
cross_phase_matches = [
|
|
530
|
+
(finding, phase, score)
|
|
531
|
+
for finding, phase, score in matches
|
|
532
|
+
if phase != current_phase
|
|
533
|
+
]
|
|
534
|
+
|
|
535
|
+
if not cross_phase_matches:
|
|
536
|
+
return None
|
|
537
|
+
|
|
538
|
+
# Format matches
|
|
539
|
+
lines = [
|
|
540
|
+
"[SEMANTICALLY RELATED FROM OTHER PHASES]",
|
|
541
|
+
""
|
|
542
|
+
]
|
|
543
|
+
|
|
544
|
+
for finding, phase, score in cross_phase_matches:
|
|
545
|
+
# Format: [phase] finding (similarity%)
|
|
546
|
+
similarity_pct = int(score * 100)
|
|
547
|
+
lines.append(f"• [{phase}] {finding} ({similarity_pct}% match)")
|
|
548
|
+
|
|
549
|
+
logger.debug(f"🔍 Found {len(cross_phase_matches)} semantic matches across phases")
|
|
550
|
+
|
|
551
|
+
return "\n".join(lines)
|
|
552
|
+
|
|
553
|
+
except Exception as e:
|
|
554
|
+
logger.warning(f"⚠️ Semantic search failed: {e}")
|
|
555
|
+
return None
|
|
556
|
+
|
|
557
|
+
def _format_completed_phases(self) -> Optional[str]:
|
|
558
|
+
"""Format completed phases summary.
|
|
559
|
+
|
|
560
|
+
Brief summary of what was accomplished in earlier phases.
|
|
561
|
+
|
|
562
|
+
Returns:
|
|
563
|
+
Formatted completed phases string, or None if no completed phases
|
|
564
|
+
"""
|
|
565
|
+
if not self.memory.phases_completed:
|
|
566
|
+
return None
|
|
567
|
+
|
|
568
|
+
lines = [
|
|
569
|
+
"[COMPLETED PHASES]",
|
|
570
|
+
""
|
|
571
|
+
]
|
|
572
|
+
|
|
573
|
+
for phase_name in self.memory.phases_completed:
|
|
574
|
+
phase_memory = self.memory.get_phase_by_name(phase_name)
|
|
575
|
+
if phase_memory:
|
|
576
|
+
findings_count = len(phase_memory.key_findings)
|
|
577
|
+
lines.append(
|
|
578
|
+
f"✓ {phase_name}: {phase_memory.summary} "
|
|
579
|
+
f"({findings_count} findings)"
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
return "\n".join(lines)
|
|
583
|
+
|
|
584
|
+
def _format_recent_steps(self, compress: bool = False) -> Optional[str]:
|
|
585
|
+
"""Format Layer 3: Recent steps with optional compression.
|
|
586
|
+
|
|
587
|
+
Shows most recent actions across all phases (helps with continuity).
|
|
588
|
+
|
|
589
|
+
OPTIMIZATION (Jan 2026): When compress=True, groups steps by phase
|
|
590
|
+
for 5-10x token reduction (5000 tokens -> 500 tokens).
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
compress: If True, use compressed format grouping by phase
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
Formatted recent steps string, or None if no steps
|
|
597
|
+
"""
|
|
598
|
+
if not self.memory.recent_steps:
|
|
599
|
+
return None
|
|
600
|
+
|
|
601
|
+
# Get most recent N steps
|
|
602
|
+
steps_to_show = self.memory.recent_steps[-self.max_recent_steps:]
|
|
603
|
+
|
|
604
|
+
if not steps_to_show:
|
|
605
|
+
return None
|
|
606
|
+
|
|
607
|
+
# Use compressed format if requested and many steps
|
|
608
|
+
if compress and len(steps_to_show) > 10:
|
|
609
|
+
return self._format_recent_steps_compressed(steps_to_show)
|
|
610
|
+
|
|
611
|
+
# Standard detailed format
|
|
612
|
+
lines = [
|
|
613
|
+
f"[RECENT ACTIVITY - Last {len(steps_to_show)} steps]",
|
|
614
|
+
""
|
|
615
|
+
]
|
|
616
|
+
|
|
617
|
+
for step in steps_to_show:
|
|
618
|
+
# Format: [Step #] phase: action
|
|
619
|
+
line = f"[{step.step}] {step.phase}: {step.action_summary}"
|
|
620
|
+
lines.append(line)
|
|
621
|
+
|
|
622
|
+
# Include finding if present (indented)
|
|
623
|
+
if step.finding:
|
|
624
|
+
lines.append(f" → {step.finding}")
|
|
625
|
+
|
|
626
|
+
return "\n".join(lines)
|
|
627
|
+
|
|
628
|
+
def _format_recent_steps_compressed(self, steps: list) -> str:
|
|
629
|
+
"""Compressed recent steps format - groups by phase.
|
|
630
|
+
|
|
631
|
+
Reduces tokens by ~10x while preserving key information.
|
|
632
|
+
|
|
633
|
+
Instead of:
|
|
634
|
+
[1] discovery: Searched google
|
|
635
|
+
[2] discovery: Clicked result
|
|
636
|
+
[3] collection: Extracted data
|
|
637
|
+
...
|
|
638
|
+
|
|
639
|
+
Produces:
|
|
640
|
+
[RECENT: 50 steps]
|
|
641
|
+
steps 1-15 (discovery): searched, clicked, navigated to 5 sites
|
|
642
|
+
steps 16-40 (collection): extracted 8 pages, saved to files
|
|
643
|
+
steps 41-50 (processing): analyzed data, created summary
|
|
644
|
+
|
|
645
|
+
Args:
|
|
646
|
+
steps: List of Step objects to format
|
|
647
|
+
|
|
648
|
+
Returns:
|
|
649
|
+
Compressed recent steps string
|
|
650
|
+
"""
|
|
651
|
+
from collections import defaultdict
|
|
652
|
+
|
|
653
|
+
# Group steps by phase with step ranges
|
|
654
|
+
phase_ranges = defaultdict(lambda: {"start": float('inf'), "end": 0, "actions": set(), "count": 0})
|
|
655
|
+
|
|
656
|
+
for step in steps:
|
|
657
|
+
phase = step.phase
|
|
658
|
+
phase_ranges[phase]["start"] = min(phase_ranges[phase]["start"], step.step)
|
|
659
|
+
phase_ranges[phase]["end"] = max(phase_ranges[phase]["end"], step.step)
|
|
660
|
+
phase_ranges[phase]["count"] += 1
|
|
661
|
+
|
|
662
|
+
# Extract key action (first word or phrase)
|
|
663
|
+
action_key = step.action_summary[:40].split(',')[0].strip()
|
|
664
|
+
if action_key:
|
|
665
|
+
phase_ranges[phase]["actions"].add(action_key)
|
|
666
|
+
|
|
667
|
+
lines = [f"[RECENT: {len(steps)} steps grouped by phase]", ""]
|
|
668
|
+
|
|
669
|
+
# Sort phases by start step
|
|
670
|
+
sorted_phases = sorted(phase_ranges.items(), key=lambda x: x[1]["start"])
|
|
671
|
+
|
|
672
|
+
for phase, data in sorted_phases:
|
|
673
|
+
step_range = f"steps {data['start']}-{data['end']}" if data['start'] != data['end'] else f"step {data['start']}"
|
|
674
|
+
# Take first 5 unique actions
|
|
675
|
+
action_samples = list(data["actions"])[:5]
|
|
676
|
+
actions_str = ", ".join(action_samples)
|
|
677
|
+
if len(data["actions"]) > 5:
|
|
678
|
+
actions_str += f"... (+{len(data['actions']) - 5} more)"
|
|
679
|
+
|
|
680
|
+
lines.append(f"• {step_range} ({phase}, {data['count']} steps): {actions_str}")
|
|
681
|
+
|
|
682
|
+
return "\n".join(lines)
|
|
683
|
+
|
|
684
|
+
def get_phase_context(self, phase_name: str) -> Optional[str]:
|
|
685
|
+
"""Get context for a specific phase (not current).
|
|
686
|
+
|
|
687
|
+
Useful for reviewing past phases or cross-phase analysis.
|
|
688
|
+
|
|
689
|
+
Args:
|
|
690
|
+
phase_name: Phase to get context for
|
|
691
|
+
|
|
692
|
+
Returns:
|
|
693
|
+
Formatted phase context, or None if phase doesn't exist
|
|
694
|
+
"""
|
|
695
|
+
return self._format_current_phase(phase_name)
|
|
696
|
+
|
|
697
|
+
def get_all_findings(self, phase_name: Optional[str] = None) -> List[str]:
|
|
698
|
+
"""Get all findings for a phase (or all phases) - position-indexed.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
phase_name: Phase to get findings for (if None, returns all)
|
|
702
|
+
|
|
703
|
+
Returns:
|
|
704
|
+
List of finding strings
|
|
705
|
+
"""
|
|
706
|
+
if phase_name:
|
|
707
|
+
phase_memory = self.memory.get_phase_by_name(phase_name)
|
|
708
|
+
return phase_memory.key_findings if phase_memory else []
|
|
709
|
+
|
|
710
|
+
# All phases - iterate over list
|
|
711
|
+
all_findings = []
|
|
712
|
+
for phase_memory in self.memory.phase_memories:
|
|
713
|
+
all_findings.extend(phase_memory.key_findings)
|
|
714
|
+
|
|
715
|
+
return all_findings
|
|
716
|
+
|
|
717
|
+
def get_context_token_estimate(self) -> int:
|
|
718
|
+
"""Estimate token count for context injection.
|
|
719
|
+
|
|
720
|
+
Uses rough approximation: ~4 characters per token.
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
Estimated token count
|
|
724
|
+
"""
|
|
725
|
+
context = self.get_context_injection()
|
|
726
|
+
# Use generic model name - will fall back to character-based estimation
|
|
727
|
+
return count_tokens(context, "default")
|
|
728
|
+
|
|
729
|
+
def get_context_statistics(self) -> Dict[str, Any]:
|
|
730
|
+
"""Get statistics about context.
|
|
731
|
+
|
|
732
|
+
Returns:
|
|
733
|
+
Dictionary with context statistics
|
|
734
|
+
"""
|
|
735
|
+
context = self.get_context_injection()
|
|
736
|
+
|
|
737
|
+
return {
|
|
738
|
+
"total_phases": len(self.memory.phase_memories),
|
|
739
|
+
"completed_phases": len(self.memory.phases_completed),
|
|
740
|
+
"current_phase": self.memory.current_phase,
|
|
741
|
+
"total_findings": sum(
|
|
742
|
+
len(pm.key_findings)
|
|
743
|
+
for pm in self.memory.phase_memories # Now a list
|
|
744
|
+
),
|
|
745
|
+
"recent_steps_count": len(self.memory.recent_steps),
|
|
746
|
+
"context_length_chars": len(context),
|
|
747
|
+
"context_length_tokens_estimate": len(context) // 4
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
def format_for_display(self, include_steps: bool = True) -> str:
|
|
751
|
+
"""Format memory for human-readable display.
|
|
752
|
+
|
|
753
|
+
Args:
|
|
754
|
+
include_steps: Include recent steps (default True)
|
|
755
|
+
|
|
756
|
+
Returns:
|
|
757
|
+
Formatted string for display/debugging
|
|
758
|
+
"""
|
|
759
|
+
lines = [
|
|
760
|
+
"=" * 80,
|
|
761
|
+
"HIERARCHICAL MEMORY SNAPSHOT",
|
|
762
|
+
"=" * 80,
|
|
763
|
+
"",
|
|
764
|
+
f"Session: {self.memory.session_id}",
|
|
765
|
+
f"Task: {self.memory.task}",
|
|
766
|
+
f"Progress: {self.memory.progress}",
|
|
767
|
+
"",
|
|
768
|
+
f"Current Phase: {self.memory.current_phase}",
|
|
769
|
+
f"Total Phases: {len(self.memory.phase_memories)}",
|
|
770
|
+
f"Completed: {len(self.memory.phases_completed)}",
|
|
771
|
+
"",
|
|
772
|
+
"-" * 80,
|
|
773
|
+
"PHASES",
|
|
774
|
+
"-" * 80,
|
|
775
|
+
""
|
|
776
|
+
]
|
|
777
|
+
|
|
778
|
+
# Show all phases (position-indexed iteration)
|
|
779
|
+
for phase_memory in self.memory.phase_memories:
|
|
780
|
+
status_icon = "✓" if phase_memory.status == "completed" else "→"
|
|
781
|
+
lines.append(f"{status_icon} {phase_memory.phase_name.upper()}")
|
|
782
|
+
lines.append(f" Steps: {phase_memory.started_step}-{phase_memory.ended_step or '?'}")
|
|
783
|
+
lines.append(f" Summary: {phase_memory.summary or '(none)'}")
|
|
784
|
+
lines.append(f" Findings: {len(phase_memory.key_findings)}")
|
|
785
|
+
# Show sub_memory_indices if present
|
|
786
|
+
if phase_memory.sub_memory_indices:
|
|
787
|
+
lines.append(f" Sub-indices: {phase_memory.sub_memory_indices[:10]}") # First 10
|
|
788
|
+
|
|
789
|
+
if phase_memory.key_findings:
|
|
790
|
+
for finding in phase_memory.key_findings[-3:]: # Last 3
|
|
791
|
+
lines.append(f" • {finding}")
|
|
792
|
+
|
|
793
|
+
lines.append("")
|
|
794
|
+
|
|
795
|
+
if include_steps and self.memory.recent_steps:
|
|
796
|
+
lines.append("-" * 80)
|
|
797
|
+
lines.append("RECENT STEPS")
|
|
798
|
+
lines.append("-" * 80)
|
|
799
|
+
lines.append("")
|
|
800
|
+
|
|
801
|
+
for step in self.memory.recent_steps[-10:]: # Last 10
|
|
802
|
+
lines.append(f"[{step.step}] {step.phase}: {step.action_summary}")
|
|
803
|
+
if step.finding:
|
|
804
|
+
lines.append(f" → {step.finding}")
|
|
805
|
+
|
|
806
|
+
lines.append("")
|
|
807
|
+
lines.append("=" * 80)
|
|
808
|
+
|
|
809
|
+
return "\n".join(lines)
|