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,389 @@
1
+ """Action result-processing mixin (roadmap P9; code-motion from step.py).
2
+
3
+ Post-execution handling: turning action results into tool messages and processing
4
+ them into the next-step brain/memory. Split out of StepMixin so step.py trends
5
+ toward the step-phase skeleton. Agent composes ResultProcessingMixin; the step
6
+ loop calls these via MRO.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import logging
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ from agents.task.agent.views import ActionResult, AgentBrain
15
+ from agents.task.agent.core.untrusted_wrap import maybe_wrap
16
+ from modules.llm.messages import AIMessage, ToolMessage
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ def _pair_results_to_calls(result, tool_calls_to_pass, source_for=None):
22
+ """Map each tool_call id -> (content, had_error) by identity.
23
+
24
+ Prefers ActionResult.tool_call_id (set by multi_act). Falls back to
25
+ positional pairing only when ids are absent (legacy / non-native path).
26
+
27
+ UP-06: when ``source_for`` is provided (a callable ``tool_call_id ->
28
+ (action_name, tool)``), the string ``extracted_content`` of an untrusted
29
+ tool result (mcp/browser/web/perplexity) is framed in
30
+ ``<untrusted_tool_result>`` delimiters. ``source_for=None`` (default) skips
31
+ wrapping entirely — preserving the legacy 2-arg call sites/tests byte-for-byte.
32
+ Only the returned string is wrapped; the ``ActionResult`` object is never
33
+ mutated, so memory previews / telemetry stay clean.
34
+ """
35
+ def _entry(ar, tc_id=None):
36
+ if ar.error:
37
+ # UP-06: an untrusted tool can control its own error string, so the error
38
+ # branch must go through the same wrap as extracted_content — otherwise
39
+ # injected instructions in a tool's error reach the model unframed.
40
+ content = f"Error: {ar.error}"
41
+ if source_for is not None and tc_id is not None:
42
+ action_name, tool = source_for(tc_id)
43
+ content = maybe_wrap(action_name, tool, content)
44
+ return (content, True)
45
+ if ar.extracted_content:
46
+ content = str(ar.extracted_content)
47
+ if source_for is not None and tc_id is not None:
48
+ action_name, tool = source_for(tc_id)
49
+ content = maybe_wrap(action_name, tool, content)
50
+ return (content, False)
51
+ if ar.is_done:
52
+ return ("Task marked as complete", False)
53
+ return ("Action completed successfully", False)
54
+
55
+ results = result or []
56
+ have_ids = len(results) > 0 and all(getattr(r, "tool_call_id", None) for r in results)
57
+ # P0-2 tripwire: identity pairing is the correct path. Positional pairing with
58
+ # >1 result misattributes outputs whenever the executor skips/reorders/truncates
59
+ # actions. If ids are missing on a multi-result step, the _tool_call_id re-stamp
60
+ # in next_action_internal regressed — surface it loudly instead of dying silent.
61
+ if not have_ids and len(results) > 1:
62
+ logger.warning(
63
+ "[pair] no tool_call_id on a %d-result step; falling back to POSITIONAL "
64
+ "pairing (results may be misattributed if the executor skipped/reordered "
65
+ "actions). Check the P0-2 tool_call_id re-stamp.",
66
+ len(results),
67
+ )
68
+ paired = {}
69
+ if have_ids:
70
+ ids = [ar.tool_call_id for ar in results]
71
+ if len(set(ids)) == len(ids):
72
+ return {ar.tool_call_id: _entry(ar, ar.tool_call_id) for ar in results}
73
+ # Duplicate tool_call_id in one step: identity pairing would silently drop a
74
+ # result (both dups collapse to one dict key). Fall back to POSITION-keyed
75
+ # pairing — (tool_call_id, index) — so every result surfaces; the consumer
76
+ # looks up by (id, i) first, then by id. Keeps the unique early-return above
77
+ # (keyed by id) unchanged.
78
+ logger.warning(
79
+ "[pair] duplicate tool_call_id detected (%s); falling back to positional pairing",
80
+ ids,
81
+ )
82
+ for i, tc in enumerate(tool_calls_to_pass):
83
+ if i < len(results):
84
+ tc_id = tc.get("id")
85
+ paired[(tc_id, i)] = _entry(results[i], tc_id)
86
+ return paired
87
+
88
+
89
+ class ResultProcessingMixin:
90
+ """Tool-message construction + action-result processing for Agent."""
91
+
92
+ async def _add_tool_messages(self, result, model_output, tool_calls_to_pass) -> bool:
93
+ """Add AIMessage + ToolMessages for the step's tool calls.
94
+
95
+ Returns True on success (continue the step) and False on the corrupted-state
96
+ path (atomic mismatch), which signals the caller to abort the step.
97
+ """
98
+ # Atomic message addition: AIMessage + all ToolMessages added together
99
+ if self.use_native_tools and tool_calls_to_pass:
100
+ self.logger.info(f"[ATOMIC] Preparing atomic addition for {len(tool_calls_to_pass)} tool calls")
101
+
102
+ # FIXED: Get validation errors from controller for tool calls that failed validation
103
+ validation_errors = {}
104
+ if self.controller:
105
+ validation_errors = self.controller.get_last_validation_errors()
106
+ if validation_errors:
107
+ self.logger.warning(f"[ATOMIC] {len(validation_errors)} tool calls had validation errors")
108
+
109
+ # Step 1: Build all tool responses from action results
110
+ # Pair results to calls by IDENTITY (tool_call_id), not list position —
111
+ # the executor reorders/truncates actions, so positional pairing misaligns.
112
+ tool_responses = []
113
+ # UP-06: resolve each tool call's (action_name, tool namespace) so untrusted
114
+ # results (mcp/browser/web/perplexity) get framed as DATA before entering
115
+ # history. Gated on the flag; source_for=None => no wrapping (legacy).
116
+ source_for = None
117
+ from agents.task.constants import UNTRUSTED_TOOL_RESULT_WRAP
118
+ if UNTRUSTED_TOOL_RESULT_WRAP:
119
+ name_by_id = {tc.get('id'): tc.get('name') for tc in tool_calls_to_pass if tc.get('id')}
120
+
121
+ def _source_for(tc_id, _name_by_id=name_by_id):
122
+ name = _name_by_id.get(tc_id)
123
+ tool = None
124
+ if name and self.controller:
125
+ # NOTE: get_action() returns the callable; get_action_details()
126
+ # returns the RegisteredAction carrying the .tool namespace.
127
+ ra = self.controller.get_action_details(name)
128
+ tool = getattr(ra, 'tool', None) if ra else None
129
+ return (name, tool)
130
+
131
+ source_for = _source_for
132
+ paired = _pair_results_to_calls(result, tool_calls_to_pass, source_for=source_for)
133
+
134
+ for i, tc in enumerate(tool_calls_to_pass):
135
+ tc_id = tc.get('id')
136
+ tc_name = tc.get('name', 'unknown')
137
+
138
+ if not tc_id:
139
+ self.logger.error(f"Tool call at index {i} missing ID, skipping")
140
+ continue
141
+
142
+ # FIXED: Check validation errors FIRST - these tool calls were never executed
143
+ if tc_id in validation_errors:
144
+ error_msg = validation_errors[tc_id]
145
+ self.logger.warning(f"Tool call '{tc_name}' (id={tc_id}) failed validation: {error_msg[:100]}")
146
+ tool_responses.append((tc_id, f"Error: {error_msg}"))
147
+
148
+ if self.tool_call_tracker:
149
+ self.tool_call_tracker.mark_failed(tc_id, f"Validation failed: {error_msg[:100]}")
150
+ # This call was never executed (no result to pair)
151
+ continue
152
+
153
+ # This tool call passed validation - pair its result. Try the
154
+ # position-keyed entry first (duplicate-id fallback), then the
155
+ # identity entry (the normal unique-id path).
156
+ entry = paired.get((tc_id, i))
157
+ if entry is None:
158
+ entry = paired.get(tc_id)
159
+ if entry is not None:
160
+ content, had_error = entry
161
+ tool_responses.append((tc_id, content))
162
+ if self.tool_call_tracker:
163
+ if had_error:
164
+ self.tool_call_tracker.mark_failed(tc_id, content)
165
+ else:
166
+ self.tool_call_tracker.mark_completed(tc_id, content[:200])
167
+ else:
168
+ error_msg = (
169
+ f"Action '{tc_name}' was NOT executed this step "
170
+ f"(deferred, truncated, or a prior action stopped execution). "
171
+ f"Retry it next step if still needed."
172
+ )
173
+ tool_responses.append((tc_id, f"Error: {error_msg}"))
174
+ if self.tool_call_tracker:
175
+ self.tool_call_tracker.mark_failed(tc_id, "Not executed this step")
176
+
177
+ # Step 2: Extract AI content from model_output (reuse existing pattern from line 2057)
178
+ try:
179
+ if model_output and hasattr(model_output, 'current_state'):
180
+ _mem = getattr(model_output.current_state, 'memory', '') or ''
181
+ if _mem.startswith("Synthesis pending"):
182
+ # P2-16: this is the pre-synthesis PLACEHOLDER brain (a native step
183
+ # with tool calls but no parseable brain JSON). The synthesis that
184
+ # replaces it runs AFTER this atomic add, so serializing it here
185
+ # persisted "Synthesis pending - will be generated..." verbatim into
186
+ # provider-visible history. Store neutral content instead — the
187
+ # tool_calls carry the turn, and the real (synthesized) brain is
188
+ # recorded to memory downstream by _process_action_results.
189
+ ai_content = "Processing actions"
190
+ else:
191
+ brain_dict = model_output.current_state.model_dump(exclude_unset=True)
192
+ ai_content = json.dumps(brain_dict, separators=(',', ':')) # Compact JSON
193
+ else:
194
+ ai_content = "Processing actions"
195
+ except Exception as e:
196
+ self.logger.warning(f"Failed to serialize brain state: {e}")
197
+ ai_content = "Brain state extraction failed"
198
+
199
+ # Step 3: Atomic addition - all or nothing
200
+ try:
201
+ self.message_manager.add_tool_call_pair_atomic(
202
+ ai_content=ai_content,
203
+ tool_calls=tool_calls_to_pass,
204
+ tool_responses=tool_responses
205
+ )
206
+ self.logger.info(f"[ATOMIC] ✅ Added AIMessage + {len(tool_responses)} ToolMessages atomically")
207
+
208
+ # Complete the step in tracker
209
+ if self.tool_call_tracker:
210
+ self.tool_call_tracker.complete_step()
211
+
212
+ except ValueError as e:
213
+ # FIX: Fail fast on mismatch - don't use degraded fallback
214
+ # The atomic method already rolled back, so we're in a clean state
215
+ # Adding separately would create inconsistent message state
216
+ self.logger.error(
217
+ f"[ATOMIC] Failed atomic addition (mismatch): {e}. "
218
+ f"Expected {len(tool_calls_to_pass)} tool_calls, got {len(tool_responses)} responses. "
219
+ f"Tool call IDs: {[tc.get('id') for tc in tool_calls_to_pass]}"
220
+ )
221
+ # Mark step as failed in tracker
222
+ if self.tool_call_tracker:
223
+ self.tool_call_tracker.complete_step()
224
+ # Set error result so next step can retry
225
+ self._last_result = [ActionResult(
226
+ error=f"Tool response mismatch: {e}. Actions executed but responses couldn't be recorded atomically.",
227
+ include_in_memory=True
228
+ )]
229
+ return False # Don't continue with corrupted state
230
+ except Exception as e:
231
+ # Other error during atomic addition
232
+ self.logger.error(f"[ATOMIC] Failed atomic addition: {e}")
233
+ # History already rolled back by atomic method
234
+ raise
235
+
236
+ elif tool_calls_to_pass:
237
+ self.logger.debug("Using non-atomic pattern (non-native tools)")
238
+ tool_call_id = self.message_manager.add_model_output(model_output, tool_calls=tool_calls_to_pass)
239
+
240
+ return True
241
+
242
+ async def _process_action_results(self, result, model_output, tool_calls_to_pass):
243
+ """Phase 3b: record action results into context (large-result handling + atomic tool-message pairing). Returns the result list, or None to abort on corrupted state."""
244
+ if model_output and hasattr(model_output, 'action') and model_output.action:
245
+ # OPTIMIZATION: Log tool outputs (Task 1 - Nov 14, 2025)
246
+ self._log_tool_outputs(model_output.action, result, self.state.n_steps)
247
+
248
+ # CRITICAL: Handle large action results BEFORE adding to context
249
+ # This offloads content >10K to files, preventing context overflow
250
+ # Must happen BEFORE tool responses are added to message history
251
+ self._handle_large_action_results(result)
252
+
253
+ # Atomic message addition: AIMessage + all ToolMessages added together
254
+ if not await self._add_tool_messages(result, model_output, tool_calls_to_pass):
255
+ return None # Don't continue with corrupted state
256
+
257
+ # Build intelligent brain state from previous state + action results
258
+ if model_output and hasattr(model_output, 'action') and model_output.action and result:
259
+ # Start with previous brain state for continuity
260
+ previous_brain = None
261
+ if hasattr(self, '_last_model_output') and self._last_model_output and hasattr(self._last_model_output, 'current_state'):
262
+ previous_brain = self._last_model_output.current_state
263
+
264
+ # Build specific action summaries with actual results
265
+ action_summaries = []
266
+ has_errors = False
267
+ has_completion = False
268
+
269
+ for i, res in enumerate(result):
270
+ if i >= len(model_output.action):
271
+ break
272
+
273
+ # Safe extraction with validation (matching pattern from _log_response)
274
+ action_dump = model_output.action[i].model_dump(exclude_unset=True)
275
+ if not action_dump:
276
+ self.logger.warning(f"Action {i} has empty model_dump, skipping")
277
+ continue
278
+
279
+ action_keys = list(action_dump.keys())
280
+ if not action_keys:
281
+ self.logger.warning(f"Action {i} has no keys in model_dump, skipping")
282
+ continue
283
+
284
+ action_name = action_keys[0]
285
+ action_params = action_dump[action_name]
286
+
287
+ # Build specific description with params and results
288
+ param_str = ""
289
+ if isinstance(action_params, dict) and action_params:
290
+ # Show key params (not all)
291
+ key_params = []
292
+ for k, v in list(action_params.items())[:2]: # First 2 params only
293
+ if isinstance(v, str):
294
+ key_params.append(f"{k}={v[:30]}")
295
+ else:
296
+ key_params.append(f"{k}={v}")
297
+ param_str = f"({', '.join(key_params)})" if key_params else ""
298
+
299
+ if res.error:
300
+ action_summaries.append(f"{action_name}{param_str}→ERROR:{res.error[:50]}")
301
+ has_errors = True
302
+ elif res.is_done:
303
+ action_summaries.append(f"{action_name}{param_str}→DONE")
304
+ has_completion = True
305
+ elif res.extracted_content:
306
+ # Show actual extracted content
307
+ content_preview = res.extracted_content[:80].replace('\n', ' ')
308
+ action_summaries.append(f"{action_name}{param_str}→{content_preview}")
309
+ else:
310
+ action_summaries.append(f"{action_name}{param_str}→OK")
311
+
312
+ # Extract previous memory and progress for continuity
313
+ previous_memory = None
314
+ previous_progress = None
315
+ if previous_brain and hasattr(previous_brain, 'memory'):
316
+ previous_memory = previous_brain.memory
317
+ previous_progress = self._extract_progress_from_memory(previous_memory)
318
+
319
+ # Build memory that preserves previous context
320
+ new_memory = self._build_memory_from_actions(
321
+ step_number=self.state.n_steps,
322
+ action_summaries=action_summaries,
323
+ previous_memory=previous_memory,
324
+ previous_progress=previous_progress
325
+ )
326
+
327
+ # Build evaluation
328
+ if has_completion:
329
+ evaluation = f"Success - Task marked complete at step {self.state.n_steps}"
330
+ elif has_errors:
331
+ evaluation = f"Partial - {sum(1 for r in result if not r.error)}/{len(result)} actions succeeded"
332
+ else:
333
+ evaluation = f"Success - All {len(result)} actions completed"
334
+
335
+ # Keep previous goal if it existed, otherwise create continuation goal
336
+ if previous_brain and hasattr(previous_brain, 'next_goal') and previous_brain.next_goal and 'Continue' not in previous_brain.next_goal:
337
+ next_goal = previous_brain.next_goal # Preserve specific goal
338
+ else:
339
+ # Create new goal based on what we just did
340
+ next_goal = f"Continue based on results from step {self.state.n_steps}"
341
+
342
+ # Build reasoning from action sequence
343
+ reasoning = f"Executed: {' → '.join([a.split('→')[0] for a in action_summaries[:3]])}"
344
+
345
+ # ✅ FIX #3: Enhanced brain state synthesis
346
+ # Synthesis happens AFTER actions execute, so it can use actual results
347
+ # Also detect placeholder brain state and replace it
348
+ needs_synthesis = (
349
+ not model_output.current_state
350
+ or not model_output.current_state.memory
351
+ or "Synthesis pending" in model_output.current_state.memory
352
+ or model_output.current_state.evaluation_previous_goal == "Pending"
353
+ )
354
+
355
+ # FIX #3: Detect hallucinated brain state
356
+ # If LLM says it "created/wrote/generated" but actual actions were only "read", force synthesis
357
+ if not needs_synthesis and model_output.current_state and model_output.current_state.memory:
358
+ llm_memory = model_output.current_state.memory.lower()
359
+ actual_action_names = [a.split('→')[0].split('(')[0].lower() for a in action_summaries]
360
+
361
+ # Check for hallucination: LLM claims write actions but only read actions occurred
362
+ write_claims = any(word in llm_memory for word in ['created', 'wrote', 'generated', 'saved', 'writing'])
363
+ actual_reads_only = all('read' in a for a in actual_action_names)
364
+
365
+ if write_claims and actual_reads_only and len(actual_action_names) > 0:
366
+ self.logger.warning(
367
+ f"🚨 Detected hallucinated brain state: LLM claims write actions "
368
+ f"but actual actions were [{', '.join(actual_action_names)}]. Forcing synthesis."
369
+ )
370
+ needs_synthesis = True
371
+
372
+ if needs_synthesis:
373
+ # LLM failed to provide brain state OR used placeholder OR hallucinated
374
+ model_output.current_state = AgentBrain(
375
+ page_summary="",
376
+ memory=new_memory[:500],
377
+ evaluation_previous_goal=evaluation,
378
+ next_goal=next_goal,
379
+ reasoning=reasoning[:200]
380
+ )
381
+ self.logger.info(f"✅ Synthesized brain state from action results: {new_memory[:120]}")
382
+ # CO-F8: write the synthesized brain back to _last_brain_state so the
383
+ # next memory-prefetch recall query is enriched instead of degrading
384
+ # to the static task string (no-text-content / tool-calls-only steps).
385
+ self._last_brain_state = model_output.current_state
386
+ # ✅ FIX: Removed enhancement logic - don't corrupt LLM-provided brain state
387
+
388
+ return result
389
+