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,506 @@
1
+ """Message-retrieval / LLM-request-assembly mixin (roadmap P9; from message_manager/service.py).
2
+
3
+ get_messages / get_messages_for_llm (foundation + history assembly with the pinned
4
+ system/task/skill/H-MEM messages), ephemeral push, LLM timeout/param helpers, and
5
+ message-structure logging. Split out so MessageManager service.py drops under 700L.
6
+ MessageManager composes MessageRetrievalMixin; call sites (step.py, llm_runner.py,
7
+ logging_io, output_validation, tests) unchanged via MRO.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import logging
13
+ from collections import deque
14
+ from typing import Any, Dict, List, Optional
15
+
16
+ from modules.llm.messages import (
17
+ AIMessage, BaseMessage, HumanMessage, SystemMessage, ToolMessage,
18
+ MessageOrigin, make_control_message,
19
+ )
20
+ from agents.task.constants import hmem_tail_placement
21
+
22
+
23
+ class MessageRetrievalMixin:
24
+ """Message retrieval + LLM-request assembly for MessageManager."""
25
+
26
+ def get_messages(self) -> List[BaseMessage]:
27
+ """Get current message list with foundation messages always prepended.
28
+
29
+ CRITICAL FIX: SystemMessage and InitialTask are stored separately to prevent deque eviction.
30
+ This method always returns [SystemMessage, InitialTask (if exists), ...conversation] to ensure
31
+ the LLM always receives brain state instructions and knows the original task.
32
+
33
+ FOUNDATION (never evicted):
34
+ [0] SystemMessage - Instructions, format, workflow
35
+ [1] Initial Task - Original user request (if new session)
36
+
37
+ CONVERSATION (deque, last 20 messages):
38
+ [2+] Recent messages (auto-evicts oldest)
39
+ """
40
+
41
+ # Build foundation messages (stored separately, never evicted)
42
+ foundation = [self._system_message]
43
+
44
+ # Runtime identity (model/provider actually running), pinned right after the
45
+ # system message so the agent answers "what model are you" from this line, not
46
+ # by reading env/config. Empty/unset => skipped. Matches get_messages_for_llm().
47
+ if getattr(self, '_runtime_identity_message', None) is not None:
48
+ foundation.append(self._runtime_identity_message)
49
+
50
+ # polyrob Phase C: SOUL/IDENTITY self-context, pinned right after the system
51
+ # message (identity precedes task), matching get_messages_for_llm().
52
+ if getattr(self, '_self_context_message', None) is not None:
53
+ foundation.append(self._self_context_message)
54
+
55
+ # C9: project context (CLAUDE.md/AGENTS.md/.cursorrules) pinned AFTER
56
+ # self-context and BEFORE the initial task, matching get_messages_for_llm().
57
+ if getattr(self, '_project_context_message', None) is not None:
58
+ foundation.append(self._project_context_message)
59
+
60
+ if self._initial_task_message is not None:
61
+ foundation.append(self._initial_task_message)
62
+
63
+ # Skills (PR13) - pinned in the foundation after the task, matching
64
+ # get_messages_for_llm() so logs and guidance-position math see the same layout.
65
+ if getattr(self, '_skill_message', None) is not None:
66
+ foundation.append(self._skill_message)
67
+
68
+ # Get conversation messages from history (recent messages in deque)
69
+ conversation_messages = [m.message for m in self.history.messages]
70
+
71
+ # Assemble: Foundation + Conversation
72
+ msg = foundation + conversation_messages
73
+
74
+ # Debug logging with correct indexing
75
+ total_input_tokens = self._system_message_tokens + self._initial_task_tokens
76
+
77
+ foundation_count = len(foundation)
78
+ self.logger.debug(f'Messages: {foundation_count} foundation (separate) + {len(self.history.messages)} conversation (deque)')
79
+ self.logger.debug(f'[0] SystemMessage - {self._system_message_tokens} tokens (separate)')
80
+
81
+ if self._initial_task_message:
82
+ self.logger.debug(f'[1] InitialTask - {self._initial_task_tokens} tokens (separate)')
83
+
84
+ for i, m in enumerate(self.history.messages):
85
+ total_input_tokens += m.metadata.input_tokens
86
+ msg_type = m.message.__class__.__name__
87
+ msg_index = foundation_count + i # Correct index accounting for foundation
88
+
89
+ # Add detailed logging for tool calls and responses
90
+ if isinstance(m.message, AIMessage) and hasattr(m.message, 'tool_calls') and m.message.tool_calls:
91
+ tool_call_ids = [tc.get('id') for tc in m.message.tool_calls if 'id' in tc]
92
+ self.logger.debug(f'[{msg_index}] {msg_type} - {m.metadata.input_tokens} tokens - tool_calls: {tool_call_ids}')
93
+ elif isinstance(m.message, ToolMessage) and hasattr(m.message, 'tool_call_id'):
94
+ self.logger.debug(f'[{msg_index}] {msg_type} - {m.metadata.input_tokens} tokens - tool_call_id: {m.message.tool_call_id}')
95
+ else:
96
+ self.logger.debug(f'[{msg_index}] {msg_type} - {m.metadata.input_tokens} tokens')
97
+
98
+ self.logger.debug(f'Total tokens: {total_input_tokens} (foundation: {self._system_message_tokens + self._initial_task_tokens}, conversation: {total_input_tokens - self._system_message_tokens - self._initial_task_tokens})')
99
+
100
+ return msg
101
+
102
+
103
+ def push_ephemeral_message(self, message: BaseMessage) -> None:
104
+ """Queue a one-shot message to be included on the next LLM call only.
105
+
106
+ This does not modify history or token counts and is cleared after use.
107
+ """
108
+ try:
109
+ # Apply sensitive data filtering if configured, or the HISTORY_SECRET_SCRUB
110
+ # backstop (default ON) which scrubs unregistered secrets with no allowlist.
111
+ if self.sensitive_data or getattr(self, "_history_secret_scrub", True):
112
+ message = self._filter_sensitive_data(message)
113
+ self._ephemeral_messages.append(message)
114
+ self.logger.debug(f"Queued ephemeral message of type {type(message).__name__}")
115
+ except Exception as e:
116
+ self.logger.debug(f"Failed to queue ephemeral message: {e}")
117
+
118
+ def commit_ephemeral_consumption(self) -> None:
119
+ """P2-14: drop the pending (delivered) ephemerals. Call once the LLM has
120
+ responded — even a parse-failed response means they WERE delivered."""
121
+ self._ephemeral_pending = []
122
+
123
+ def restore_ephemeral_on_failure(self) -> None:
124
+ """P2-14: re-queue ephemerals consumed for an LLM call that ultimately failed
125
+ (transient invoke error), so the next step re-includes them instead of losing
126
+ a correspondent reply / RECALL forever."""
127
+ pending = getattr(self, '_ephemeral_pending', None)
128
+ if pending:
129
+ self._ephemeral_messages = pending + self._ephemeral_messages
130
+ self._ephemeral_pending = []
131
+
132
+ def get_messages_for_llm(self, consume_ephemeral: bool = True) -> List[BaseMessage]:
133
+ """Get messages ready for LLM call with all processing applied.
134
+
135
+ Args:
136
+ consume_ephemeral: when True (default, the real send-to-LLM path), any
137
+ queued one-shot ephemeral messages are cleared after being included
138
+ exactly once. Diagnostic/observational callers (e.g. context-usage
139
+ logging) must pass False so they peek without draining a one-shot
140
+ message (correspondent reply, deferral notice, memory-writer note)
141
+ before the real LLM call ever sees it (CX-H3).
142
+
143
+ ✅ FIX (Nov 5, 2025): Refactored to protect H-MEM from repair
144
+
145
+ ARCHITECTURE:
146
+ 1. Foundation (NEVER repaired): System message, Task message, H-MEM
147
+ 2. Conversation (subject to repair): Recent messages, ephemeral messages
148
+ 3. Repair ONLY conversation (foundation untouched)
149
+ 4. Combine: foundation + repaired conversation
150
+ 5. Model-specific handling (e.g., deepseek merging)
151
+ 6. Return messages: Ready for LLM provider
152
+
153
+ This ensures H-MEM can never be lost because it's in the protected foundation layer.
154
+ """
155
+ # Build foundation (never repaired): system, task, memory
156
+ foundation = []
157
+
158
+ # 1.1: System message (always first - instructions)
159
+ foundation.append(self._system_message)
160
+
161
+ # 1.1a: Runtime identity (model/provider actually running), pinned right after
162
+ # the system prompt so the agent answers its model from THIS line, not by
163
+ # reading env/config. Kept out of the system prompt (cacheable). Unset => skip.
164
+ if getattr(self, '_runtime_identity_message', None) is not None:
165
+ foundation.append(self._runtime_identity_message)
166
+
167
+ # 1.1b: polyrob Phase C - SOUL/IDENTITY self-context, pinned in the foundation
168
+ # right after the system prompt (NOT embedded in it, so the system prompt stays
169
+ # stable/cacheable). Identity precedes the task. Empty/unset => skipped.
170
+ if getattr(self, '_self_context_message', None) is not None:
171
+ foundation.append(self._self_context_message)
172
+
173
+ # 1.1c: C9 - project context (CLAUDE.md/AGENTS.md/.cursorrules), pinned AFTER
174
+ # self-context and BEFORE the initial task. CLI-only; empty/unset => skipped.
175
+ if getattr(self, '_project_context_message', None) is not None:
176
+ foundation.append(self._project_context_message)
177
+
178
+ # 1.2: Initial task message (if exists - defines what user wants)
179
+ if self._initial_task_message is not None:
180
+ foundation.append(self._initial_task_message)
181
+
182
+ # 1.2b: Skills (PR13) - pinned in the foundation, NOT the system prompt, so the
183
+ # system prompt stays stable/cacheable and skills read as a distinct block.
184
+ if getattr(self, '_skill_message', None) is not None:
185
+ foundation.append(self._skill_message)
186
+
187
+ # 1.3: Hierarchical memory.
188
+ # Placement (Phase 0.1): legacy = pinned in the foundation ahead of the
189
+ # conversation; tail = held here and appended as a dynamic SUFFIX after the
190
+ # conversation (see combine step below). The H-MEM block changes every step,
191
+ # so in the foundation prefix it broke the prompt cache for everything after
192
+ # it; as a tail suffix the stable foundation + growing conversation stay a
193
+ # cacheable prefix. Either way it is NEVER subject to tool-sequence repair.
194
+ memory_injected = False
195
+ hmem_msg = None # held for tail placement
196
+ tail_placement = hmem_tail_placement()
197
+
198
+ # FIX #1: Explicit warnings when H-MEM cannot be injected (was silently skipped)
199
+ if not self.task_context_manager:
200
+ self.logger.warning(
201
+ "⚠️ H-MEM DISABLED: TaskContextManager not available. "
202
+ "Agent will run without hierarchical memory context."
203
+ )
204
+ elif not self.session_id:
205
+ self.logger.warning(
206
+ "⚠️ H-MEM DISABLED: session_id not set. "
207
+ "Agent will run without hierarchical memory context."
208
+ )
209
+ else:
210
+ try:
211
+ context = self.task_context_manager.get_context_injection(self.session_id)
212
+ if context:
213
+ # Chat-schema: tag injected hierarchical memory as MEMORY control
214
+ # content (origin + <session-memory> envelope), not a user turn.
215
+ context_msg = make_control_message(context, MessageOrigin.MEMORY)
216
+ if tail_placement:
217
+ hmem_msg = context_msg # appended after conversation below
218
+ else:
219
+ foundation.append(context_msg)
220
+ memory_injected = True
221
+ context_tokens = len(context.split()) * 1.3 # Rough estimate
222
+ where = "tail suffix" if tail_placement else "foundation"
223
+ self.logger.debug(f"✅ H-MEM in {where} (protected from repair) - ~{context_tokens:.0f} tokens")
224
+ else:
225
+ self.logger.debug("H-MEM returned empty context (session may be new)")
226
+ except Exception as e:
227
+ self.logger.warning(f"Failed to inject hierarchical memory: {e}")
228
+
229
+
230
+ # Build conversation (may be repaired)
231
+ conversation = []
232
+
233
+ # 2.1: Recent conversation (from history deque)
234
+ # Use all available messages (up to MAX_RECENT_MESSAGES=10)
235
+ conversation_messages = [m.message for m in self.history.messages]
236
+ conversation.extend(conversation_messages)
237
+
238
+ # 2.2: Include any queued ephemeral messages (one-shot)
239
+ if hasattr(self, '_ephemeral_messages') and self._ephemeral_messages:
240
+ conversation.extend(self._ephemeral_messages)
241
+ self.logger.debug(f"Included {len(self._ephemeral_messages)} ephemeral messages")
242
+ if consume_ephemeral:
243
+ # P2-14: don't drop the one-shot ephemerals (correspondent replies,
244
+ # RECALL) at assembly time — the LLM call hasn't happened yet, so a
245
+ # transient invoke failure would lose them forever (correspondent data
246
+ # has no other delivery path). MOVE them to a pending buffer instead:
247
+ # commit_ephemeral_consumption() drops them once the LLM has responded,
248
+ # restore_ephemeral_on_failure() re-queues them if the call ultimately
249
+ # failed. Wired in the next_action wrapper (output_validation.py).
250
+ self._ephemeral_pending = list(self._ephemeral_messages)
251
+ self._ephemeral_messages.clear()
252
+
253
+ self.logger.debug(
254
+ f"Message composition: foundation={len(foundation)} (system=1, task={1 if self._initial_task_message else 0}, "
255
+ f"h_mem={1 if memory_injected else 0}), conversation={len(conversation)}"
256
+ )
257
+
258
+ # Repair tool sequences in conversation
259
+ conversation = self._validate_and_repair_tool_sequences(conversation)
260
+ self.logger.debug(f"After repair: foundation={len(foundation)}, conversation={len(conversation)}")
261
+
262
+ # B2/B3 (Reference-parity media hygiene, see docs/REFERENCE_VS_ROB_CONTEXT_SYSTEM_2026-06.md §9):
263
+ # dedup byte-identical tool outputs, and strip base64 from every image-bearing turn
264
+ # EXCEPT the most recent. Conversation only — foundation (system/task/H-MEM/skills)
265
+ # is never touched. Both are idempotent and fail-soft no-ops when nothing matches.
266
+ try:
267
+ from agents.task.agent.messages.filters import dedup_tool_results, strip_historical_media
268
+ conversation = dedup_tool_results(conversation)
269
+ conversation = strip_historical_media(conversation)
270
+ except Exception as e:
271
+ self.logger.debug(f"media-hygiene pass skipped: {e}")
272
+
273
+ # Combine foundation + conversation (+ H-MEM tail suffix when relocated).
274
+ # The tail suffix goes AFTER the repaired conversation so it never participates
275
+ # in tool-sequence repair (same protection it had in the foundation), while
276
+ # keeping the foundation+conversation prefix cache-stable across steps.
277
+ messages = foundation + conversation
278
+ if hmem_msg is not None:
279
+ messages.append(hmem_msg)
280
+
281
+ # Model-specific handling
282
+ # Message types imported at module level from modules.llm.messages
283
+ if self.model_name == 'deepseek-reasoner':
284
+ messages = self.merge_successive_messages(messages, HumanMessage)
285
+ messages = self.merge_successive_messages(messages, AIMessage)
286
+ self.logger.debug("Applied deepseek-reasoner message merging")
287
+
288
+ # Step 5: Log the final message structure for debugging
289
+ if self.use_native_tools:
290
+ self._log_message_structure(messages)
291
+
292
+ # Step 6: Apply non-native conversion if not using native tools
293
+ if not self.use_native_tools:
294
+ messages = self.convert_messages_for_non_function_calling_models(messages)
295
+
296
+ # ✅ FIX #1.1: CRITICAL ASSERTION - Ensure system message is present
297
+ # This validates that the system message with brain state instructions reaches the LLM
298
+ # SystemMessage imported at module level from modules.llm.messages
299
+ if not messages or not isinstance(messages[0], SystemMessage):
300
+ error_msg = (
301
+ "CRITICAL ERROR: System message missing from message history! "
302
+ "This will cause the LLM to not return brain state JSON. "
303
+ f"Message count: {len(messages)}, "
304
+ f"First message type: {type(messages[0]).__name__ if messages else 'None'}"
305
+ )
306
+ self.logger.error(error_msg)
307
+
308
+ # Check if system message exists anywhere in history
309
+ system_messages = [m for m in messages if isinstance(m, SystemMessage)]
310
+ if system_messages:
311
+ self.logger.error(f"System message found at index {messages.index(system_messages[0])}, should be at index 0")
312
+ else:
313
+ self.logger.error("No system message found anywhere in message list")
314
+
315
+ # FAIL FAST - don't continue with broken state
316
+ from core.exceptions import AgentError
317
+ raise AgentError(error_msg)
318
+
319
+ # Verify system message contains brain state instructions
320
+ system_content = str(messages[0].content)
321
+ if "brain state" not in system_content.lower() and "text content" not in system_content.lower():
322
+ self.logger.warning(
323
+ "System message present but may not contain brain state instructions. "
324
+ f"Content preview: {system_content[:200]}"
325
+ )
326
+
327
+ self.logger.debug(f"Final messages for LLM: {len(messages)} messages")
328
+ self.logger.debug("✅ System message validation passed")
329
+ return messages
330
+
331
+
332
+ def calculate_llm_timeout(self, tool_count: int = 0, use_vision: bool = False) -> float:
333
+ """Calculate dynamic timeout based on CURRENT token count and complexity.
334
+
335
+ Args:
336
+ tool_count: Number of tools available to the LLM
337
+ use_vision: Whether vision is enabled
338
+
339
+ Returns:
340
+ Timeout in seconds, scaled based on complexity
341
+ """
342
+ import os
343
+
344
+ # Use current token count (no parameter needed!)
345
+ estimated_tokens = self.history.total_tokens + self._system_message_tokens + self._initial_task_tokens
346
+
347
+ # Base timeout on token count
348
+ # INCREASED: Complex code generation can take 120-180s even with small contexts
349
+ # The bottleneck is LLM generation time, not just token processing
350
+ if estimated_tokens < 5000:
351
+ timeout = 120.0 # 2 minutes for small contexts (code gen can be slow)
352
+ elif estimated_tokens < 10000:
353
+ timeout = 150.0 # 2.5 minutes for medium contexts
354
+ elif estimated_tokens < 30000:
355
+ timeout = 180.0 # 3 minutes for large contexts
356
+ elif estimated_tokens < 50000:
357
+ timeout = 240.0 # 4 minutes for very large contexts
358
+ elif estimated_tokens < 100000:
359
+ timeout = 360.0 # 6 minutes for huge contexts
360
+ elif estimated_tokens < 500000:
361
+ timeout = 480.0 # 8 minutes for massive contexts
362
+ else:
363
+ timeout = 600.0 # 10 minutes max for 500K+ contexts
364
+
365
+ # Adjust for tool count (large tool sets add processing time)
366
+ if tool_count > 20:
367
+ timeout += 30.0 # Extra 30s for 20+ tools
368
+ self.logger.debug(f"Added 30s timeout for {tool_count} tools")
369
+ elif tool_count > 10:
370
+ timeout += 15.0 # Extra 15s for 10-20 tools
371
+ self.logger.debug(f"Added 15s timeout for {tool_count} tools")
372
+
373
+ # Adjust for vision (image processing adds time)
374
+ if use_vision:
375
+ timeout += 20.0 # Extra 20s for vision processing
376
+ self.logger.debug(f"Added 20s timeout for vision")
377
+
378
+ # Minimum timeout for first call (model loading, code generation, etc.)
379
+ # Complex code generation tasks can take 120-180s even with small prompts
380
+ timeout = max(timeout, 120.0) # At least 2 minutes
381
+
382
+ # Add environment override for debugging
383
+ env_timeout = os.getenv('AUTOV2_LLM_TIMEOUT_OVERRIDE')
384
+ if env_timeout:
385
+ try:
386
+ timeout = float(env_timeout)
387
+ self.logger.info(f"Using environment timeout override: {timeout:.0f}s")
388
+ except ValueError:
389
+ self.logger.warning(f"Invalid timeout override value: {env_timeout}")
390
+
391
+ self.logger.info(f"Context: {estimated_tokens:,} tokens, {tool_count} tools, vision={use_vision} → {timeout:.0f}s timeout")
392
+ return timeout
393
+
394
+ def remove_last_state_message(self) -> None:
395
+ """Remove the last state message from history to avoid bloat.
396
+
397
+ State messages are typically HumanMessages containing browser state or
398
+ other contextual information. This method removes them after processing
399
+ to prevent history bloat.
400
+ """
401
+ # SECURITY FIX: Protect iteration and removal with lock
402
+ with self._history_lock:
403
+ if not self.history.messages:
404
+ return
405
+
406
+ # CX-H1: tag-first removal. A shape-based scan ("list content OR
407
+ # contains 'Current url:'") can't tell a minimal (non-browser)
408
+ # state message apart from a user's multimodal image turn — both
409
+ # can be the nearest match, and the wrong one gets deleted.
410
+ # add_state_message now stamps every state message it builds with
411
+ # metadata["state_message"]=True, so scan for the tag first across
412
+ # the WHOLE history. Only if no tagged message exists anywhere
413
+ # (e.g. history loaded from disk that predates the tag) fall back
414
+ # once to the old shape heuristic.
415
+ for i in range(len(self.history.messages) - 1, -1, -1):
416
+ msg = self.history.messages[i].message if hasattr(self.history.messages[i], 'message') else self.history.messages[i]
417
+ if isinstance(msg, HumanMessage) and (getattr(msg, "metadata", None) or {}).get("state_message"):
418
+ self.history.remove_message(i)
419
+ self.logger.debug(f"Removed tagged state message at index {i}")
420
+ return
421
+
422
+ # Legacy fallback: no tagged message found anywhere in history.
423
+ for i in range(len(self.history.messages) - 1, -1, -1):
424
+ msg = self.history.messages[i].message if hasattr(self.history.messages[i], 'message') else self.history.messages[i]
425
+ if isinstance(msg, HumanMessage):
426
+ # Check if it looks like a state message (pre-tag heuristic)
427
+ if isinstance(msg.content, list) or (isinstance(msg.content, str) and 'Current url:' in msg.content):
428
+ self.history.remove_message(i)
429
+ self.logger.debug(f"Removed state message (legacy shape fallback) at index {i}")
430
+ return
431
+
432
+
433
+ def get_llm_parameters(self) -> dict:
434
+ """Extract LLM parameters for logging and telemetry.
435
+
436
+ Returns:
437
+ Dictionary of LLM parameters
438
+ """
439
+ params = {}
440
+
441
+ # Get class name for type detection
442
+ class_name = self.llm.__class__.__name__
443
+
444
+ # Extract common parameters based on LLM type
445
+ if 'OpenAI' in class_name:
446
+ # OpenAI parameters
447
+ for attr in ['model_name', 'temperature', 'max_tokens', 'top_p',
448
+ 'frequency_penalty', 'presence_penalty', 'timeout', 'seed']:
449
+ if hasattr(self.llm, attr):
450
+ params[attr if attr != 'model_name' else 'model'] = getattr(self.llm, attr)
451
+
452
+ elif 'Anthropic' in class_name:
453
+ # Anthropic parameters
454
+ for attr in ['model_name', 'temperature', 'max_tokens', 'top_k', 'top_p', 'timeout']:
455
+ if hasattr(self.llm, attr):
456
+ params[attr if attr != 'model_name' else 'model'] = getattr(self.llm, attr)
457
+
458
+ elif 'Google' in class_name or 'Gemini' in class_name:
459
+ # Google parameters
460
+ for attr in ['model_name', 'temperature', 'max_tokens', 'top_k', 'top_p']:
461
+ if hasattr(self.llm, attr):
462
+ params[attr if attr != 'model_name' else 'model'] = getattr(self.llm, attr)
463
+
464
+ else:
465
+ # Generic parameters
466
+ for attr in ['model_name', 'model', 'temperature', 'max_tokens']:
467
+ if hasattr(self.llm, attr):
468
+ value = getattr(self.llm, attr)
469
+ if value is not None:
470
+ params[attr] = value
471
+
472
+ # Add MessageManager-specific parameters
473
+ params['use_native_tools'] = self.use_native_tools
474
+ params['max_input_tokens'] = self.max_input_tokens
475
+ params['provider'] = self.provider_name
476
+
477
+ return params
478
+
479
+
480
+ def _log_message_structure(self, messages: List[BaseMessage]) -> None:
481
+ """Log message structure for debugging native tools."""
482
+ if self.logger.level > logging.DEBUG:
483
+ return
484
+
485
+ self.logger.debug("[NATIVE_TOOLS] Final message structure:")
486
+ for i, msg in enumerate(messages[-10:]): # Last 10 messages
487
+ if isinstance(msg, SystemMessage):
488
+ self.logger.debug(f" [{i}] SystemMessage")
489
+ elif isinstance(msg, HumanMessage):
490
+ self.logger.debug(f" [{i}] HumanMessage: {msg.content[:50]}...")
491
+ elif isinstance(msg, AIMessage):
492
+ if hasattr(msg, 'tool_calls') and msg.tool_calls:
493
+ self.logger.debug(f" [{i}] AIMessage with {len(msg.tool_calls)} tool_calls")
494
+ else:
495
+ self.logger.debug(f" [{i}] AIMessage: {msg.content[:50]}...")
496
+ elif isinstance(msg, ToolMessage):
497
+ self.logger.debug(f" [{i}] ToolMessage for {msg.tool_call_id}: {msg.content[:50]}...")
498
+
499
+
500
+ # === Context overflow recovery ===
501
+
502
+
503
+
504
+
505
+
506
+
@@ -0,0 +1,85 @@
1
+ """SqliteMessageStore — durable, queryable per-session message history.
2
+
3
+ Drop-in alternative to the JSON-blob persistence (PersistenceMixin.save_to_disk),
4
+ opt-in via MESSAGE_STORE_BACKEND=sqlite. WAL + jittered retry via core/sqlite_util,
5
+ matching the SESSION_REGISTRY_BACKEND=sqlite precedent. Stores each message as a JSON
6
+ row keyed by (session_id, seq) so ordering and per-session isolation are preserved.
7
+ """
8
+ import json
9
+ import logging
10
+ import os
11
+ from typing import Any, Dict, List
12
+
13
+ from core.sqlite_util import execute_retry, wal_connect
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class SqliteMessageStore:
19
+ def __init__(self, db_path: str):
20
+ parent = os.path.dirname(db_path)
21
+ if parent:
22
+ os.makedirs(parent, exist_ok=True)
23
+ self.db_path = db_path
24
+ self._init_schema()
25
+
26
+ def _init_schema(self) -> None:
27
+ conn = wal_connect(self.db_path)
28
+ try:
29
+ conn.execute(
30
+ "CREATE TABLE IF NOT EXISTS messages ("
31
+ " session_id TEXT NOT NULL,"
32
+ " seq INTEGER NOT NULL,"
33
+ " payload TEXT NOT NULL,"
34
+ " PRIMARY KEY (session_id, seq)"
35
+ ")"
36
+ )
37
+ conn.commit()
38
+ finally:
39
+ conn.close()
40
+
41
+ def append(self, session_id: str, message: Dict[str, Any]) -> None:
42
+ row = execute_retry(
43
+ self.db_path,
44
+ "SELECT COALESCE(MAX(seq), -1) AS m FROM messages WHERE session_id = ?",
45
+ (session_id,),
46
+ fetch="one",
47
+ )
48
+ next_seq = (row["m"] if row else -1) + 1
49
+ execute_retry(
50
+ self.db_path,
51
+ "INSERT INTO messages (session_id, seq, payload) VALUES (?, ?, ?)",
52
+ (session_id, next_seq, json.dumps(message)),
53
+ )
54
+
55
+ def replace_all(self, session_id: str, messages: List[Dict[str, Any]]) -> None:
56
+ """Atomically replace every row for a session in ONE connection/transaction.
57
+
58
+ Preferred over clear()+append()-loop: avoids the SELECT-MAX+INSERT PK race
59
+ (seq is assigned by enumerate inside the txn) and collapses ~2n+1 connection
60
+ open/close cycles per save down to 1 (MED-4).
61
+ """
62
+ conn = wal_connect(self.db_path)
63
+ try:
64
+ conn.execute("DELETE FROM messages WHERE session_id = ?", (session_id,))
65
+ conn.executemany(
66
+ "INSERT INTO messages (session_id, seq, payload) VALUES (?, ?, ?)",
67
+ [(session_id, i, json.dumps(m)) for i, m in enumerate(messages)],
68
+ )
69
+ conn.commit()
70
+ finally:
71
+ conn.close()
72
+
73
+ def load(self, session_id: str) -> List[Dict[str, Any]]:
74
+ rows = execute_retry(
75
+ self.db_path,
76
+ "SELECT payload FROM messages WHERE session_id = ? ORDER BY seq ASC",
77
+ (session_id,),
78
+ fetch="all",
79
+ )
80
+ return [json.loads(r["payload"]) for r in (rows or [])]
81
+
82
+ def clear(self, session_id: str) -> None:
83
+ execute_retry(
84
+ self.db_path, "DELETE FROM messages WHERE session_id = ?", (session_id,)
85
+ )