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.
Files changed (745) hide show
  1. agents/README.md +453 -0
  2. agents/__init__.py +164 -0
  3. agents/base_agent.py +743 -0
  4. agents/personality/character.py +119 -0
  5. agents/personality/character_manager.py +309 -0
  6. agents/personality/characters/rob.character.json +56 -0
  7. agents/personality/characters/trump.character.json +63 -0
  8. agents/personality/persona_render.py +78 -0
  9. agents/personality/persona_resolver.py +115 -0
  10. agents/prompt/__init__.py +58 -0
  11. agents/prompt/base_prompt.py +92 -0
  12. agents/prompt/system.py +417 -0
  13. agents/task/README_PATH_MANAGEMENT.md +141 -0
  14. agents/task/__init__.py +162 -0
  15. agents/task/agent/__init__.py +70 -0
  16. agents/task/agent/agent_state.py +408 -0
  17. agents/task/agent/async_delegation.py +274 -0
  18. agents/task/agent/autonomy_state.py +275 -0
  19. agents/task/agent/conversation.py +75 -0
  20. agents/task/agent/core/__init__.py +6 -0
  21. agents/task/agent/core/aux_metering.py +61 -0
  22. agents/task/agent/core/background_review.py +157 -0
  23. agents/task/agent/core/construction.py +1179 -0
  24. agents/task/agent/core/conversational_exit.py +49 -0
  25. agents/task/agent/core/correspondent_gate.py +227 -0
  26. agents/task/agent/core/curator.py +246 -0
  27. agents/task/agent/core/episodic_digest.py +103 -0
  28. agents/task/agent/core/error_recovery.py +480 -0
  29. agents/task/agent/core/history_io.py +257 -0
  30. agents/task/agent/core/llm_provisioning.py +287 -0
  31. agents/task/agent/core/llm_runner.py +547 -0
  32. agents/task/agent/core/logging_io.py +276 -0
  33. agents/task/agent/core/loop_detection.py +262 -0
  34. agents/task/agent/core/memory_prefetch.py +325 -0
  35. agents/task/agent/core/memory_writer.py +667 -0
  36. agents/task/agent/core/model_introspection.py +82 -0
  37. agents/task/agent/core/model_swap.py +177 -0
  38. agents/task/agent/core/next_action_internal.py +1331 -0
  39. agents/task/agent/core/output_validation.py +303 -0
  40. agents/task/agent/core/project_context.py +272 -0
  41. agents/task/agent/core/resources.py +93 -0
  42. agents/task/agent/core/result_processing.py +389 -0
  43. agents/task/agent/core/run_loop.py +661 -0
  44. agents/task/agent/core/safety_lifecycle.py +228 -0
  45. agents/task/agent/core/secret_guard.py +293 -0
  46. agents/task/agent/core/self_wake.py +280 -0
  47. agents/task/agent/core/session_metadata.py +104 -0
  48. agents/task/agent/core/step.py +781 -0
  49. agents/task/agent/core/step_execution.py +398 -0
  50. agents/task/agent/core/step_telemetry.py +222 -0
  51. agents/task/agent/core/test_aux_metering.py +50 -0
  52. agents/task/agent/core/test_billing_failover.py +68 -0
  53. agents/task/agent/core/test_fatal_error_classifier.py +38 -0
  54. agents/task/agent/core/test_judge_validation.py +275 -0
  55. agents/task/agent/core/test_model_swap.py +190 -0
  56. agents/task/agent/core/test_reflection_offload.py +54 -0
  57. agents/task/agent/core/turn_input.py +8 -0
  58. agents/task/agent/core/untrusted_wrap.py +103 -0
  59. agents/task/agent/core/user_ingress.py +228 -0
  60. agents/task/agent/hitl_manager.py +396 -0
  61. agents/task/agent/log_sanitize.py +36 -0
  62. agents/task/agent/message_manager/config.py +97 -0
  63. agents/task/agent/message_manager/service.py +758 -0
  64. agents/task/agent/message_manager/tests.py +305 -0
  65. agents/task/agent/message_manager/tool_call_builder.py +539 -0
  66. agents/task/agent/message_manager/tool_message_repair.py +364 -0
  67. agents/task/agent/message_manager/views.py +143 -0
  68. agents/task/agent/messages/__init__.py +8 -0
  69. agents/task/agent/messages/builders.py +309 -0
  70. agents/task/agent/messages/compactor.py +611 -0
  71. agents/task/agent/messages/context_references.py +271 -0
  72. agents/task/agent/messages/filters.py +333 -0
  73. agents/task/agent/messages/guidance.py +328 -0
  74. agents/task/agent/messages/persistence.py +448 -0
  75. agents/task/agent/messages/retrieval.py +506 -0
  76. agents/task/agent/messages/sqlite_persistence.py +85 -0
  77. agents/task/agent/messages/test_compaction_upgrade.py +349 -0
  78. agents/task/agent/messages/test_persistence_subdir_fix.py +28 -0
  79. agents/task/agent/messages/test_sqlite_persistence.py +54 -0
  80. agents/task/agent/messages/token_counter.py +532 -0
  81. agents/task/agent/orchestrator.py +776 -0
  82. agents/task/agent/profile_manager.py +105 -0
  83. agents/task/agent/profile_registry.py +173 -0
  84. agents/task/agent/prompts.py +1090 -0
  85. agents/task/agent/scenario_registry.py +226 -0
  86. agents/task/agent/service.py +509 -0
  87. agents/task/agent/session.py +984 -0
  88. agents/task/agent/skill_discovery.py +110 -0
  89. agents/task/agent/skill_frontmatter.py +94 -0
  90. agents/task/agent/skill_manager.py +1201 -0
  91. agents/task/agent/skill_store.py +627 -0
  92. agents/task/agent/skill_validation.py +56 -0
  93. agents/task/agent/skill_writer.py +492 -0
  94. agents/task/agent/sub_agent_manager.py +1137 -0
  95. agents/task/agent/test_session_disk_recovery.py +34 -0
  96. agents/task/agent/tests.py +513 -0
  97. agents/task/agent/tool_call_tracker.py +511 -0
  98. agents/task/agent/views.py +469 -0
  99. agents/task/config.py +432 -0
  100. agents/task/constants.py +1384 -0
  101. agents/task/flag_defaults.py +63 -0
  102. agents/task/goals/__init__.py +1 -0
  103. agents/task/goals/autonomy_marker.py +28 -0
  104. agents/task/goals/board.py +690 -0
  105. agents/task/goals/completion_judge.py +247 -0
  106. agents/task/goals/context.py +62 -0
  107. agents/task/goals/dispatcher.py +848 -0
  108. agents/task/goals/escalation.py +102 -0
  109. agents/task/goals/planner.py +114 -0
  110. agents/task/logging_config.py +155 -0
  111. agents/task/path.py +991 -0
  112. agents/task/robust_parse_config.py +400 -0
  113. agents/task/runtime/__init__.py +0 -0
  114. agents/task/runtime/run_as_session.py +130 -0
  115. agents/task/runtime_safety.py +308 -0
  116. agents/task/session/__init__.py +5 -0
  117. agents/task/session/browser_pool.py +130 -0
  118. agents/task/session/cleanup.py +612 -0
  119. agents/task/session/execution.py +397 -0
  120. agents/task/session/feed.py +200 -0
  121. agents/task/session/hitl_ingress.py +201 -0
  122. agents/task/session/hooks.py +83 -0
  123. agents/task/session/multi_agent.py +243 -0
  124. agents/task/session/workspace.py +91 -0
  125. agents/task/session_registry.py +91 -0
  126. agents/task/session_route.py +35 -0
  127. agents/task/sqlite_session_registry.py +261 -0
  128. agents/task/surface_config.py +185 -0
  129. agents/task/telemetry/__init__.py +58 -0
  130. agents/task/telemetry/event_log.py +180 -0
  131. agents/task/telemetry/formatters.py +642 -0
  132. agents/task/telemetry/manager.py +571 -0
  133. agents/task/telemetry/memory_events.py +66 -0
  134. agents/task/telemetry/self_events.py +53 -0
  135. agents/task/telemetry/sequence.py +160 -0
  136. agents/task/telemetry/service.py +1265 -0
  137. agents/task/telemetry/views.py +882 -0
  138. agents/task/templates.py +98 -0
  139. agents/task/test_aux_chain.py +151 -0
  140. agents/task/test_aux_router.py +41 -0
  141. agents/task/tests/test_parse_robustness.py +170 -0
  142. agents/task/tool_defaults.py +110 -0
  143. agents/task/utils.py +762 -0
  144. agents/task/utils_json.py +768 -0
  145. agents/task/utils_webview.py +86 -0
  146. agents/task/workspace_context.py +229 -0
  147. agents/task_agent_lite.py +2398 -0
  148. api/README.md +683 -0
  149. api/TASK_API_DOCS.md +151 -0
  150. api/__init__.py +0 -0
  151. api/a2a/__init__.py +37 -0
  152. api/a2a/agent_card.py +337 -0
  153. api/a2a/client.py +657 -0
  154. api/a2a/endpoints.py +375 -0
  155. api/a2a/models.py +268 -0
  156. api/a2a/streaming.py +419 -0
  157. api/a2a/task_handler.py +929 -0
  158. api/admin_endpoints.py +1584 -0
  159. api/app.py +923 -0
  160. api/auth_constants.py +28 -0
  161. api/auth_endpoints.py +402 -0
  162. api/auth_state.py +36 -0
  163. api/chat_via_task.py +42 -0
  164. api/conversation_manager.py +231 -0
  165. api/dependencies.py +186 -0
  166. api/eip8004_endpoints.py +452 -0
  167. api/hyperliquid_models.py +174 -0
  168. api/hyperliquid_routes.py +450 -0
  169. api/interfaces.py +207 -0
  170. api/jwt_middleware.py +83 -0
  171. api/kb/__init__.py +1 -0
  172. api/kb/endpoints.py +314 -0
  173. api/mcp_models.py +279 -0
  174. api/mcp_routes.py +438 -0
  175. api/middleware.py +526 -0
  176. api/models.py +176 -0
  177. api/openai_compat/__init__.py +0 -0
  178. api/openai_compat/model_map.py +25 -0
  179. api/openai_compat/models.py +59 -0
  180. api/openai_compat/router.py +140 -0
  181. api/payment_endpoints.py +444 -0
  182. api/payment_verification.py +138 -0
  183. api/polymarket_models.py +155 -0
  184. api/polymarket_routes.py +501 -0
  185. api/pricing_endpoints.py +89 -0
  186. api/session_routing.py +54 -0
  187. api/skill_endpoints.py +453 -0
  188. api/task_http_api.py +1856 -0
  189. api/webhooks.py +52 -0
  190. api/x402_endpoints.py +280 -0
  191. avatar/README.md +44 -0
  192. avatar/__init__.py +1 -0
  193. avatar/config/rob.json +7 -0
  194. avatar/mindprint.js +395 -0
  195. avatar/renders/rob.meta.json +20 -0
  196. avatar/renders/rob.png +0 -0
  197. avatar/studio.html +373 -0
  198. avatar/webview/avatar-live.js +44 -0
  199. cli/README.md +89 -0
  200. cli/__init__.py +0 -0
  201. cli/cli_surface.py +67 -0
  202. cli/commands/__init__.py +0 -0
  203. cli/commands/_bootstrap.py +49 -0
  204. cli/commands/_errors.py +53 -0
  205. cli/commands/chat.py +973 -0
  206. cli/commands/config.py +100 -0
  207. cli/commands/dashboard.py +103 -0
  208. cli/commands/doctor.py +222 -0
  209. cli/commands/email.py +174 -0
  210. cli/commands/gateway.py +346 -0
  211. cli/commands/goals.py +509 -0
  212. cli/commands/init.py +276 -0
  213. cli/commands/journey.py +30 -0
  214. cli/commands/kb.py +280 -0
  215. cli/commands/model.py +107 -0
  216. cli/commands/owner.py +431 -0
  217. cli/commands/pfp.py +232 -0
  218. cli/commands/run.py +414 -0
  219. cli/commands/serve.py +41 -0
  220. cli/commands/session.py +684 -0
  221. cli/commands/skill_install.py +786 -0
  222. cli/commands/skills.py +128 -0
  223. cli/commands/subagents.py +100 -0
  224. cli/commands/surface.py +81 -0
  225. cli/commands/telegram.py +185 -0
  226. cli/commands/todos.py +209 -0
  227. cli/commands/tools.py +102 -0
  228. cli/commands/update.py +316 -0
  229. cli/commands/whatsapp.py +175 -0
  230. cli/config_store.py +276 -0
  231. cli/gitignore.py +43 -0
  232. cli/inventory.py +46 -0
  233. cli/keys.py +101 -0
  234. cli/persona.py +17 -0
  235. cli/polyrob.py +137 -0
  236. cli/ui/__init__.py +64 -0
  237. cli/ui/activity.py +141 -0
  238. cli/ui/app.py +538 -0
  239. cli/ui/banner.py +233 -0
  240. cli/ui/blocks.py +534 -0
  241. cli/ui/bootstrap_notice.py +47 -0
  242. cli/ui/commands/__init__.py +41 -0
  243. cli/ui/commands/h_cron.py +64 -0
  244. cli/ui/commands/h_journey.py +164 -0
  245. cli/ui/commands/h_kb.py +121 -0
  246. cli/ui/commands/h_learn.py +100 -0
  247. cli/ui/commands/h_mcp.py +215 -0
  248. cli/ui/commands/h_self.py +113 -0
  249. cli/ui/commands/h_skills.py +314 -0
  250. cli/ui/commands/handlers.py +1672 -0
  251. cli/ui/commands/registry.py +426 -0
  252. cli/ui/dialog.py +425 -0
  253. cli/ui/event_registry.py +110 -0
  254. cli/ui/event_specs.py +45 -0
  255. cli/ui/events.py +433 -0
  256. cli/ui/identity.py +15 -0
  257. cli/ui/lifecycle.py +159 -0
  258. cli/ui/live_hooks.py +43 -0
  259. cli/ui/model_selector.py +532 -0
  260. cli/ui/persistent_loop.py +181 -0
  261. cli/ui/pick.py +113 -0
  262. cli/ui/plain_renderer.py +351 -0
  263. cli/ui/renderer.py +384 -0
  264. cli/ui/rich_renderer.py +533 -0
  265. cli/ui/secrets.py +115 -0
  266. cli/ui/state.py +376 -0
  267. cli/ui/statusbar.py +231 -0
  268. cli/ui/streaming.py +254 -0
  269. cli/ui/terminal_render.py +105 -0
  270. cli/ui/theme.py +132 -0
  271. cli/update/__init__.py +6 -0
  272. cli/update/context.py +121 -0
  273. cli/update/detect.py +176 -0
  274. cli/update/engine.py +95 -0
  275. cli/update/migrate_guarded.py +55 -0
  276. cli/update/process_guard.py +215 -0
  277. cli/update/runners.py +78 -0
  278. cli/update/snapshot.py +270 -0
  279. cli/update/versions.py +205 -0
  280. core/README.md +377 -0
  281. core/__init__.py +174 -0
  282. core/assets.py +87 -0
  283. core/async_bridge.py +76 -0
  284. core/autonomy_runtime.py +259 -0
  285. core/base_component.py +302 -0
  286. core/bootstrap.py +681 -0
  287. core/bot.py +310 -0
  288. core/config.py +833 -0
  289. core/constants.py +126 -0
  290. core/container.py +462 -0
  291. core/db_manifest.py +101 -0
  292. core/embedding.py +57 -0
  293. core/env.py +39 -0
  294. core/exceptions.py +287 -0
  295. core/flags.py +144 -0
  296. core/flags_catalog.py +345 -0
  297. core/home_migration.py +62 -0
  298. core/identity.py +118 -0
  299. core/initialization.py +1004 -0
  300. core/instance.py +537 -0
  301. core/interactive_gate.py +96 -0
  302. core/logging.py +436 -0
  303. core/owner_doc_writer.py +176 -0
  304. core/pairing.py +193 -0
  305. core/path_safety.py +20 -0
  306. core/paths.py +27 -0
  307. core/payment_config.py +21 -0
  308. core/permissions.py +322 -0
  309. core/runtime_config.py +96 -0
  310. core/runtime_paths.py +135 -0
  311. core/seams.py +35 -0
  312. core/secret_scan.py +25 -0
  313. core/secret_scrub.py +71 -0
  314. core/secrets.py +35 -0
  315. core/security_logging_filter.py +148 -0
  316. core/self_context_writer.py +333 -0
  317. core/self_evolution.py +294 -0
  318. core/session_context.py +47 -0
  319. core/sqlite_util.py +59 -0
  320. core/surfaces/__init__.py +16 -0
  321. core/surfaces/access.py +118 -0
  322. core/surfaces/binding.py +88 -0
  323. core/surfaces/bootstrap.py +78 -0
  324. core/surfaces/circuit.py +168 -0
  325. core/surfaces/continuity.py +77 -0
  326. core/surfaces/correspondents.py +228 -0
  327. core/surfaces/dispatcher.py +216 -0
  328. core/surfaces/envelopes.py +75 -0
  329. core/surfaces/gc.py +30 -0
  330. core/surfaces/idempotency.py +57 -0
  331. core/surfaces/inbound_webhook.py +124 -0
  332. core/surfaces/media.py +32 -0
  333. core/surfaces/message_router.py +91 -0
  334. core/surfaces/outbound_allowlist.py +80 -0
  335. core/surfaces/outbound_dispatcher.py +110 -0
  336. core/surfaces/outbound_mirror.py +37 -0
  337. core/surfaces/outbound_queue.py +107 -0
  338. core/surfaces/outbound_target.py +12 -0
  339. core/surfaces/owner_admin.py +29 -0
  340. core/surfaces/proactive.py +78 -0
  341. core/surfaces/progress.py +108 -0
  342. core/surfaces/rate_bucket.py +21 -0
  343. core/surfaces/registry.py +51 -0
  344. core/surfaces/rendering.py +53 -0
  345. core/surfaces/send_policy.py +14 -0
  346. core/surfaces/serialize.py +24 -0
  347. core/surfaces/session_chat_registry.py +112 -0
  348. core/surfaces/session_policy.py +60 -0
  349. core/surfaces/surface.py +288 -0
  350. core/surfaces/transcription.py +88 -0
  351. core/surfaces/voice_echo.py +26 -0
  352. core/surfaces/voice_guard.py +23 -0
  353. core/tickers.py +174 -0
  354. core/tool_catalog.py +192 -0
  355. core/version.py +75 -0
  356. core/wallet/__init__.py +5 -0
  357. core/wallet/agent_wallet.py +59 -0
  358. core/wallet/audit_sink.py +61 -0
  359. core/wallet/config.py +70 -0
  360. core/wallet/factory.py +87 -0
  361. core/wallet/policy.py +104 -0
  362. core/wallet/signer.py +51 -0
  363. cron/README.md +65 -0
  364. cron/__init__.py +0 -0
  365. cron/delivery.py +277 -0
  366. cron/digest.py +124 -0
  367. cron/jobs.py +207 -0
  368. cron/runner.py +297 -0
  369. cron/schedule.py +194 -0
  370. cron/scheduler.py +201 -0
  371. cron/service.py +57 -0
  372. cron/wake_gate.py +223 -0
  373. data/__init__.py +1 -0
  374. data/prompts/autov2_prompts.json +87 -0
  375. data/prompts/skills/browser-automation/SKILL.md +43 -0
  376. data/prompts/skills/coding-workflow/SKILL.md +36 -0
  377. data/prompts/skills/crypto-trading-safety/SKILL.md +53 -0
  378. data/prompts/skills/document-writing/SKILL.md +33 -0
  379. data/prompts/skills/email-comms/SKILL.md +33 -0
  380. data/prompts/skills/file-data-ops/SKILL.md +35 -0
  381. data/prompts/skills/hyperliquid-account-review/SKILL.md +48 -0
  382. data/prompts/skills/hyperliquid-market-data/SKILL.md +52 -0
  383. data/prompts/skills/hyperliquid-trading/SKILL.md +59 -0
  384. data/prompts/skills/lead-research/SKILL.md +86 -0
  385. data/prompts/skills/market-research-brief/SKILL.md +104 -0
  386. data/prompts/skills/person-analyzer/SKILL.md +124 -0
  387. data/prompts/skills/polymarket-market-research/SKILL.md +54 -0
  388. data/prompts/skills/polymarket-portfolio-review/SKILL.md +50 -0
  389. data/prompts/skills/polymarket-trading/SKILL.md +55 -0
  390. data/prompts/skills/presentation-creator/SKILL.md +36 -0
  391. data/prompts/skills/project-analyzer/SKILL.md +169 -0
  392. data/prompts/skills/rules.json +268 -0
  393. data/prompts/skills/secret-handling/SKILL.md +20 -0
  394. data/prompts/skills/skill-authoring/SKILL.md +31 -0
  395. data/prompts/skills/skill-security-review/SKILL.md +27 -0
  396. data/prompts/skills/social-discovery/SKILL.md +57 -0
  397. data/prompts/skills/task-planning/SKILL.md +33 -0
  398. data/prompts/skills/web-research/SKILL.md +39 -0
  399. data/prompts/skills/web-scraping/SKILL.md +73 -0
  400. data/prompts/skills/x-engagement/SKILL.md +69 -0
  401. data/prompts/system_prompts.json +4 -0
  402. data/subscription_channels.json +5 -0
  403. modules/README.md +503 -0
  404. modules/__init__.py +124 -0
  405. modules/auth/__init__.py +13 -0
  406. modules/auth/api_key_manager.py +157 -0
  407. modules/auth/identity_mapper.py +380 -0
  408. modules/auth/siwe_auth.py +273 -0
  409. modules/auth/tier_manager.py +173 -0
  410. modules/base_module.py +190 -0
  411. modules/credits/__init__.py +29 -0
  412. modules/credits/balance_manager.py +237 -0
  413. modules/credits/cost_utils.py +129 -0
  414. modules/credits/pricing.py +139 -0
  415. modules/credits/unified_ledger.py +148 -0
  416. modules/credits/usage_meter.py +310 -0
  417. modules/credits/usage_tracker.py +674 -0
  418. modules/database/__init__.py +33 -0
  419. modules/database/audit_log.py +322 -0
  420. modules/database/auth_tables.py +276 -0
  421. modules/database/connection.py +644 -0
  422. modules/database/connection_pool.py +305 -0
  423. modules/database/conversation_contexts.py +446 -0
  424. modules/database/database_manager.py +237 -0
  425. modules/database/hyperliquid.py +375 -0
  426. modules/database/polymarket.py +574 -0
  427. modules/database/user_mcp_servers.py +728 -0
  428. modules/database/user_profiles.py +562 -0
  429. modules/database/utils.py +107 -0
  430. modules/database/x402_tables.py +92 -0
  431. modules/eip8004/README.md +1069 -0
  432. modules/eip8004/__init__.py +65 -0
  433. modules/eip8004/contracts.py +498 -0
  434. modules/eip8004/models.py +271 -0
  435. modules/eip8004/registration.py +148 -0
  436. modules/eip8004/reputation.py +367 -0
  437. modules/eip8004/validation.py +277 -0
  438. modules/llm/__init__.py +97 -0
  439. modules/llm/adapters.py +963 -0
  440. modules/llm/anthropic_client.py +831 -0
  441. modules/llm/available_models.py +106 -0
  442. modules/llm/brain_scrubber.py +137 -0
  443. modules/llm/cache_hints.py +162 -0
  444. modules/llm/deepseek_client.py +562 -0
  445. modules/llm/gemini_client.py +1367 -0
  446. modules/llm/llm_client.py +1139 -0
  447. modules/llm/llm_client_registry.py +188 -0
  448. modules/llm/llm_factory.py +237 -0
  449. modules/llm/llm_manager.py +1036 -0
  450. modules/llm/messages.py +374 -0
  451. modules/llm/model_registry.py +1935 -0
  452. modules/llm/nvidia_client.py +125 -0
  453. modules/llm/openai_client.py +800 -0
  454. modules/llm/openrouter_client.py +969 -0
  455. modules/llm/profiles.py +178 -0
  456. modules/llm/test_anthropic_cache_capture.py +35 -0
  457. modules/llm/test_cached_pricing_derivation.py +24 -0
  458. modules/llm/think_scrubber.py +444 -0
  459. modules/llm/token_counter.py +268 -0
  460. modules/memory/__init__.py +35 -0
  461. modules/memory/backend_factory.py +83 -0
  462. modules/memory/cache_manager.py +186 -0
  463. modules/memory/episodic.py +108 -0
  464. modules/memory/local_vector_memory_provider.py +552 -0
  465. modules/memory/memory_manager.py +225 -0
  466. modules/memory/models.py +312 -0
  467. modules/memory/provider.py +210 -0
  468. modules/memory/registry.py +205 -0
  469. modules/memory/sqlite_memory_provider.py +767 -0
  470. modules/memory/task/__init__.py +52 -0
  471. modules/memory/task/compaction_manager.py +157 -0
  472. modules/memory/task/context_retriever.py +809 -0
  473. modules/memory/task/hierarchical_memory.py +1002 -0
  474. modules/memory/task/lexical_retriever.py +71 -0
  475. modules/memory/task/null_context_manager.py +213 -0
  476. modules/memory/task/phase_manager.py +625 -0
  477. modules/memory/task/reflection_service.py +157 -0
  478. modules/memory/task/semantic_retriever.py +218 -0
  479. modules/memory/task/task_context_manager.py +1165 -0
  480. modules/memory/task/test_forgetting_engages.py +24 -0
  481. modules/memory/task/test_reflection_llm.py +39 -0
  482. modules/memory/task/test_threat_scan.py +53 -0
  483. modules/memory/task/threat_scan.py +90 -0
  484. modules/memory/test_sqlite_memory_provider.py +106 -0
  485. modules/memory/user_profile_manager.py +527 -0
  486. modules/payments/__init__.py +11 -0
  487. modules/payments/deposit_monitor.py +478 -0
  488. modules/payments/price_oracle.py +70 -0
  489. modules/payments/treasury_sweeper.py +401 -0
  490. modules/payments/wallet_generator.py +145 -0
  491. modules/pfp/__init__.py +1 -0
  492. modules/pfp/config.py +92 -0
  493. modules/pfp/mesh.py +565 -0
  494. modules/pfp/push.py +69 -0
  495. modules/pfp/renderer.py +85 -0
  496. modules/pfp/store.py +82 -0
  497. modules/skills/__init__.py +1 -0
  498. modules/skills/skill_usage.py +248 -0
  499. modules/transcription/__init__.py +24 -0
  500. modules/transcription/base.py +20 -0
  501. modules/transcription/faster_whisper_transcriber.py +55 -0
  502. modules/x402/README.md +249 -0
  503. modules/x402/__init__.py +22 -0
  504. modules/x402/invoicing.py +416 -0
  505. modules/x402/middleware.py +364 -0
  506. modules/x402/settlement_watcher.py +145 -0
  507. modules/x402/x402_integration.py +344 -0
  508. polyrob-0.5.0.dist-info/METADATA +482 -0
  509. polyrob-0.5.0.dist-info/RECORD +745 -0
  510. polyrob-0.5.0.dist-info/WHEEL +5 -0
  511. polyrob-0.5.0.dist-info/entry_points.txt +2 -0
  512. polyrob-0.5.0.dist-info/licenses/LICENSE +21 -0
  513. polyrob-0.5.0.dist-info/top_level.txt +12 -0
  514. surfaces/README.md +70 -0
  515. surfaces/__init__.py +0 -0
  516. surfaces/email/__init__.py +0 -0
  517. surfaces/email/dedup.py +44 -0
  518. surfaces/email/harness.py +187 -0
  519. surfaces/email/inbound.py +159 -0
  520. surfaces/email/seed.py +60 -0
  521. surfaces/email/surface.py +105 -0
  522. surfaces/telegram/__init__.py +0 -0
  523. surfaces/telegram/dedup.py +83 -0
  524. surfaces/telegram/harness.py +886 -0
  525. surfaces/telegram/inbound.py +145 -0
  526. surfaces/telegram/interactive_tools.py +49 -0
  527. surfaces/telegram/markdown.py +347 -0
  528. surfaces/telegram/rate_limit.py +167 -0
  529. surfaces/telegram/surface.py +187 -0
  530. surfaces/telegram/voice.py +52 -0
  531. surfaces/whatsapp/__init__.py +0 -0
  532. surfaces/whatsapp/client.py +51 -0
  533. surfaces/whatsapp/harness.py +62 -0
  534. surfaces/whatsapp/inbound.py +132 -0
  535. surfaces/whatsapp/surface.py +64 -0
  536. surfaces/whatsapp/window.py +34 -0
  537. tools/README.md +578 -0
  538. tools/__init__.py +424 -0
  539. tools/alchemy/__init__.py +5 -0
  540. tools/alchemy/alchemy_tool.py +389 -0
  541. tools/anysite/__init__.py +17 -0
  542. tools/anysite/client.py +88 -0
  543. tools/anysite/tool.py +89 -0
  544. tools/base_tool.py +468 -0
  545. tools/browser/__init__.py +22 -0
  546. tools/browser/actions.py +118 -0
  547. tools/browser/browser.py +1457 -0
  548. tools/browser/browser_manager.py +690 -0
  549. tools/browser/context.py +1748 -0
  550. tools/browser/playwright_utils.py +137 -0
  551. tools/browser/views.py +56 -0
  552. tools/code_exec/SANDBOX_SECURITY.md +144 -0
  553. tools/code_exec/__init__.py +178 -0
  554. tools/code_exec/backend.py +73 -0
  555. tools/code_exec/backends/__init__.py +1 -0
  556. tools/code_exec/backends/docker.py +779 -0
  557. tools/code_exec/backends/local_subprocess.py +154 -0
  558. tools/code_exec/env_policy.py +45 -0
  559. tools/code_exec/result.py +42 -0
  560. tools/code_exec/sandbox_guard.py +59 -0
  561. tools/code_exec/tool.py +227 -0
  562. tools/coding/__init__.py +50 -0
  563. tools/coding/edit.py +107 -0
  564. tools/coding/search.py +113 -0
  565. tools/coding/tool.py +353 -0
  566. tools/collabland/__init__.py +5 -0
  567. tools/collabland/collabland_tool.py +471 -0
  568. tools/controller/__init__.py +0 -0
  569. tools/controller/_helpers.py +165 -0
  570. tools/controller/action_registration.py +1765 -0
  571. tools/controller/approval.py +244 -0
  572. tools/controller/approval_interactive.py +90 -0
  573. tools/controller/delegation.py +195 -0
  574. tools/controller/execution.py +797 -0
  575. tools/controller/execution_context.py +141 -0
  576. tools/controller/hooks.py +135 -0
  577. tools/controller/introspection.py +344 -0
  578. tools/controller/mcp_registrar.py +190 -0
  579. tools/controller/message_send.py +31 -0
  580. tools/controller/registry/__init__.py +4 -0
  581. tools/controller/registry/schema_generators.py +395 -0
  582. tools/controller/registry/schema_sanitizer.py +406 -0
  583. tools/controller/registry/service.py +1266 -0
  584. tools/controller/registry/views.py +151 -0
  585. tools/controller/service.py +409 -0
  586. tools/controller/tool_management.py +334 -0
  587. tools/controller/types.py +28 -0
  588. tools/controller/views.py +450 -0
  589. tools/cronjob_tools.py +166 -0
  590. tools/crypto_trade_gate.py +59 -0
  591. tools/descriptors.py +452 -0
  592. tools/dom/__init__.py +0 -0
  593. tools/dom/history_tree_processor/service.py +244 -0
  594. tools/dom/history_tree_processor/view.py +99 -0
  595. tools/dom/service.py +471 -0
  596. tools/dom/views.py +346 -0
  597. tools/email_tool.py +403 -0
  598. tools/exceptions.py +169 -0
  599. tools/filesystem.py +1038 -0
  600. tools/filesystem_docproc.py +331 -0
  601. tools/filesystem_pdf.py +755 -0
  602. tools/git/__init__.py +42 -0
  603. tools/git/tool.py +258 -0
  604. tools/github/__init__.py +33 -0
  605. tools/github/client.py +94 -0
  606. tools/github/tool.py +237 -0
  607. tools/goal_tools.py +309 -0
  608. tools/hyperliquid/__init__.py +56 -0
  609. tools/hyperliquid/models.py +233 -0
  610. tools/hyperliquid/service.py +1680 -0
  611. tools/knowledge_ingest.py +950 -0
  612. tools/mcp/README.md +535 -0
  613. tools/mcp/__init__.py +52 -0
  614. tools/mcp/catalog.py +115 -0
  615. tools/mcp/config.py +337 -0
  616. tools/mcp/mcp_tool.py +1415 -0
  617. tools/mcp/param_coercion.py +289 -0
  618. tools/mcp/protocol.py +1520 -0
  619. tools/mcp/rate_limit.py +50 -0
  620. tools/mcp/security.py +424 -0
  621. tools/mcp/self_install.py +111 -0
  622. tools/mcp/server_manager.py +829 -0
  623. tools/mcp/subscriptions.py +72 -0
  624. tools/mcp/user_mcp_service.py +899 -0
  625. tools/mcp/validation_tracker.py +89 -0
  626. tools/mcp/views.py +346 -0
  627. tools/oauth/__init__.py +17 -0
  628. tools/oauth/manager.py +75 -0
  629. tools/oauth/provider.py +69 -0
  630. tools/oauth/providers/__init__.py +1 -0
  631. tools/oauth/providers/generic_oauth2.py +96 -0
  632. tools/perplexity_tool.py +280 -0
  633. tools/polymarket/__init__.py +41 -0
  634. tools/polymarket/clob_adapter.py +76 -0
  635. tools/polymarket/models.py +182 -0
  636. tools/polymarket/service.py +2082 -0
  637. tools/self_env/__init__.py +61 -0
  638. tools/self_env/tool.py +274 -0
  639. tools/shell/__init__.py +80 -0
  640. tools/shell/backend_pool.py +118 -0
  641. tools/shell/discipline.py +62 -0
  642. tools/shell/executor.py +157 -0
  643. tools/shell/loopback_allow.py +87 -0
  644. tools/shell/process_registry.py +113 -0
  645. tools/shell/process_tool.py +131 -0
  646. tools/shell/state.py +129 -0
  647. tools/shell/tool.py +151 -0
  648. tools/task_tool.py +637 -0
  649. tools/twitter_tool.py +2086 -0
  650. tools/user_directory.py +179 -0
  651. tools/web_fetch/__init__.py +3 -0
  652. tools/web_fetch/fetcher.py +144 -0
  653. tools/web_fetch/render.py +44 -0
  654. tools/web_fetch/tool.py +60 -0
  655. tools/x402/__init__.py +60 -0
  656. tools/x402/client.py +47 -0
  657. tools/x402/invoice_tool.py +143 -0
  658. tools/x402/real_client.py +250 -0
  659. tools/x402/service.py +131 -0
  660. utils/README.md +498 -0
  661. utils/__init__.py +46 -0
  662. utils/auth_utils.py +114 -0
  663. utils/bounded_collections.py +162 -0
  664. utils/circuit_breaker.py +389 -0
  665. utils/gif_utils.py +435 -0
  666. utils/markdown_utils.py +291 -0
  667. utils/message_utils.py +147 -0
  668. utils/metrics.py +36 -0
  669. utils/path_validator.py +227 -0
  670. utils/rate_limit_manager.py +399 -0
  671. utils/result_size.py +296 -0
  672. utils/time_utils.py +206 -0
  673. utils/user_utils.py +188 -0
  674. webview/README.md +111 -0
  675. webview/RENAME_AND_ALIGN_HANDOFF.md +200 -0
  676. webview/__init__.py +8 -0
  677. webview/activity.py +636 -0
  678. webview/owner_auth.py +108 -0
  679. webview/pages.py +448 -0
  680. webview/repair_sessions.py +399 -0
  681. webview/server.py +4323 -0
  682. webview/server_launcher.py +112 -0
  683. webview/static/css/activity.css +89 -0
  684. webview/static/css/chat.css +1772 -0
  685. webview/static/css/components.css +1190 -0
  686. webview/static/css/config-panel.css +651 -0
  687. webview/static/css/pages/admin.css +766 -0
  688. webview/static/css/pages/profile.css +231 -0
  689. webview/static/css/pages/settings.css +297 -0
  690. webview/static/css/pages/signin.css +316 -0
  691. webview/static/css/style.css +4250 -0
  692. webview/static/css/variables.css +147 -0
  693. webview/static/css/workspace-fullwidth-fix.css +109 -0
  694. webview/static/img/favicon.ico +0 -0
  695. webview/static/js/activity.js +394 -0
  696. webview/static/js/admin/activity.js +186 -0
  697. webview/static/js/admin/dashboard.js +163 -0
  698. webview/static/js/admin/user_detail.js +400 -0
  699. webview/static/js/admin/users.js +174 -0
  700. webview/static/js/admin/utils.js +214 -0
  701. webview/static/js/chat.js +3178 -0
  702. webview/static/js/config-panel.js +522 -0
  703. webview/static/js/constants.js +20 -0
  704. webview/static/js/error-handler.js +226 -0
  705. webview/static/js/ethers.min.js +1 -0
  706. webview/static/js/event-filter.js +137 -0
  707. webview/static/js/event-store.js +351 -0
  708. webview/static/js/file-attachments.js +374 -0
  709. webview/static/js/file-loader.js +163 -0
  710. webview/static/js/index.js +103 -0
  711. webview/static/js/performance-utils.js +196 -0
  712. webview/static/js/profile.js +396 -0
  713. webview/static/js/screenshot.js +386 -0
  714. webview/static/js/session.js +1527 -0
  715. webview/static/js/settings.js +1288 -0
  716. webview/static/js/sidebar-data.js +766 -0
  717. webview/static/js/sidebar-toggle.js +296 -0
  718. webview/static/js/socket.io.min.js +7 -0
  719. webview/static/js/stats.js +355 -0
  720. webview/static/js/ui-utils.js +420 -0
  721. webview/static/js/workspace.js +966 -0
  722. webview/stats_service.py +613 -0
  723. webview/templates/__init__.py +1 -0
  724. webview/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  725. webview/templates/activity.html +36 -0
  726. webview/templates/admin/activity.html +122 -0
  727. webview/templates/admin/dashboard.html +112 -0
  728. webview/templates/admin/user_detail.html +228 -0
  729. webview/templates/admin/users.html +105 -0
  730. webview/templates/autonomy.html +60 -0
  731. webview/templates/error.html +133 -0
  732. webview/templates/finance.html +90 -0
  733. webview/templates/identity.html +73 -0
  734. webview/templates/index.html +42 -0
  735. webview/templates/layout.html +125 -0
  736. webview/templates/memory.html +59 -0
  737. webview/templates/owner_login.html +15 -0
  738. webview/templates/profile.html +121 -0
  739. webview/templates/session.html +312 -0
  740. webview/templates/settings.html +534 -0
  741. webview/templates/sidebar.html +112 -0
  742. webview/templates/signin.html +213 -0
  743. webview/templates/status.html +14 -0
  744. webview/templates/system.html +45 -0
  745. 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)