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,276 @@
1
+ """Logging / conversation-file output mixin (roadmap P9 decomposition; code-motion from service.py).
2
+
3
+ The response/context/conversation/tool-output logging concern, moved verbatim off
4
+ the ``Agent`` god-file. ``Agent`` composes ``LoggingIOMixin`` so call sites
5
+ (``self._log_response``, ``self._log_context_breakdown``, ``self._save_conversation``,
6
+ ``self._log_tool_outputs`` — invoked from step.py/llm_runner.py) are unchanged.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from datetime import datetime
12
+ from pathlib import Path
13
+ from typing import Any, List
14
+
15
+ from modules.llm.messages import BaseMessage, ToolMessage
16
+ from agents.task.agent.views import AgentOutput, ActionResult
17
+ from agents.task.telemetry.views import AgentRunTelemetryEvent
18
+
19
+
20
+ class LoggingIOMixin:
21
+ """Response/context/conversation/tool-output logging for Agent."""
22
+
23
+ def _log_response(self, response: AgentOutput) -> None:
24
+ """Log the model's response"""
25
+ if 'Success' in response.current_state.evaluation_previous_goal:
26
+ status_indicator = "SUCCESS"
27
+ elif 'Failed' in response.current_state.evaluation_previous_goal:
28
+ status_indicator = "FAILED"
29
+ else:
30
+ status_indicator = "UNKNOWN"
31
+
32
+ # Log concise brain state summary (memory is most important for continuity)
33
+ self.logger.info(f"Evaluation: [{status_indicator}] {response.current_state.evaluation_previous_goal}")
34
+ self.logger.info(f"Memory: {response.current_state.memory}")
35
+
36
+ # Log other fields at debug level to reduce noise
37
+ self.logger.debug(f"Next goal: {response.current_state.next_goal}")
38
+ self.logger.debug(f"Reasoning: {response.current_state.reasoning}")
39
+ if response.current_state.phase:
40
+ self.logger.debug(f"Phase: {response.current_state.phase}")
41
+
42
+ if response.action:
43
+ self.logger.info(f"Actions: {len(response.action)} action(s)")
44
+ for i, action in enumerate(response.action):
45
+ try:
46
+ action_dump = action.model_dump(exclude_unset=True)
47
+ if not action_dump:
48
+ # Handle empty action data
49
+ self.logger.debug(f" Action {i+1}/{len(response.action)}: Empty action data")
50
+ continue
51
+
52
+ # Safely get first key
53
+ action_keys = list(action_dump.keys())
54
+ if not action_keys:
55
+ self.logger.debug(f" Action {i+1}/{len(response.action)}: No keys in action data")
56
+ continue
57
+
58
+ action_type = action_keys[0]
59
+ action_params = action_dump[action_type]
60
+
61
+ # Log both the action type and its parameters at INFO level
62
+ self.logger.info(f" Action {i+1}/{len(response.action)}: {action_type} - Parameters: {action_params}")
63
+ # Still keep the detailed JSON in debug for complete data
64
+ self.logger.debug(f" Details: {action.model_dump_json(exclude_unset=True)}")
65
+ except Exception as e:
66
+ # Log error but continue processing other actions
67
+ self.logger.error(f"Error logging action {i+1}: {str(e)}", exc_info=True)
68
+ continue
69
+
70
+ def _log_context_breakdown(self):
71
+ """Log detailed context usage breakdown.
72
+
73
+ FIX (Context Optimization Phase 2): Real-time visibility into context consumption.
74
+ Logs breakdown by category: Foundation, H-MEM, Conversation, Tools
75
+ """
76
+ try:
77
+ # Diagnostic/logging-only call — must peek, not drain, one-shot
78
+ # ephemeral messages (CX-H3). The real provider call in
79
+ # next_action_internal.py keeps the draining default.
80
+ messages = self.message_manager.get_messages_for_llm(consume_ephemeral=False)
81
+
82
+ # Count by type
83
+ foundation_tokens = 0
84
+ hmem_tokens = 0
85
+ conversation_tokens = 0
86
+ tool_tokens = 0
87
+
88
+ for msg in messages:
89
+ tokens = self.message_manager._count_message_tokens(msg)
90
+
91
+ if msg == self.message_manager._system_message:
92
+ foundation_tokens += tokens
93
+ elif msg == self.message_manager._initial_task_message:
94
+ foundation_tokens += tokens
95
+ elif hasattr(msg, 'content') and '[Session Memory]' in str(msg.content):
96
+ hmem_tokens += tokens
97
+ elif isinstance(msg, ToolMessage):
98
+ tool_tokens += tokens
99
+ else:
100
+ conversation_tokens += tokens
101
+
102
+ total_tokens = sum([foundation_tokens, hmem_tokens, conversation_tokens, tool_tokens])
103
+ usage_pct = (total_tokens / self.message_manager.max_input_tokens) * 100 if self.message_manager.max_input_tokens > 0 else 0
104
+
105
+ # Determine emoji
106
+ if usage_pct >= 65:
107
+ emoji = "🔴"
108
+ elif usage_pct >= 55:
109
+ emoji = "🟡"
110
+ else:
111
+ emoji = "🟢"
112
+
113
+ self.logger.info(
114
+ f"{emoji} Step {self.state.n_steps} Context: {total_tokens:,}/{self.message_manager.max_input_tokens:,} "
115
+ f"({usage_pct:.1f}%) | "
116
+ f"Foundation: {foundation_tokens:,} | "
117
+ f"H-MEM: {hmem_tokens:,} | "
118
+ f"Conv: {conversation_tokens:,} | "
119
+ f"Tools: {tool_tokens:,}"
120
+ )
121
+
122
+ # Warning if approaching compaction
123
+ if usage_pct >= 55:
124
+ self.logger.warning(
125
+ f"⚠️ Context at {usage_pct:.0f}% - will compact at 65%"
126
+ )
127
+
128
+ except Exception as e:
129
+ self.logger.debug(f"Context breakdown failed: {e}")
130
+
131
+ def _save_conversation(self, input_messages: list[BaseMessage], response: Any) -> None:
132
+ """Save conversation to disk or telemetry for later retrieval"""
133
+ from agents.task.utils import safe_operation
134
+
135
+ # If no save path specified, just return
136
+ if not self.save_conversation_path:
137
+ return
138
+
139
+ def save_messages():
140
+ # Ensure path exists
141
+ path = Path(self.save_conversation_path)
142
+ os.makedirs(path.parent, exist_ok=True)
143
+
144
+ # Get encoding to use
145
+ encoding = self.save_conversation_path_encoding or 'utf-8'
146
+
147
+ with open(path, 'a', encoding=encoding) as f:
148
+ # If file is empty, add headers
149
+ if os.path.getsize(path) == 0:
150
+ f.write(f'# Task: {self.task}\n')
151
+ f.write(f'# Created at: {datetime.now().isoformat()}\n')
152
+ f.write(f'# Model: {self.model_name}\n')
153
+ f.write(f'# Agent ID: {self.agent_id}\n')
154
+ f.write(f'# Session ID: {self.session_id}\n\n')
155
+
156
+ # Write timestamped step header
157
+ f.write(f'\n## Step {self.state.n_steps} ({datetime.now().isoformat()})\n\n')
158
+
159
+ # Write messages
160
+ f.write('### Input Messages\n')
161
+ self._write_messages_to_file(f, input_messages)
162
+
163
+ # Write response
164
+ f.write('\n### Response\n')
165
+ self._write_response_to_file(f, response)
166
+
167
+ # Write horizontal rule
168
+ f.write('\n---\n')
169
+
170
+ safe_operation(
171
+ save_messages,
172
+ self.logger,
173
+ f"Failed to save conversation to {self.save_conversation_path}",
174
+ default_value=None
175
+ )
176
+
177
+ def _write_messages_to_file(self, f: Any, messages: list[BaseMessage]) -> None:
178
+ """Write messages to conversation file (sanitised)."""
179
+ import json
180
+ for message in messages:
181
+ f.write(f' {message.__class__.__name__} \n')
182
+
183
+ if isinstance(message.content, list):
184
+ for item in message.content:
185
+ if isinstance(item, dict) and item.get('type') == 'text':
186
+ clean_txt = self._sanitize_text_for_log(item.get('text', ''))
187
+ f.write(clean_txt.strip() + '\n')
188
+ elif isinstance(message.content, str):
189
+ try:
190
+ content = json.loads(message.content)
191
+ content = self._sanitize_structure_for_log(content)
192
+ f.write(json.dumps(content, indent=2) + '\n')
193
+ except json.JSONDecodeError:
194
+ f.write(self._sanitize_text_for_log(message.content.strip()) + '\n')
195
+
196
+ f.write('\n')
197
+
198
+ def _write_response_to_file(self, f: Any, response: Any) -> None:
199
+ """Write model response to conversation file (sanitised)."""
200
+ import json
201
+ f.write(' RESPONSE\n')
202
+ try:
203
+ resp_dict = json.loads(response.model_dump_json(exclude_unset=True))
204
+ resp_dict = self._sanitize_structure_for_log(resp_dict)
205
+ f.write(json.dumps(resp_dict, indent=2))
206
+ except Exception:
207
+ # Fallback – write raw if sanitisation fails
208
+ f.write(json.dumps(json.loads(response.model_dump_json(exclude_unset=True)), indent=2))
209
+
210
+ def _log_agent_run(self) -> None:
211
+ """Log the agent run"""
212
+
213
+ self.logger.debug(f'Version: {self.version}, Source: {self.source}')
214
+ self.telemetry_manager.capture_event(
215
+ AgentRunTelemetryEvent(
216
+ agent_id=self.agent_id,
217
+ use_vision=self.use_vision,
218
+ task=self.task,
219
+ model_name=self.model_name,
220
+ chat_model_library=self.chat_model_library,
221
+ version=self.version,
222
+ source=self.source,
223
+ )
224
+ )
225
+
226
+ def _log_tool_outputs(self, actions: List[Any], results: List[ActionResult], step_number: int):
227
+ """Log exact tool outputs with item counts (OPTIMIZATION: Task 1 - Nov 14, 2025)"""
228
+ if not self.tool_output_log_path:
229
+ return
230
+
231
+ try:
232
+ import json
233
+ from datetime import datetime as _dt
234
+
235
+ for idx, (action, result) in enumerate(zip(actions, results)):
236
+ tool_name = action.get('name') if isinstance(action, dict) else getattr(action, 'name', 'unknown')
237
+ arguments = action.get('arguments', {}) if isinstance(action, dict) else {}
238
+
239
+ log_entry = {
240
+ "timestamp": _dt.now().isoformat(),
241
+ "step": step_number,
242
+ "tool": tool_name,
243
+ "arguments": arguments,
244
+ "result": {
245
+ "content": result.extracted_content,
246
+ "error": result.error,
247
+ "bytes": len(result.extracted_content or "")
248
+ }
249
+ }
250
+
251
+ # Add item count for lists
252
+ if result.extracted_content:
253
+ try:
254
+ parsed = json.loads(result.extracted_content)
255
+ if isinstance(parsed, list):
256
+ log_entry["result"]["item_count"] = len(parsed)
257
+ log_entry["result"]["first_item"] = parsed[0] if parsed else None
258
+ except (json.JSONDecodeError, TypeError, AttributeError):
259
+ pass # Content may not be JSON - skip enhanced logging
260
+
261
+ # Write to JSONL
262
+ with open(self.tool_output_log_path, 'a') as f:
263
+ f.write(json.dumps(log_entry) + '\n')
264
+
265
+ # Console log with count
266
+ if "item_count" in log_entry["result"]:
267
+ count = log_entry["result"]["item_count"]
268
+ requested = arguments.get('count') or arguments.get('limit')
269
+
270
+ if requested:
271
+ self.logger.info(f"📊 {tool_name}: {count} items (requested: {requested})")
272
+ else:
273
+ self.logger.info(f"📊 {tool_name}: {count} items")
274
+
275
+ except Exception as e:
276
+ self.logger.error(f"Failed to log tool outputs: {e}")
@@ -0,0 +1,262 @@
1
+ """Loop-detection + intervention mixin (roadmap P9 decomposition; code-motion from service.py).
2
+
3
+ Stop-flag checks, multi-signal action-loop detection (action repetition + memory
4
+ similarity + goal stagnation), and the guidance-injection intervention. Moved
5
+ verbatim off the ``Agent`` god-file; ``Agent`` composes ``LoopDetectionMixin``
6
+ (call sites in the step loop and flow-efficiency tests unchanged via MRO).
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ from modules.llm.messages import make_control_message, MessageOrigin
13
+ from agents.task.agent.views import ActionResult
14
+
15
+
16
+ class LoopDetectionMixin:
17
+ """Stop-flag handling + action-loop detection/intervention for Agent."""
18
+
19
+ def _check_if_stopped(self) -> bool:
20
+ """Handle stop flag. Returns True if execution should stop.
21
+
22
+ NOTE: Pause functionality removed. This method now only checks for stop.
23
+ When stopped, it raises InterruptedError to break out of the current step.
24
+ Users can send a new message to continue with a different task.
25
+ """
26
+ if self.state.stopped:
27
+ self.logger.info('⏹️ Agent execution stopped by control flag')
28
+ raise InterruptedError("Agent stopped")
29
+ # Legacy: treat paused as stopped for backwards compatibility
30
+ if self.state.paused:
31
+ self.logger.info('⏹️ Agent paused flag set - treating as stop (pause deprecated)')
32
+ self.state.stopped = True # Normalize to stopped
33
+ raise InterruptedError("Agent stopped (paused flag deprecated)")
34
+ return False
35
+
36
+ def _trigger_loop_intervention(self, reason: str, clear_history: bool = False) -> None:
37
+ """Trigger loop intervention guidance without clearing history.
38
+
39
+ Args:
40
+ reason: Human-readable reason for intervention
41
+ clear_history: Deprecated, always False. History is preserved.
42
+ """
43
+ clear_history = False # History clearing disabled - causes amnesia loops
44
+ self.logger.warning(f"🔄 LOOP DETECTED: {reason}")
45
+
46
+ # Check if this looks like a validation/parameter error loop
47
+ is_validation_error = any(x in reason.lower() for x in [
48
+ 'validation', 'parameter', 'missing required', 'invalid argument',
49
+ 'schema', 'type error', 'arguments'
50
+ ])
51
+
52
+ # Also check if recent results have validation errors
53
+ if hasattr(self, '_last_result') and self._last_result:
54
+ for result in self._last_result[-3:]:
55
+ if hasattr(result, 'error') and result.error:
56
+ error_lower = str(result.error).lower()
57
+ if any(x in error_lower for x in ['missing required parameter', 'validation failed', 'invalid']):
58
+ is_validation_error = True
59
+ break
60
+
61
+ # History clearing disabled - preserves context
62
+ # if clear_history and hasattr(self, 'message_manager'):
63
+ # self.message_manager.clear_history_keep_system(keep_last_n=2)
64
+
65
+ # Build intervention message based on error type.
66
+ # Chat-schema: tag as INTERVENTION control content (origin + <system-directive>
67
+ # envelope) so it is not indistinguishable from a genuine user turn.
68
+ if is_validation_error:
69
+ intervention_msg = make_control_message(f"""🚨 TOOL CALL ERROR LOOP DETECTED: {reason}
70
+
71
+ ⚠️ Your tool calls are failing repeatedly with the SAME validation error.
72
+ The error message above tells you exactly what's wrong - READ IT CAREFULLY.
73
+
74
+ 🔍 DIAGNOSIS FOR MCP TOOLS:
75
+ If calling mcp_execute_tool, the 'arguments' field must be a proper JSON object with the required parameters.
76
+ For example, a search tool requires: {{"count": 100, "query": "your search query"}}
77
+
78
+ 🛑 TO FIX THIS:
79
+ 1. READ the error message - it tells you which parameter is missing
80
+ 2. INCLUDE all required parameters in your tool call
81
+ 3. For mcp_execute_tool, ensure 'arguments' contains the nested tool's parameters as a JSON object
82
+
83
+ Example correct mcp_execute_tool call:
84
+ {{
85
+ "server_name": "example_server",
86
+ "tool_name": "search",
87
+ "arguments": {{"count": 100, "query": "crypto tweets"}}
88
+ }}
89
+
90
+ DO NOT just repeat the same broken call. FIX the parameters first.""", MessageOrigin.INTERVENTION)
91
+ else:
92
+ intervention_msg = make_control_message(f"""🚨 CRITICAL LOOP DETECTED: {reason}
93
+
94
+ ⚠️ You have been repeating the same actions without making progress. This is wasting resources.
95
+
96
+ 🛑 YOU MUST DO SOMETHING DIFFERENT NOW:
97
+ 1. DO NOT read the same files again - you already have their content
98
+ 2. DO NOT repeat the same action sequence
99
+ 3. If you need to CREATE something, use filesystem_write_file NOW
100
+ 4. If you're truly stuck, call the 'done' action with your current progress
101
+
102
+ 📋 Your available actions include:
103
+ - filesystem_write_file: CREATE new files (HTML, text, etc.)
104
+ - done: Mark task complete with summary
105
+
106
+ TAKE A DIFFERENT ACTION IMMEDIATELY. Reading the same files again will result in task termination.""", MessageOrigin.INTERVENTION)
107
+
108
+ self.message_manager.add_message(intervention_msg)
109
+
110
+ # Add error to force reconsideration
111
+ if not hasattr(self, '_last_result') or not self._last_result:
112
+ self._last_result = []
113
+
114
+ error_msg = f"LOOP DETECTED: {reason}."
115
+ if clear_history:
116
+ error_msg += " Context cleared."
117
+ error_msg += " You MUST fix the issue - DO NOT repeat the same broken call."
118
+
119
+ self._last_result.append(ActionResult(
120
+ error=error_msg,
121
+ include_in_memory=True
122
+ ))
123
+
124
+ # Reset counter and clear action history after intervention
125
+ self._action_repetition_counter = 0
126
+ self._previous_actions.clear()
127
+
128
+ # FIX #4: Signal to AgentState for tracking
129
+ if hasattr(self, 'state'):
130
+ self.state.loop_warning_count += 1
131
+ self.state.reset_loop_detection()
132
+
133
+ # CRITICAL: History is NEVER cleared - agent retains full context
134
+ self.logger.info(f"✅ Loop intervention complete. Full context preserved, guidance injected.")
135
+
136
+ def detect_action_loop(
137
+ self,
138
+ current_brain: Any,
139
+ current_actions: List[Dict],
140
+ lookback_steps: int = 5
141
+ ) -> tuple[bool, Optional[str]]:
142
+ """Detect if agent is repeating the same action pattern.
143
+
144
+ PHASE 2 FIX (Nov 4, 2025): Multi-signal loop detection prevents infinite loops
145
+ where agent repeats same actions without progress.
146
+
147
+ Detection Signals:
148
+ - Action repetition (same tool called N+ times in recent steps)
149
+ - Memory similarity (semantic overlap >70% in brain state)
150
+ - Goal stagnation (same next_goal for N+ consecutive steps)
151
+
152
+ Args:
153
+ current_brain: Current step's brain state
154
+ current_actions: Actions agent wants to take
155
+ lookback_steps: How many recent steps to check (default: 5)
156
+
157
+ Returns:
158
+ (is_loop, warning_message) tuple
159
+ """
160
+ if not self.task_context_manager or not self.session_id:
161
+ return False, None
162
+
163
+ # Get session data using correct API
164
+ session_data = self.task_context_manager.get_session(self.session_id)
165
+ if not session_data or not session_data.memory:
166
+ return False, None
167
+
168
+ memory = session_data.memory
169
+ recent_steps = memory.recent_steps[-lookback_steps:] if hasattr(memory, 'recent_steps') else []
170
+
171
+ if len(recent_steps) < 3:
172
+ return False, None # Need at least 3 steps to detect pattern
173
+
174
+ # SIGNAL 1: Action Repetition
175
+ current_action_names = set()
176
+ for action_dict in current_actions:
177
+ if isinstance(action_dict, dict):
178
+ current_action_names.update(action_dict.keys())
179
+
180
+ if not current_action_names:
181
+ return False, None
182
+
183
+ repeated_action_count = 0
184
+ repeated_step_nums = []
185
+ for step in recent_steps:
186
+ step_summary_lower = step.action_summary.lower()
187
+ if any(action.lower() in step_summary_lower for action in current_action_names):
188
+ repeated_action_count += 1
189
+ repeated_step_nums.append(step.step_number)
190
+
191
+ action_repeat_ratio = repeated_action_count / len(recent_steps)
192
+
193
+ # SIGNAL 2: Memory Similarity
194
+ similar_memory_count = 0
195
+ similar_memories = []
196
+
197
+ if hasattr(current_brain, 'memory') and current_brain.memory:
198
+ import re
199
+ current_keywords = set(re.findall(r'\b\w{4,}\b', current_brain.memory.lower()))
200
+
201
+ for step in recent_steps:
202
+ if step.finding:
203
+ step_keywords = set(re.findall(r'\b\w{4,}\b', step.finding.lower()))
204
+ if current_keywords and step_keywords:
205
+ overlap = len(current_keywords & step_keywords)
206
+ total = len(current_keywords | step_keywords)
207
+ if total > 0:
208
+ similarity = overlap / total
209
+ if similarity > 0.70: # 70% keyword overlap
210
+ similar_memory_count += 1
211
+ similar_memories.append((step.step_number, similarity, step.finding[:60]))
212
+
213
+ memory_similarity_ratio = similar_memory_count / len(recent_steps) if recent_steps else 0
214
+
215
+ # SIGNAL 3: Goal Stagnation (if available)
216
+ goal_stagnation = False
217
+ if hasattr(current_brain, 'next_goal') and current_brain.next_goal:
218
+ stagnant_goals = 0
219
+ for step in recent_steps:
220
+ if hasattr(step, 'brain_state') and step.brain_state:
221
+ if hasattr(step.brain_state, 'next_goal'):
222
+ if step.brain_state.next_goal == current_brain.next_goal:
223
+ stagnant_goals += 1
224
+ if stagnant_goals >= len(recent_steps) * 0.6:
225
+ goal_stagnation = True
226
+
227
+ # DETECTION LOGIC: Multiple signals = higher confidence
228
+ is_loop = False
229
+ warning_message = None
230
+
231
+ if action_repeat_ratio >= 0.60: # Action repeated in 60%+ of steps
232
+ severity = "🔄 LOOP DETECTED"
233
+ if memory_similarity_ratio >= 0.60: # AND memory is similar
234
+ severity = "🚨 SEVERE LOOP"
235
+ is_loop = True
236
+ elif goal_stagnation: # AND goal hasn't changed
237
+ severity = "⚠️ POSSIBLE LOOP"
238
+ is_loop = True
239
+ else:
240
+ severity = "⚠️ ACTION REPETITION"
241
+ is_loop = True
242
+
243
+ warning_message = (
244
+ f"{severity} at step {self.state.n_steps}:\n\n"
245
+ f"📊 Detection Signals:\n"
246
+ f" • Action repetition: {action_repeat_ratio:.0%} ({repeated_action_count}/{len(recent_steps)} steps)\n"
247
+ f" • Memory similarity: {memory_similarity_ratio:.0%} ({similar_memory_count}/{len(recent_steps)} steps)\n"
248
+ f" • Goal stagnation: {'YES' if goal_stagnation else 'NO'}\n\n"
249
+ f"🔍 Pattern Analysis:\n"
250
+ f" • Repeated action: {', '.join(current_action_names)}\n"
251
+ f" • Steps with this action: {repeated_step_nums}\n"
252
+ f" • Current memory: {current_brain.memory[:100] if hasattr(current_brain, 'memory') else 'N/A'}...\n\n"
253
+ f"💡 Recommended Actions:\n"
254
+ f" 1. Review recent steps to avoid repeating work\n"
255
+ f" 2. Check if data/result already available from previous steps\n"
256
+ f" 3. Try a DIFFERENT approach or tool\n"
257
+ f" 4. Verify your goal hasn't already been achieved\n"
258
+ )
259
+
260
+ self.logger.warning(warning_message)
261
+
262
+ return is_loop, warning_message