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,661 @@
1
+
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import base64
6
+ import http
7
+ import importlib
8
+ import json
9
+ import logging
10
+ import os
11
+ import re
12
+ import subprocess
13
+ import traceback
14
+ import time
15
+ from dataclasses import dataclass, field
16
+ from datetime import datetime
17
+ from io import BytesIO
18
+ from pathlib import Path
19
+ from typing import Any, Callable, Dict, List, Optional, Type, TypeVar, Union, Tuple
20
+ from collections import deque # ADDED: Import deque for bounded collections
21
+ from agents.task.agent.tool_call_tracker import ToolCallTracker # Robust tool call ID tracking
22
+
23
+ from dotenv import load_dotenv
24
+
25
+ # Import centralized constants
26
+ from agents.task.constants import (
27
+ IMG_TOKENS,
28
+ LoopDetectionConfig,
29
+ DEFAULT_USER_ID,
30
+ MemoryConfig,
31
+ MAX_MCP_PER_STEP
32
+ )
33
+
34
+ # Import POLYROB exceptions
35
+ from core.exceptions import (
36
+ AgentError,
37
+ ValidationError as ROBValidationError,
38
+ LLMResponseError,
39
+ ToolError
40
+ )
41
+
42
+ # Import billing exception for fail-fast handling
43
+ from core.exceptions import InsufficientCreditsError
44
+
45
+ # Import centralized utilities to avoid repeated inline imports
46
+ from agents.task.utils_json import normalize_action_schema
47
+ # Model limits now come from modules.llm.model_registry
48
+
49
+ # Native message types
50
+ from modules.llm.messages import (
51
+ AIMessage,
52
+ BaseMessage,
53
+ HumanMessage,
54
+ SystemMessage,
55
+ ToolMessage,
56
+ MessageOrigin,
57
+ make_control_message,
58
+ )
59
+ from modules.llm.adapters import BaseChatModel
60
+ from core.exceptions import (
61
+ RateLimitError,
62
+ LLMError,
63
+ LLMRateLimitError,
64
+ LLMAuthenticationError,
65
+ LLMConnectionError,
66
+ LLMContextLengthError,
67
+ LLMResponseError,
68
+ LLMPermanentError,
69
+ LLMProviderExhaustedError
70
+ )
71
+ # PIL Image imported locally in save_screenshot() method where needed
72
+ from pydantic import BaseModel, ConfigDict, ValidationError
73
+
74
+ from tools.browser.views import BrowserStateHistory, BrowserState
75
+ from agents.task.agent.message_manager.service import MessageManager
76
+ from agents.task.agent.core.llm_runner import LLMRunnerMixin
77
+ from agents.task.agent.core.memory_writer import MemoryWriterMixin
78
+ from agents.task.agent.core.step import StepMixin
79
+ from agents.task.agent.core.history_io import HistoryIOMixin
80
+ from agents.task.agent.core.logging_io import LoggingIOMixin
81
+ from agents.task.agent.core.safety_lifecycle import SafetyLifecycleMixin
82
+ from agents.task.agent.core.user_ingress import UserIngressMixin
83
+ from agents.task.agent.core.llm_provisioning import LLMProvisioningMixin
84
+ from agents.task.agent.core.model_introspection import ModelIntrospectionMixin
85
+ from agents.task.agent.core.loop_detection import LoopDetectionMixin
86
+ from agents.task.agent.core.resources import ResourceMixin
87
+ from agents.task.agent.core.session_metadata import SessionMetadataMixin
88
+ from agents.task.agent.core.error_recovery import ErrorRecoveryMixin
89
+ from agents.task.agent.core.step_telemetry import StepTelemetryMixin
90
+ from agents.task.agent.core.output_validation import OutputValidationMixin
91
+ from agents.task.agent.core.result_processing import ResultProcessingMixin
92
+ from agents.task.agent.core.next_action_internal import NextActionInternalMixin
93
+ from agents.task.agent.core.step_execution import StepExecutionMixin
94
+ from agents.task.agent.prompts import SystemPrompt, AgentMessagePrompt
95
+ from agents.task.agent.views import (
96
+ ActionModel,
97
+ AgentBrain,
98
+ AgentError,
99
+ AgentHistory,
100
+ AgentHistoryList,
101
+ AgentOutput,
102
+ AgentStepInfo,
103
+ ActionResult,
104
+ )
105
+ from tools.browser.context import BrowserContext
106
+ from tools.dom.views import DOMElementNode, SelectorMap
107
+ from agents.task.telemetry.views import (
108
+ HumanApprovalRequestedEvent,
109
+ HumanApprovalDecisionEvent,
110
+ TodoStatusEvent,
111
+ AgentRunTelemetryEvent,
112
+ AgentEndTelemetryEvent,
113
+ ProviderFailureEvent,
114
+ ProviderFallbackSuccessEvent,
115
+ )
116
+ # ProductTelemetry no longer directly used - accessed via TelemetryManager
117
+ from agents.task.utils import time_execution_async, detect_llm_provider, extract_token_usage
118
+ # Safely import Google API exceptions
119
+ try:
120
+ from google.api_core.exceptions import ResourceExhausted
121
+ except ImportError:
122
+ # Create a dummy exception class if google-api-core is not installed
123
+ class ResourceExhausted(Exception):
124
+ pass
125
+
126
+ # Import from our own logging config
127
+ from agents.task.logging_config import get_task_logger
128
+
129
+ # Import centralized path management
130
+ from agents.task.path import pm
131
+
132
+ load_dotenv()
133
+
134
+ # Generic logger for the module itself (not instances)
135
+ logger = get_task_logger('agent')
136
+
137
+
138
+ class RunLoopMixin:
139
+ """The agent run loop (run), split whole out of Agent so service.py drops toward
140
+ its constructor core (P9). Agent composes RunLoopMixin; callers use agent.run()
141
+ unchanged via MRO. Imports above are service.py's (incl. module-level logger)."""
142
+
143
+ async def run(self, max_steps: int = 100, _continue_session: bool = False) -> AgentHistoryList:
144
+ """Run agent for the task.
145
+
146
+ Runs the agent to complete the task set during initialization.
147
+
148
+ Args:
149
+ max_steps: Maximum number of steps to take
150
+
151
+ Returns:
152
+ List of history items
153
+ """
154
+ # Log a truncated version of the task to avoid verbose logging
155
+ truncated_task = self.task[:50] + "..." if len(self.task) > 50 else self.task
156
+ self.logger.info(f'Running agent for task: {truncated_task}')
157
+
158
+ # Track start time for metrics
159
+ start_time = time.time()
160
+
161
+ # Initialize step counter - will be managed by step() method
162
+ self.state.n_steps = 0 # Start at 0, will be incremented by step() method
163
+
164
+ # Session-scoped setup (session-start telemetry + hierarchical memory)
165
+ # runs ONCE per session. Conversational turns (Conversation.respond with
166
+ # _continue_session=True) reuse the same agent and skip it to avoid
167
+ # telemetry spam + redundant H-MEM reloads. Tasks/HITL resume are unchanged.
168
+ if not _continue_session:
169
+ # Capture session start telemetry using TelemetryManager
170
+ try:
171
+ self.telemetry_manager.capture_session_start(
172
+ task=self.task,
173
+ model=self.model_name,
174
+ agent_type=self.__class__.__name__,
175
+ use_vision=self.use_vision,
176
+ internal_planning=False
177
+ )
178
+ except Exception as e:
179
+ self.logger.debug(f"Failed to capture session start telemetry: {e}")
180
+
181
+ # Initialize and create/load hierarchical memory session (MANDATORY)
182
+ try:
183
+ # Initialize TaskContextManager if we created it
184
+ if self._created_task_context_manager:
185
+ await self.task_context_manager._initialize()
186
+ self.logger.info("✅ Initialized dedicated TaskContextManager")
187
+
188
+ # Try to load existing session first
189
+ memory = self.task_context_manager.load_session(self.session_id, self.user_id)
190
+
191
+ if not memory:
192
+ # Create new session
193
+ memory = self.task_context_manager.create_session(
194
+ session_id=self.session_id,
195
+ task=self.task,
196
+ user_id=self.user_id
197
+ )
198
+ self.logger.info(f"📚 Created hierarchical memory session: {self.session_id}")
199
+ else:
200
+ self.logger.info(f"📚 Loaded hierarchical memory session: {self.session_id}")
201
+
202
+ except Exception as e:
203
+ # FAIL HARD - hierarchical memory is mandatory
204
+ error_msg = f"CRITICAL: Cannot initialize hierarchical memory: {e}"
205
+ self.logger.error(error_msg)
206
+ raise RuntimeError(error_msg)
207
+
208
+ try:
209
+ # Do initial actions if provided
210
+ if self.initial_actions:
211
+ try:
212
+ # Get fresh browser context for initial actions
213
+ browser_context = await self.get_browser_context()
214
+ result = await self.controller.multi_act(
215
+ self.initial_actions,
216
+ browser_context=browser_context,
217
+ _page_extraction_llm=self.page_extraction_llm
218
+ )
219
+ self._last_result = result
220
+ self.logger.info(f'Executed initial actions')
221
+ except Exception as e:
222
+ self.logger.error(f'Failed to execute initial actions: {str(e)}', exc_info=True)
223
+
224
+ # Create gif if required
225
+ if self.generate_gif:
226
+ if isinstance(self.generate_gif, str):
227
+ output_path = self.generate_gif
228
+ else:
229
+ if not self.save_conversation_path:
230
+ # Use proper path handling for conversation path
231
+ try:
232
+ # Create proper path in the results directory
233
+ from agents.task.path import pm
234
+ self.save_conversation_path = str(pm().create_file_path(
235
+ self.session_id,
236
+ "logs",
237
+ f"conversation_{str(time.time())}"
238
+ ))
239
+ except Exception as e:
240
+ self.logger.warning(f"Failed to create conversation path: {e}")
241
+
242
+ # Fallback to direct utility functions
243
+ try:
244
+ from agents.task.path import pm
245
+ self.save_conversation_path = str(pm().create_file_path(
246
+ self.session_id,
247
+ "logs",
248
+ f"conversation_{str(time.time())}"
249
+ ))
250
+ except ImportError:
251
+ # Last resort - still use central PathManager to avoid nested path issues
252
+ from agents.task.path import pm
253
+ self.save_conversation_path = str(pm().create_file_path(
254
+ self.session_id,
255
+ "logs",
256
+ f"conversation_{str(time.time())}"
257
+ ))
258
+ else:
259
+ # Fallback path handling using centralized PathManager to avoid directory issues
260
+ from agents.task.path import pm
261
+ self.save_conversation_path = str(pm().create_file_path(
262
+ self.session_id,
263
+ "logs",
264
+ f"conversation_{str(time.time())}"
265
+ ))
266
+
267
+ output_path = f'{self.save_conversation_path}.gif'
268
+ self.logger.debug(f'Will create gif at {output_path}')
269
+
270
+ # Loop until done or max steps reached
271
+ max_failures_reached = False
272
+
273
+ # Log the starting point
274
+ self.logger.info(f"Starting task execution with max {max_steps} steps")
275
+
276
+ # FIX 10: Add progress monitoring with heartbeat
277
+ last_progress_time = time.time()
278
+ PROGRESS_INTERVAL = 30 # Report progress every 30 seconds
279
+ last_activity_time = time.time()
280
+ # Use configurable stall timeout or default to 600 seconds
281
+ STALL_TIMEOUT = self.stall_timeout_seconds if self.stall_timeout_seconds else 600
282
+
283
+ # Import AgentStepInfo for proper step tracking
284
+ from agents.task.agent.views import AgentStepInfo
285
+
286
+ # Start stall monitor task
287
+ if self.stall_timeout_seconds:
288
+ self._stall_check_task = asyncio.create_task(self._stall_monitor_loop())
289
+ self.logger.debug("Started stall monitor task")
290
+
291
+ # Track task in orchestrator for proper cleanup
292
+ if hasattr(self, 'orchestrator') and self.orchestrator:
293
+ if hasattr(self.orchestrator, '_execution_tasks'):
294
+ self.orchestrator._execution_tasks.append(self._stall_check_task)
295
+ self.logger.debug("Registered stall monitor task with orchestrator for cleanup")
296
+
297
+ # CRITICAL: Check for queued messages BEFORE starting execution loop
298
+ # This enables continuous chat - when agent restarts after completion,
299
+ # it picks up messages that were queued while it was idle
300
+ initial_messages = await self._drain_user_messages()
301
+ self.logger.info(f"Checked for queued messages at start: found {len(initial_messages)} messages")
302
+ if initial_messages:
303
+ self.logger.info(f"Processing {len(initial_messages)} queued messages:")
304
+ for i, msg in enumerate(initial_messages):
305
+ msg_text = msg.get('text', '')[:100]
306
+ self.logger.info(f" Message {i+1}: {msg_text}...")
307
+
308
+ # ✅ FIX: Pass continuation context that was set by _drain_user_messages()
309
+ # This triggers the strong "PRIORITY INPUT" signal in inject_user_guidance()
310
+ session_context = getattr(self, '_session_continuation_context', None)
311
+
312
+ # Inject messages into history so LLM sees them
313
+ self.message_manager.inject_user_guidance(initial_messages, session_context=session_context)
314
+
315
+ self.logger.info(
316
+ f"✅ Injected {len(initial_messages)} messages with continuation context "
317
+ f"(continuation={session_context.get('continuation') if session_context else False})"
318
+ )
319
+
320
+ # R1: count consecutive reply-only steps. A turn ends after a run of
321
+ # them (see conversational_exit) so a chat answer doesn't loop and
322
+ # re-greet; any productive step resets the run, so a real task is never
323
+ # cut short. See agent/core/conversational_exit.py for the policy.
324
+ from agents.task.agent.core.conversational_exit import (
325
+ is_reply_only_step, should_conversational_exit,
326
+ )
327
+ consecutive_reply_steps = 0
328
+
329
+ for step_num in range(max_steps):
330
+ # Check for cancellation first
331
+ if self._cancelled:
332
+ self.logger.warning(f"❌ Agent execution cancelled at step {step_num}")
333
+ break
334
+
335
+ # Create step info with correct step number
336
+ step_info = AgentStepInfo(step_number=step_num, max_steps=max_steps)
337
+
338
+ # The agent's step() method will use this to update its internal counter
339
+ self.logger.debug(f"Starting step {step_num + 1}/{max_steps}")
340
+
341
+ # P6 session-registry heartbeat — refresh last_seen_at each step so the
342
+ # periodic reaper (SESSION_REGISTRY_BACKEND=sqlite) spares this live
343
+ # session. Best-effort / fail-open: a no-op for the in-process registry,
344
+ # and must never break the run loop. Resolves the TaskAgent through the
345
+ # existing container accessor (no new globals).
346
+ try:
347
+ from core.container import DependencyContainer
348
+ _task_agent = DependencyContainer.get_instance().get_agent("task_agent")
349
+ if _task_agent is not None and hasattr(_task_agent, "heartbeat_session"):
350
+ _task_agent.heartbeat_session(self.session_id)
351
+ except Exception:
352
+ pass # best-effort; never break the loop on heartbeat
353
+
354
+ # FIX 10: Periodic progress reporting
355
+ current_time = time.time()
356
+ if current_time - last_progress_time > PROGRESS_INTERVAL:
357
+ elapsed = current_time - start_time
358
+ progress_pct = (step_num / max_steps) * 100
359
+ self.logger.info(f"Progress: Step {step_num}/{max_steps} ({progress_pct:.1f}%), elapsed: {elapsed:.1f}s")
360
+
361
+ # Also emit telemetry for progress monitoring
362
+ if hasattr(self, 'session_data'):
363
+ self.session_data['last_progress_update'] = {
364
+ 'timestamp': current_time,
365
+ 'step': step_num,
366
+ 'max_steps': max_steps,
367
+ 'elapsed_seconds': elapsed
368
+ }
369
+ last_progress_time = current_time
370
+
371
+ # FIX 10: Check for stalled execution and take action
372
+ if current_time - last_activity_time > STALL_TIMEOUT:
373
+ self.logger.error(f"Agent appears stalled - no activity for {STALL_TIMEOUT} seconds", exc_info=True)
374
+ # Emit stall warning telemetry
375
+ if hasattr(self, 'session_data'):
376
+ if 'stall_warnings' not in self.session_data:
377
+ self.session_data['stall_warnings'] = []
378
+ self.session_data['stall_warnings'].append({
379
+ 'timestamp': current_time,
380
+ 'step': step_num,
381
+ 'stall_duration': current_time - last_activity_time
382
+ })
383
+
384
+ # CRITICAL: Actually stop execution when stalled
385
+ self.logger.error("Breaking execution due to stall timeout", exc_info=True)
386
+ # Set error state before breaking
387
+ self.state.consecutive_failures = self.max_failures
388
+ # Mark as failed and break
389
+ break
390
+
391
+ if self._too_many_failures():
392
+ max_failures_reached = True
393
+ self.logger.warning(f"Too many consecutive failures ({self.state.consecutive_failures}/{self.max_failures})")
394
+ break
395
+
396
+ if await self._handle_control_flags():
397
+ self.logger.info("Agent execution stopped by control flags")
398
+ break
399
+
400
+ # Check for context overflow before executing step
401
+ if self._check_context_overflow():
402
+ self.logger.info("Context overflow detected and mitigated - continuing")
403
+
404
+ # Execute one step with the correct step info
405
+ try:
406
+ await self.step(step_info=step_info)
407
+ except asyncio.CancelledError:
408
+ self.logger.warning(f"❌ Step execution cancelled at step {step_num + 1}")
409
+ break
410
+
411
+ # FIX 10: Update last activity time after successful step
412
+ last_activity_time = time.time()
413
+
414
+ # UPGRADE (Dec 2025): Reset failure counter on successful step
415
+ # This prevents accumulated failures from previous steps from affecting new steps
416
+ if self._last_result and not any(r.error for r in self._last_result):
417
+ if self.state.consecutive_failures > 0:
418
+ self.logger.info(f"✅ Step succeeded, resetting failure counter (was {self.state.consecutive_failures})")
419
+ self.state.consecutive_failures = 0
420
+ self.state.reset_llm_errors()
421
+
422
+ # Log step completion
423
+ self.logger.debug(f"Completed step {step_num + 1}")
424
+
425
+ # Note: n_steps is now managed by the step() method using step_info
426
+
427
+ # Check for messages that arrived during step
428
+ user_messages = await self._drain_user_messages()
429
+ if user_messages:
430
+ self.logger.info(f"Received {len(user_messages)} messages during step - injecting immediately")
431
+ # Inject messages with context (set by _drain_user_messages)
432
+ session_context = getattr(self, '_session_continuation_context', None)
433
+ self.message_manager.inject_user_guidance(user_messages, session_context=session_context)
434
+ self.logger.info(f"✅ Injected {len(user_messages)} messages into conversation")
435
+
436
+ # R1: track consecutive reply-only steps. A reply-only step is one
437
+ # whose every result is a non-blocking user-facing reply (no tool
438
+ # ran); anything else (productive tool, planning turn, error, done)
439
+ # resets the run.
440
+ results = self._last_result or []
441
+ if is_reply_only_step(results):
442
+ consecutive_reply_steps += 1
443
+ else:
444
+ consecutive_reply_steps = 0
445
+
446
+ # Check if the agent is done
447
+ if results and any(result.is_done for result in results):
448
+ # CO-F1: judge the FINAL answer, not an intermediate step. This
449
+ # replaces the old top-of-loop check (which ran before step() on
450
+ # the previous step's result and could never see the true final
451
+ # answer, since a done() result broke the loop before that check
452
+ # ever re-ran). Gated by AgentConfig.validate_output (env
453
+ # VALIDATE_OUTPUT, default off) — off is byte-identical to legacy.
454
+ # Bound: `continue` re-enters the SAME `for step_num in
455
+ # range(max_steps)` loop, so a judge that always says "invalid"
456
+ # still terminates at max_steps (no unbounded retry loop); the
457
+ # consecutive_failures bump also lets `_too_many_failures()`
458
+ # (checked at the top of the loop) cut it short sooner.
459
+ if self.validate_output and not await self._validate_output():
460
+ self.logger.warning('❌ Final output failed judge validation - continuing task')
461
+ self.state.consecutive_failures += 1
462
+ continue
463
+
464
+ self.logger.info('✅ Task completed - agent called done()')
465
+
466
+ # W2-C: at the turn boundary, maybe fork a background reviewer to
467
+ # distil a durable skill from the run just completed. Productive =
468
+ # the run actually did work (the final step wasn't reply-only).
469
+ # Non-blocking + fail-open + gated BACKGROUND_REVIEW_ENABLED (off).
470
+ # E6 (deliberate scope): this fires ONLY on the done()-completion
471
+ # path — NOT on conversational-exit (reply-only = not productive,
472
+ # correctly skipped) nor max-steps exhaustion (a non-converging /
473
+ # incomplete run is poor skill material and would dilute the
474
+ # "every N productive turns" cadence with failures). The cadence is
475
+ # intentionally measured over COMPLETED productive runs only.
476
+ try:
477
+ self._maybe_spawn_background_review(
478
+ turn_was_productive=not is_reply_only_step(results)
479
+ )
480
+ except Exception:
481
+ pass
482
+
483
+ # CHAT MODE: Break immediately, let session finalize
484
+ # Continuous chat works via:
485
+ # 1. Session status → "completed"
486
+ # 2. User sends new message → queued in HITL manager
487
+ # 3. User initiates new run → agent picks up queued messages from step 3896
488
+ # 4. Agent continues with new task phase
489
+ #
490
+ # This eliminates race conditions from the old 1.5s wait pattern
491
+ break
492
+
493
+ # R1 conversational exit: the agent has produced a run of reply-only
494
+ # steps without calling done() — it's chatting, not working. End the
495
+ # turn so a greeting/chat answer doesn't loop and re-greet. A blocking
496
+ # send or done() already ended the turn via is_done above.
497
+ # T2-08: an autonomous (goal/cron) session is exempt — status
498
+ # narration during a mission is not a greeting loop. Fail-open False.
499
+ try:
500
+ from agents.task.goals.autonomy_marker import is_autonomous
501
+ _is_autonomous_run = is_autonomous(self.session_id)
502
+ except Exception:
503
+ _is_autonomous_run = False
504
+ if should_conversational_exit(
505
+ consecutive_reply_steps, getattr(self, '_is_sub_agent', False),
506
+ is_autonomous=_is_autonomous_run,
507
+ ):
508
+ self.logger.info(
509
+ f'💬 {consecutive_reply_steps} consecutive reply-only steps — '
510
+ f'ending turn (R1 conversational exit)'
511
+ )
512
+ break
513
+
514
+
515
+ if max_failures_reached:
516
+ self.logger.warning(f'Too many consecutive failures')
517
+
518
+ # Create output gif (PIL decode + GIF assembly are blocking — off-loop)
519
+ if self.generate_gif and len(self.history.history) > 0:
520
+ try:
521
+ await asyncio.to_thread(self.create_history_gif, output_path)
522
+ except Exception as e:
523
+ self.logger.warning(f'Failed to create history gif: {str(e)}')
524
+
525
+ # Call done callback if provided
526
+ if self.register_done_callback:
527
+ if callable(self.register_done_callback):
528
+ self.register_done_callback(self.history)
529
+ else:
530
+ self.logger.warning("register_done_callback is set but not callable")
531
+
532
+ # RAM optimization: Final memory cleanup after session completion
533
+ try:
534
+ import gc
535
+
536
+ # Clean up telemetry buffers if available
537
+ self.telemetry_manager.flush_buffers()
538
+
539
+ # Force garbage collection after session completion
540
+ gc.collect()
541
+
542
+ self.logger.debug("Completed final memory cleanup")
543
+ except Exception as cleanup_error:
544
+ self.logger.debug(f"Error during final memory cleanup: {cleanup_error}")
545
+
546
+ # Capture session completion telemetry
547
+ try:
548
+ end_time = time.time()
549
+ duration = end_time - start_time
550
+
551
+ # Determine if the session was successful
552
+ success = False
553
+ if self._last_result and any(result.is_done for result in self._last_result):
554
+ success = True
555
+
556
+ # Extract metrics about the session
557
+ metrics = {
558
+ "steps_completed": self.state.n_steps - 1, # -1 because n_steps is incremented at the end of step
559
+ "consecutive_failures": self.state.consecutive_failures,
560
+ "max_failures_reached": max_failures_reached,
561
+ "token_count": self.message_manager.get_token_count() if hasattr(self.message_manager, "get_token_count") else None,
562
+ "model_name": self.model_name,
563
+ # Single agent handles everything
564
+ }
565
+
566
+ # Get error message if any
567
+ error_message = None
568
+ if self._last_result and any(result.error for result in self._last_result):
569
+ error_messages = [result.error for result in self._last_result if result.error]
570
+ if error_messages:
571
+ error_message = "; ".join(error_messages)
572
+
573
+ # Get final result content if successful
574
+ if success and self._last_result:
575
+ for result in self._last_result:
576
+ if result.is_done and result.extracted_content:
577
+ metrics["final_result"] = result.extracted_content
578
+ break
579
+
580
+ # Capture completion event using TelemetryManager
581
+ self.telemetry_manager.capture_session_end(
582
+ success=success,
583
+ steps=self.state.n_steps - 1,
584
+ duration=duration,
585
+ error_message=error_message,
586
+ metrics=metrics
587
+ )
588
+ except Exception as e:
589
+ self.logger.debug(f"Failed to capture session completion telemetry: {e}")
590
+
591
+ # Log final step count and result
592
+ if self._last_result and any(result.is_done for result in self._last_result):
593
+ self.logger.info(f"Task completed successfully in {self.state.n_steps-1} steps")
594
+ else:
595
+ self.logger.warning(f"Task not completed after {self.state.n_steps-1} steps")
596
+
597
+ # Save hierarchical memory session
598
+ if self.task_context_manager:
599
+ try:
600
+ self.task_context_manager.save_session(
601
+ session_id=self.session_id,
602
+ user_id=self.user_id
603
+ )
604
+ self.logger.info(f"💾 Saved hierarchical memory for session: {self.session_id}")
605
+ except Exception as e:
606
+ self.logger.warning(f"Failed to save hierarchical memory: {e}")
607
+
608
+ # Session cost tracking - credits already deducted per-call by usage_tracker
609
+ # (usage_meter, the pre-usage_tracker fallback, was retired in C5 — see
610
+ # docs/superpowers/plans/2026-07-02-polyrob-console-web-app-finalization-C-payments.md)
611
+ if self.user_id:
612
+ try:
613
+ if self.usage_tracker:
614
+ breakdown = await self.usage_tracker.get_session_breakdown(self.session_id)
615
+ self.logger.info(
616
+ f"💰 Session {self.session_id} summary: "
617
+ f"{breakdown['total_credits_charged']} credits charged, "
618
+ f"${breakdown['total_user_cost_usd']:.4f} user cost"
619
+ )
620
+ except Exception as e:
621
+ self.logger.error(f"Failed to get session cost summary: {e}")
622
+
623
+ # CO-F7: the once-per-SESSION bootstrap flag (episodic digest +
624
+ # continuity bridge, see memory_prefetch.py) is now set from inside
625
+ # step.py, right after the injectors are actually invoked on
626
+ # n_steps == 1 (see the call site there). It is deliberately NOT
627
+ # set here at the end of run() — a turn-1 run() that breaks BEFORE
628
+ # step() ever executes (cancellation, resumed-from-done/stopped,
629
+ # too-many-failures) must leave the flag False so a later real
630
+ # turn still gets its one shot at the digest + continuity bridge.
631
+
632
+ return self.history
633
+ finally:
634
+ # Clean up stall monitor task
635
+ if hasattr(self, '_stall_check_task') and self._stall_check_task:
636
+ self._stall_check_task.cancel()
637
+ try:
638
+ await self._stall_check_task
639
+ except asyncio.CancelledError:
640
+ pass
641
+ self.logger.debug("Cancelled stall monitor task")
642
+
643
+ # Release browser context for this agent
644
+ if self.orchestrator and self.orchestrator.browser_manager:
645
+ try:
646
+ await self.orchestrator.browser_manager.release_context(self.agent_id, close=True)
647
+ self.logger.info(f"Released browser context for {self.agent_id}")
648
+ except Exception as e:
649
+ self.logger.debug(f"Error releasing browser context: {e}")
650
+
651
+ self.telemetry_manager.capture_event(
652
+ AgentEndTelemetryEvent(
653
+ agent_id=self.agent_id,
654
+ success=self.history.is_done(),
655
+ steps=self.state.n_steps,
656
+ max_steps_reached=self.state.n_steps >= max_steps,
657
+ errors=self.history.errors(),
658
+ )
659
+ )
660
+
661
+