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,480 @@
1
+ """Error-recovery + provider-failover mixin (roadmap P9; code-motion from llm_runner.py).
2
+
3
+ Step-error handling, progress recovery, LLM provider fallback, and the
4
+ provider-failure / fallback-success telemetry. Split out of llm_runner.py so that
5
+ file owns LLM *invocation* and this one owns *error recovery / failover*. ``Agent``
6
+ composes ``ErrorRecoveryMixin``; call sites (step.py, get_next_action) unchanged
7
+ via MRO.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import logging
13
+ import time
14
+ import traceback
15
+ from typing import Any, List, Optional
16
+
17
+ from pydantic import ValidationError
18
+
19
+ try:
20
+ from google.api_core.exceptions import ResourceExhausted
21
+ except ImportError: # google-api-core not installed
22
+ class ResourceExhausted(Exception):
23
+ pass
24
+
25
+ # NOTE: AgentError is imported from views (not core.exceptions) because only views'
26
+ # AgentError exposes format_error(), which _handle_step_error uses at its first line.
27
+ # core.exceptions.AgentError has no such method (importing it there crashes on any error).
28
+ from agents.task.agent.views import ActionResult, AgentError
29
+ from agents.task.telemetry.views import ProviderFailureEvent, ProviderFallbackSuccessEvent
30
+ from agents.task.utils import time_execution_async
31
+ from core.env import bool_env as _bool_env
32
+ from core.exceptions import InsufficientCreditsError
33
+ from core.exceptions import (
34
+ RateLimitError,
35
+ LLMError,
36
+ LLMRateLimitError,
37
+ LLMAuthenticationError,
38
+ LLMConnectionError,
39
+ LLMContextLengthError,
40
+ LLMResponseError,
41
+ LLMPermanentError,
42
+ LLMProviderExhaustedError,
43
+ )
44
+
45
+
46
+ def _billing_failover_enabled() -> bool:
47
+ """Whether billing errors should attempt provider fallback (default off)."""
48
+ return _bool_env("BILLING_FAILOVER_ENABLED", False)
49
+
50
+
51
+ class ErrorRecoveryMixin:
52
+ """Step-error handling + LLM provider fallback + failover telemetry for Agent."""
53
+
54
+ async def _recover_from_error(self, error: Exception) -> None:
55
+ """Comprehensive error recovery with state reset and resource cleanup.
56
+
57
+ Args:
58
+ error: The exception that triggered recovery
59
+ """
60
+ self.logger.info("Starting comprehensive error recovery")
61
+
62
+ # 1. Clear any pending operations
63
+ if hasattr(self, '_pending_operations'):
64
+ self._pending_operations = []
65
+
66
+ # 2. Reset approval state if stuck
67
+ # NOTE: Approval system simplified - no state tracking needed
68
+ if hasattr(self, 'hitl_manager'):
69
+ self.logger.debug("Approval system simplified - no state reset needed")
70
+
71
+ # 3. Clean up browser state if browser-related error
72
+ if 'browser' in str(error).lower() or 'playwright' in str(error).lower():
73
+ browser_context = await self.get_browser_context()
74
+ if browser_context:
75
+ try:
76
+ # Navigate to safe page
77
+ pages = browser_context.pages
78
+ if pages:
79
+ await pages[0].goto('about:blank', timeout=5000)
80
+ self.logger.info("Reset browser to blank page")
81
+ except Exception as browser_error:
82
+ self.logger.warning(f"Could not reset browser: {browser_error}")
83
+
84
+ # 4. Clear any cached results that might be corrupt
85
+ if hasattr(self, '_last_result'):
86
+ self._last_result = None
87
+
88
+ # 6. Reset consecutive failure counter if it's getting too high
89
+ if self.state.consecutive_failures >= self.max_failures - 1:
90
+ # Last chance - checkpoint and warn
91
+ self.logger.warning("Near max failures - checkpointing state")
92
+ self.message_manager.checkpoint_history()
93
+
94
+ # Force garbage collection
95
+ import gc
96
+ gc.collect()
97
+
98
+ # 7. Clear any tool-specific state
99
+ if self.controller:
100
+ # Reset any tool state that might be stuck
101
+ if hasattr(self.controller, 'reset_tool_state'):
102
+ try:
103
+ await self.controller.reset_tool_state()
104
+ except Exception:
105
+ pass # Best effort
106
+
107
+ # 8. Log recovery completion
108
+ self.logger.info("Error recovery completed")
109
+
110
+ async def _handle_step_error(self, error: Exception) -> list[ActionResult]:
111
+ """Enhanced error handling with LLM fallback support (Dec 2025).
112
+
113
+ UPGRADED: Now triggers LLM fallback for recoverable errors instead of
114
+ just waiting and retrying with the same broken client.
115
+ """
116
+ include_trace = self.logger.isEnabledFor(logging.DEBUG)
117
+ error_msg = AgentError.format_error(error, include_trace=include_trace)
118
+ prefix = f'❌ Result failed {self.state.consecutive_failures + 1}/{self.max_failures} times:\n '
119
+ error_str_lower = str(error).lower()
120
+ error_type = type(error).__name__
121
+
122
+ # === CONTEXT-LENGTH OVERFLOW - COMPACT, DON'T FALLBACK (CO-F6) ===
123
+ # A context-overflow needs a SMALLER prompt, not a different provider — retrying
124
+ # on a fallback provider with the same oversized history just overflows again.
125
+ # Must run BEFORE the generic is_llm_error block below (which would otherwise
126
+ # route this typed error into a blind provider-fallback retry).
127
+ if isinstance(error, LLMContextLengthError):
128
+ self.logger.warning("Context-length overflow — checkpoint + emergency prune, then retry")
129
+ self.message_manager.checkpoint_history()
130
+ self.message_manager.emergency_context_prune()
131
+ self.state.consecutive_failures += 1
132
+ return [] # retry next step with pruned history
133
+
134
+ # === PERMANENT/CRITICAL ERRORS - HALT IMMEDIATELY ===
135
+ # These errors indicate account-level issues that won't be resolved by fallback
136
+ is_permanent = (
137
+ isinstance(error, LLMPermanentError) or
138
+ isinstance(error, LLMAuthenticationError) or
139
+ 'insufficient_quota' in error_str_lower or
140
+ 'billing' in error_str_lower or
141
+ 'invalid_api_key' in error_str_lower or
142
+ 'account_deactivated' in error_str_lower
143
+ )
144
+
145
+ if is_permanent:
146
+ import os
147
+ is_billing = ('insufficient_quota' in error_str_lower or 'billing' in error_str_lower)
148
+ if is_billing and _billing_failover_enabled():
149
+ self.logger.warning("💳 Billing error with BILLING_FAILOVER_ENABLED — attempting provider fallback")
150
+ # HIGH-2: record the exhausted provider BEFORE failover so repeated billing
151
+ # errors accumulate the failed-set (prevents A↔B ping-pong / step-budget burn).
152
+ # reset_llm_errors() below clears only error-type counts, not this set.
153
+ self.state.track_llm_error("billing", self._get_provider_from_model(self.model_name))
154
+ if await self._attempt_llm_fallback_in_handler("billing"):
155
+ self.logger.info("✅ Billing failover succeeded; continuing on fallback provider")
156
+ self.state.reset_llm_errors()
157
+ return []
158
+ self.logger.error("❌ Billing failover found no alternative provider; halting")
159
+ self.logger.error(f"❌ PERMANENT ERROR - Session halted: {error}")
160
+ self.state.consecutive_failures = self.max_failures
161
+ self.state.stopped = True
162
+ return [ActionResult(
163
+ error=f"PERMANENT ERROR: {str(error)[:400]}. Session halted. Please check API configuration.",
164
+ include_in_memory=True
165
+ )]
166
+
167
+ # === PROVIDER EXHAUSTED - All fallbacks failed ===
168
+ if isinstance(error, LLMProviderExhaustedError):
169
+ self.logger.error(f"❌ ALL LLM PROVIDERS EXHAUSTED: {error}")
170
+ self.state.consecutive_failures = self.max_failures
171
+ self.state.stopped = True
172
+ providers = getattr(error, 'providers_tried', [])
173
+ return [ActionResult(
174
+ error=f"All LLM providers failed. Tried: {providers}. Session halted.",
175
+ include_in_memory=True
176
+ )]
177
+
178
+ # CRITICAL: State recovery and cleanup (only for non-critical errors)
179
+ try:
180
+ await self._recover_from_error(error)
181
+ except Exception as recovery_error:
182
+ self.logger.error(f"Failed to recover from error: {recovery_error}", exc_info=True)
183
+
184
+ # Enhanced error message creation with better context
185
+ if not error_msg or error_msg.strip() == '':
186
+ error_msg = f"Unknown error of type {error_type}"
187
+ if hasattr(error, "__str__"):
188
+ error_detail = str(error)
189
+ if error_detail:
190
+ error_msg += f": {error_detail}"
191
+
192
+ # Add context information for debugging
193
+ if hasattr(error, '__dict__'):
194
+ error_context = {k: v for k, v in error.__dict__.items() if not k.startswith('_')}
195
+ if error_context:
196
+ error_msg += f"\nError context: {error_context}"
197
+
198
+ if include_trace and hasattr(error, "__traceback__"):
199
+ import traceback
200
+ error_msg += f"\n\nTraceback:\n{traceback.format_tb(error.__traceback__)}"
201
+
202
+ # === LLM ERRORS - TRY FALLBACK (Dec 2025 upgrade) ===
203
+ is_llm_error = isinstance(error, (
204
+ LLMError, LLMRateLimitError, LLMConnectionError, LLMContextLengthError
205
+ )) or any(x in error_str_lower for x in ['rate_limit', 'rate limit', '429', 'connection', 'timeout', 'llm'])
206
+
207
+ if is_llm_error:
208
+ # Track error for circuit breaker
209
+ current_provider = self._get_provider_from_model(self.model_name)
210
+ circuit_tripped = self.state.track_llm_error(error_type, current_provider)
211
+
212
+ if circuit_tripped:
213
+ self.logger.error(f"🚨 CIRCUIT BREAKER TRIPPED: Same error {error_type} repeated {self.state.CIRCUIT_BREAKER_THRESHOLD}+ times")
214
+ # Try one last fallback before giving up
215
+ fallback_result = await self._attempt_llm_fallback_in_handler(error_type)
216
+ if fallback_result:
217
+ self.logger.info(f"✅ Circuit breaker: Fallback successful, continuing with new provider")
218
+ self.state.reset_llm_errors(reset_circuit_breaker=True)
219
+ return [] # Empty result = continue execution
220
+ else:
221
+ self.logger.error(f"❌ Circuit breaker: No fallback available, halting")
222
+ self.state.stopped = True
223
+ self.state.consecutive_failures = self.max_failures
224
+ # Raise typed exception for proper handling upstream
225
+ raise LLMProviderExhaustedError(
226
+ f"All LLM providers exhausted after {error_type}",
227
+ providers_tried=list(self.state.llm_providers_failed)
228
+ )
229
+
230
+ # First occurrence or not yet at threshold - try immediate fallback
231
+ self.logger.warning(f'{prefix}LLM Error ({error_type}): {error_msg[:200]}')
232
+
233
+ if self.state.consecutive_failures >= 1: # Already failed once, try fallback
234
+ fallback_result = await self._attempt_llm_fallback_in_handler(error_type)
235
+ if fallback_result:
236
+ self.logger.info(f"✅ Fallback successful after {error_type}")
237
+ self.state.reset_llm_errors()
238
+ return [] # Continue with new LLM
239
+
240
+ # Backoff before retry with current provider
241
+ import random
242
+ base_delay = self.retry_delay * (2 ** self.state.consecutive_failures)
243
+ jitter = random.uniform(0.8, 1.2)
244
+ delay = min(base_delay * jitter, 120)
245
+ self.logger.info(f'Waiting {delay:.1f}s before retry...')
246
+ await asyncio.sleep(delay)
247
+ self.state.consecutive_failures += 1
248
+
249
+ return [ActionResult(error=error_msg, include_in_memory=True)]
250
+
251
+ # === VALIDATION/PARSE ERRORS ===
252
+ if isinstance(error, (ValidationError, ValueError)):
253
+ self.logger.error(f'{prefix}{error_msg}', exc_info=True)
254
+ if 'Max token limit reached' in error_msg or 'context length' in error_msg.lower():
255
+ # Token limit exceeded - checkpoint and warn
256
+ self.logger.warning(f'Token limit exceeded - consider checkpointing or using smaller context')
257
+ self.message_manager.checkpoint_history()
258
+
259
+ elif 'Could not parse response' in error_msg or 'json' in error_msg.lower():
260
+ # Enhanced JSON parsing error recovery
261
+ error_msg += '\n\nIMPORTANT: Return a valid JSON object with the required fields. Ensure all JSON is properly formatted and escaped.'
262
+
263
+ # Try to recover by reducing complexity
264
+ if hasattr(self, 'max_actions_per_step') and self.max_actions_per_step > 1:
265
+ self.max_actions_per_step = max(1, self.max_actions_per_step - 1)
266
+ self.logger.info(f'Reducing max actions per step to {self.max_actions_per_step} due to parse errors')
267
+
268
+ # Smart backoff for parse errors
269
+ delay = min(self.retry_delay * (1.5 ** self.state.consecutive_failures), 30)
270
+ self.logger.info(f'Waiting {delay:.1f}s before retry due to parse error...')
271
+ await asyncio.sleep(delay)
272
+
273
+ self.state.consecutive_failures += 1
274
+
275
+ elif isinstance(error, RateLimitError) or isinstance(error, ResourceExhausted):
276
+ # IMPORTANT: Rate limits due to quota issues are critical
277
+ if 'quota' in error_str_lower or 'billing' in error_str_lower:
278
+ self.logger.error(f"❌ CRITICAL: Rate limit due to quota/billing issue")
279
+ self.state.consecutive_failures = self.max_failures
280
+ self.state.stopped = True
281
+ return [ActionResult(
282
+ error=f"CRITICAL: {str(error)[:400]}. Session halted due to quota/billing issue.",
283
+ include_in_memory=True
284
+ )]
285
+
286
+ # Regular rate limit - can retry with fallback
287
+ self.logger.warning(f'{prefix}{error_msg}')
288
+
289
+ # Track for circuit breaker and try fallback
290
+ current_provider = self._get_provider_from_model(self.model_name)
291
+ self.state.track_llm_error('RateLimitError', current_provider)
292
+
293
+ if self.state.consecutive_failures >= 1:
294
+ fallback_result = await self._attempt_llm_fallback_in_handler('RateLimitError')
295
+ if fallback_result:
296
+ self.logger.info(f"✅ Switched to fallback provider after rate limit")
297
+ self.state.reset_llm_errors()
298
+ return []
299
+
300
+ # Enhanced rate limit handling with jitter
301
+ import random
302
+ base_delay = self.retry_delay * (2 ** self.state.consecutive_failures)
303
+ jitter = random.uniform(0.8, 1.2)
304
+ delay = min(base_delay * jitter, 120)
305
+ self.logger.info(f'Rate limited - waiting {delay:.1f}s before retry (with jitter)')
306
+ await asyncio.sleep(delay)
307
+ self.state.consecutive_failures += 1
308
+
309
+ elif isinstance(error, ConnectionError) or 'connection' in error_msg.lower():
310
+ self.logger.warning(f'{prefix}Connection error: {error_msg}')
311
+
312
+ # Track for circuit breaker and try fallback
313
+ current_provider = self._get_provider_from_model(self.model_name)
314
+ self.state.track_llm_error('ConnectionError', current_provider)
315
+
316
+ if self.state.consecutive_failures >= 1:
317
+ fallback_result = await self._attempt_llm_fallback_in_handler('ConnectionError')
318
+ if fallback_result:
319
+ self.logger.info(f"✅ Switched to fallback provider after connection error")
320
+ self.state.reset_llm_errors()
321
+ return []
322
+
323
+ # Progressive backoff for connection errors
324
+ delay = min(5 * (1.5 ** self.state.consecutive_failures), 60)
325
+ self.logger.info(f'Connection error - waiting {delay:.1f}s before retry')
326
+ await asyncio.sleep(delay)
327
+ self.state.consecutive_failures += 1
328
+
329
+ else:
330
+ self.logger.error(f'{prefix}{error_msg}', exc_info=True)
331
+ # For unknown errors, add a small delay to prevent rapid retries
332
+ if self.state.consecutive_failures > 0:
333
+ delay = min(2 * self.state.consecutive_failures, 10)
334
+ self.logger.info(f'Unknown error - brief delay of {delay}s before retry')
335
+ await asyncio.sleep(delay)
336
+ self.state.consecutive_failures += 1
337
+
338
+ return [ActionResult(error=error_msg, include_in_memory=True)]
339
+
340
+ async def _attempt_llm_fallback_in_handler(self, original_error_type: str) -> bool:
341
+ """Attempt to switch to a fallback LLM provider.
342
+
343
+ Called from _handle_step_error when LLM errors occur.
344
+
345
+ Args:
346
+ original_error_type: The error type that triggered fallback
347
+
348
+ Returns:
349
+ True if fallback successful, False otherwise
350
+ """
351
+ try:
352
+ current_provider = self._get_provider_from_model(self.model_name)
353
+ exclude_providers = list(self.state.llm_providers_failed) + [current_provider]
354
+
355
+ self.logger.info(f"🔄 Attempting LLM fallback (excluding: {exclude_providers})")
356
+
357
+ fallback_llm = await self._get_fallback_llm(
358
+ exclude_providers=exclude_providers,
359
+ original_model=self.model_name
360
+ )
361
+
362
+ if fallback_llm:
363
+ # Store original for telemetry
364
+ original_model = self.model_name
365
+
366
+ # Switch to fallback
367
+ self.llm = fallback_llm
368
+ self.model_name = getattr(fallback_llm, 'model_name', 'fallback')
369
+
370
+ self.logger.info(f"✅ Switched LLM: {original_model} → {self.model_name}")
371
+
372
+ # Emit telemetry
373
+ self._emit_fallback_success_telemetry(
374
+ original_provider=current_provider,
375
+ original_model=original_model,
376
+ fallback_provider=self._get_provider_from_model(self.model_name),
377
+ fallback_model=self.model_name,
378
+ original_error_type=original_error_type,
379
+ total_attempts=len(self.state.llm_providers_failed) + 1,
380
+ total_time=0.0
381
+ )
382
+
383
+ return True
384
+ else:
385
+ self.logger.warning(f"❌ No fallback LLM available")
386
+ return False
387
+
388
+ except Exception as e:
389
+ self.logger.error(f"Fallback attempt failed: {e}")
390
+ return False
391
+
392
+ async def _get_fallback_llm(
393
+ self,
394
+ exclude_providers: list,
395
+ original_model: str
396
+ ) -> Optional[Any]:
397
+ """Get a fallback LLM from LLMManager.
398
+
399
+ Args:
400
+ exclude_providers: List of providers to exclude (already failed)
401
+ original_model: Original model name for context
402
+
403
+ Returns:
404
+ Chat model or None if no fallback available
405
+ """
406
+ try:
407
+ llm_manager = self.container.get_service('llm') if self.container else None
408
+ if not llm_manager:
409
+ self.logger.warning("LLMManager not available - cannot get fallback")
410
+ return None
411
+
412
+ # Use LLMManager's fallback method
413
+ if hasattr(llm_manager, 'get_fallback_chat_model'):
414
+ return await llm_manager.get_fallback_chat_model(
415
+ exclude_providers=exclude_providers,
416
+ original_model=original_model,
417
+ temperature=0.0
418
+ )
419
+ else:
420
+ self.logger.warning("LLMManager does not support get_fallback_chat_model")
421
+ return None
422
+
423
+ except Exception as e:
424
+ self.logger.error(f"Error getting fallback LLM: {e}")
425
+ return None
426
+
427
+ def _emit_provider_failure_telemetry(
428
+ self,
429
+ failed_provider: str,
430
+ failed_model: str,
431
+ error_type: str,
432
+ error_message: str,
433
+ attempt_number: int
434
+ ) -> None:
435
+ """Emit telemetry event for provider failure."""
436
+ try:
437
+ if self.telemetry_manager:
438
+ event = ProviderFailureEvent(
439
+ failed_provider=failed_provider,
440
+ failed_model=failed_model,
441
+ error_type=error_type,
442
+ error_message=error_message[:500],
443
+ attempt_number=attempt_number,
444
+ session_id=self.session_id,
445
+ agent_id=self.agent_id,
446
+ step=self.state.n_steps if hasattr(self, 'state') else None
447
+ )
448
+ self.telemetry_manager.capture_event(event)
449
+ except Exception as e:
450
+ self.logger.debug(f"Failed to emit provider failure telemetry: {e}")
451
+
452
+ def _emit_fallback_success_telemetry(
453
+ self,
454
+ original_provider: str,
455
+ original_model: str,
456
+ fallback_provider: str,
457
+ fallback_model: str,
458
+ original_error_type: str,
459
+ total_attempts: int,
460
+ total_time: float
461
+ ) -> None:
462
+ """Emit telemetry event for successful fallback."""
463
+ try:
464
+ if self.telemetry_manager:
465
+ event = ProviderFallbackSuccessEvent(
466
+ original_provider=original_provider,
467
+ original_model=original_model,
468
+ fallback_provider=fallback_provider,
469
+ fallback_model=fallback_model,
470
+ original_error_type=original_error_type,
471
+ total_attempts=total_attempts,
472
+ total_fallback_time_seconds=total_time,
473
+ session_id=self.session_id,
474
+ agent_id=self.agent_id,
475
+ step=self.state.n_steps if hasattr(self, 'state') else None
476
+ )
477
+ self.telemetry_manager.capture_event(event)
478
+ except Exception as e:
479
+ self.logger.debug(f"Failed to emit fallback success telemetry: {e}")
480
+