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,539 @@
1
+ """
2
+ Tool call builder for standardizing tool call structure across AutoV2.
3
+
4
+ This module provides a consistent interface for creating tool calls that align
5
+ with OpenAI tool call schemas. It also centralizes validation,
6
+ repair, and placeholder creation for tool message sequences.
7
+
8
+ GROK 4.1 / MCP NESTED ARGUMENTS FIX (Dec 2025):
9
+ Some LLMs (notably Grok 4.1 via OpenRouter) return nested object/array parameters
10
+ as JSON strings instead of parsed objects. For example:
11
+ {"authors": "[\"a\", \"b\"]", "filters": "{\"key\": \"val\"}"}
12
+ instead of:
13
+ {"authors": ["a", "b"], "filters": {"key": "val"}}
14
+
15
+ This module includes `_deep_parse_json_strings()` to recursively parse these
16
+ stringified nested structures, fixing MCP tool calling issues.
17
+ """
18
+
19
+ from typing import Dict, List, Optional, Any, Union, Tuple, Set
20
+ from dataclasses import dataclass, field, asdict
21
+ from uuid import uuid4
22
+ import json
23
+ import logging
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ def _deep_parse_json_strings(obj: Any, depth: int = 0, max_depth: int = 10) -> Any:
29
+ """Recursively parse JSON strings within nested structures.
30
+
31
+ GROK 4.1 / MCP FIX (Dec 2025):
32
+ Some LLMs return nested objects/arrays as JSON strings instead of parsed objects.
33
+ This function recursively finds and parses those strings.
34
+
35
+ Examples:
36
+ '["a", "b"]' -> ["a", "b"]
37
+ '{"key": "val"}' -> {"key": "val"}
38
+ {"items": '["x"]'} -> {"items": ["x"]}
39
+
40
+ Args:
41
+ obj: The object to process (any type)
42
+ depth: Current recursion depth (for safety)
43
+ max_depth: Maximum recursion depth to prevent infinite loops
44
+
45
+ Returns:
46
+ The object with all JSON strings parsed into their Python equivalents
47
+ """
48
+ # Safety: prevent infinite recursion
49
+ if depth > max_depth:
50
+ logger.warning(f"[DEEP_PARSE] Max depth {max_depth} reached, returning as-is")
51
+ return obj
52
+
53
+ if isinstance(obj, str):
54
+ # Check if this string looks like JSON (starts with { or [)
55
+ stripped = obj.strip()
56
+ if stripped and (stripped.startswith('{') or stripped.startswith('[')):
57
+ try:
58
+ parsed = json.loads(stripped)
59
+ # Recursively process the parsed result (it may contain more JSON strings)
60
+ result = _deep_parse_json_strings(parsed, depth + 1, max_depth)
61
+ logger.debug(f"[DEEP_PARSE] Parsed JSON string at depth {depth}: {stripped[:50]}...")
62
+ return result
63
+ except (json.JSONDecodeError, ValueError):
64
+ # Not valid JSON, return as-is
65
+ return obj
66
+ return obj
67
+
68
+ elif isinstance(obj, dict):
69
+ # Recursively process all values in the dict
70
+ return {k: _deep_parse_json_strings(v, depth + 1, max_depth) for k, v in obj.items()}
71
+
72
+ elif isinstance(obj, list):
73
+ # Recursively process all items in the list
74
+ return [_deep_parse_json_strings(item, depth + 1, max_depth) for item in obj]
75
+
76
+ else:
77
+ # For all other types (int, float, bool, None, etc.), return as-is
78
+ return obj
79
+
80
+
81
+ def _convert_to_serializable(obj: Any, depth: int = 0, max_depth: int = 20) -> Any:
82
+ """Convert protobuf and other non-serializable types to JSON-serializable Python types.
83
+
84
+ GEMINI MAPCOMPOSITE FIX (Dec 2025):
85
+ Gemini API returns proto.marshal.collections.maps.MapComposite objects
86
+ which are not JSON serializable. This function recursively converts them
87
+ to regular Python dicts/lists.
88
+
89
+ Args:
90
+ obj: The object to convert
91
+ depth: Current recursion depth
92
+ max_depth: Maximum recursion depth to prevent infinite loops
93
+
94
+ Returns:
95
+ JSON-serializable Python object
96
+ """
97
+ if depth > max_depth:
98
+ logger.warning(f"[SERIALIZABLE] Max depth {max_depth} reached, converting to str")
99
+ return str(obj)
100
+
101
+ # Handle None
102
+ if obj is None:
103
+ return None
104
+
105
+ # Handle primitives
106
+ if isinstance(obj, (str, int, float, bool)):
107
+ return obj
108
+
109
+ # Handle regular dict
110
+ if isinstance(obj, dict):
111
+ return {str(k): _convert_to_serializable(v, depth + 1, max_depth) for k, v in obj.items()}
112
+
113
+ # Handle regular list/tuple
114
+ if isinstance(obj, (list, tuple)):
115
+ return [_convert_to_serializable(item, depth + 1, max_depth) for item in obj]
116
+
117
+ # Handle protobuf MapComposite and similar map-like objects
118
+ # Check for proto.marshal types by checking class name (avoid import dependency)
119
+ obj_class_name = obj.__class__.__name__
120
+ obj_module = obj.__class__.__module__ if hasattr(obj.__class__, '__module__') else ''
121
+
122
+ if 'MapComposite' in obj_class_name or 'proto.marshal' in obj_module:
123
+ # Convert MapComposite to dict
124
+ try:
125
+ return {str(k): _convert_to_serializable(v, depth + 1, max_depth) for k, v in obj.items()}
126
+ except Exception as e:
127
+ logger.warning(f"[SERIALIZABLE] Failed to convert {obj_class_name}: {e}")
128
+ return str(obj)
129
+
130
+ if 'RepeatedComposite' in obj_class_name or ('proto.marshal' in obj_module and hasattr(obj, '__iter__')):
131
+ # Convert RepeatedComposite to list
132
+ try:
133
+ return [_convert_to_serializable(item, depth + 1, max_depth) for item in obj]
134
+ except Exception as e:
135
+ logger.warning(f"[SERIALIZABLE] Failed to convert {obj_class_name}: {e}")
136
+ return str(obj)
137
+
138
+ # Handle objects with .items() method (dict-like)
139
+ if hasattr(obj, 'items') and callable(obj.items):
140
+ try:
141
+ return {str(k): _convert_to_serializable(v, depth + 1, max_depth) for k, v in obj.items()}
142
+ except Exception:
143
+ pass
144
+
145
+ # Handle objects with __iter__ (list-like) but not strings
146
+ if hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
147
+ try:
148
+ return [_convert_to_serializable(item, depth + 1, max_depth) for item in obj]
149
+ except Exception:
150
+ pass
151
+
152
+ # Fallback: convert to string
153
+ try:
154
+ return str(obj)
155
+ except Exception:
156
+ return f"<non-serializable: {obj_class_name}>"
157
+
158
+
159
+ # Native message types
160
+ from modules.llm.messages import (
161
+ AIMessage,
162
+ BaseMessage,
163
+ HumanMessage,
164
+ SystemMessage,
165
+ ToolMessage,
166
+ )
167
+
168
+ # Import constants from centralized location
169
+ from agents.task.constants import (
170
+ TOOL_LOOKAHEAD_WINDOW
171
+ )
172
+
173
+
174
+ @dataclass
175
+ class StandardToolCall:
176
+ """Standardized tool call structure compatible with OpenAI tool-call schemas.
177
+
178
+ This ensures consistency across all tool call creation and validation.
179
+ """
180
+ name: str # Tool/function name
181
+ args: Dict[str, Any] # Arguments as dict
182
+ id: str = field(default_factory=lambda: str(uuid4())) # Unique ID
183
+ type: str = "function" # Standard type for OpenAI tool-call compatibility
184
+
185
+ def to_dict(self) -> Dict[str, Any]:
186
+ """Convert to standard dict format for AIMessage.tool_calls"""
187
+ return {
188
+ "name": self.name,
189
+ "args": self.args,
190
+ "id": self.id,
191
+ "type": self.type
192
+ }
193
+
194
+ def to_openai_format(self) -> Dict[str, Any]:
195
+ """Convert to OpenAI tool call format for AIMessage.tool_calls"""
196
+ return {
197
+ "name": self.name,
198
+ "args": self.args,
199
+ "id": self.id,
200
+ "type": self.type
201
+ }
202
+
203
+
204
+ class ToolCallBuilder:
205
+ """Builder for creating standardized tool calls.
206
+
207
+ RESPONSIBILITY CONTRACT:
208
+ - ToolCallBuilder.normalize_tool_call: Handles provider-specific tool_call format differences ONLY
209
+ (OpenAI function nesting, IDs, arguments JSON parsing)
210
+ - utils_json.normalize_action_schema: Handles action payload field name transformations ONLY
211
+ (done.text, write_file.file_path, etc.)
212
+
213
+ These are separate concerns and should NOT have overlapping logic.
214
+ """
215
+
216
+ @staticmethod
217
+ def create_agent_output_call(agent_output: Any, tool_id: Optional[Union[str, int]] = None) -> StandardToolCall:
218
+ """Create a tool call for AgentOutput.
219
+
220
+ Args:
221
+ agent_output: The AgentOutput object to convert
222
+ tool_id: Optional specific ID to use
223
+
224
+ Returns:
225
+ StandardToolCall object
226
+ """
227
+ if tool_id is None:
228
+ tool_id = str(uuid4())
229
+ else:
230
+ tool_id = str(tool_id)
231
+
232
+ # Handle model dumping if available
233
+ if hasattr(agent_output, 'model_dump'):
234
+ args = agent_output.model_dump(mode='json', exclude_unset=True)
235
+ elif hasattr(agent_output, 'dict'):
236
+ args = agent_output.dict(exclude_unset=True)
237
+ else:
238
+ args = {"data": str(agent_output)}
239
+
240
+ return StandardToolCall(
241
+ name="AgentOutput",
242
+ args=args,
243
+ id=tool_id,
244
+ type="function"
245
+ )
246
+
247
+ @staticmethod
248
+ def create_tool_call(name: str, args: Dict[str, Any], tool_id: Optional[str] = None) -> StandardToolCall:
249
+ """Create a generic tool call.
250
+
251
+ Args:
252
+ name: Tool/function name
253
+ args: Arguments dictionary
254
+ tool_id: Optional specific ID to use
255
+
256
+ Returns:
257
+ StandardToolCall object
258
+ """
259
+ if tool_id is None:
260
+ tool_id = str(uuid4())
261
+
262
+ return StandardToolCall(
263
+ name=name,
264
+ args=args,
265
+ id=tool_id,
266
+ type="function"
267
+ )
268
+
269
+ @staticmethod
270
+ def build_tool_call_from_action(action: List[Dict[str, Any]], tool_call_id: str = None) -> 'StandardToolCall':
271
+ """Build a StandardToolCall from an action list.
272
+
273
+ Args:
274
+ action: List of action dictionaries
275
+ tool_call_id: Optional tool call ID (will generate if not provided)
276
+
277
+ Returns:
278
+ StandardToolCall object
279
+ """
280
+ if not action or not isinstance(action, list) or len(action) == 0:
281
+ # Return a default 'wait' action if no action provided
282
+ return StandardToolCall(
283
+ name="wait",
284
+ args={},
285
+ id=tool_call_id or str(uuid4()),
286
+ type="function"
287
+ )
288
+
289
+ # Take the first action (AutoV2 typically sends one action at a time)
290
+ first_action = action[0]
291
+
292
+ if not isinstance(first_action, dict) or len(first_action) == 0:
293
+ return StandardToolCall(
294
+ name="wait",
295
+ args={},
296
+ id=tool_call_id or str(uuid4()),
297
+ type="function"
298
+ )
299
+
300
+ # Extract action name and args
301
+ action_name = list(first_action.keys())[0]
302
+ action_args = first_action[action_name]
303
+
304
+ # Ensure args is a dict
305
+ if not isinstance(action_args, dict):
306
+ action_args = {"value": action_args} if action_args else {}
307
+
308
+ return StandardToolCall(
309
+ name=action_name,
310
+ args=action_args,
311
+ id=tool_call_id or str(uuid4()),
312
+ type="function"
313
+ )
314
+
315
+ @staticmethod
316
+ def validate_tool_call(tool_call: Dict[str, Any]) -> bool:
317
+ """Validate if a tool call has the required structure.
318
+
319
+ Args:
320
+ tool_call: Tool call dict to validate
321
+
322
+ Returns:
323
+ True if valid, False otherwise
324
+ """
325
+ required_fields = {"name", "id"}
326
+ optional_fields = {"args", "type"}
327
+
328
+ # Check required fields
329
+ if not all(field in tool_call for field in required_fields):
330
+ missing = required_fields - set(tool_call.keys())
331
+ logger.warning(f"Tool call missing required fields: {missing}")
332
+ return False
333
+
334
+ # Validate types
335
+ if not isinstance(tool_call.get("name"), str):
336
+ logger.warning(f"Tool call 'name' must be string, got {type(tool_call.get('name'))}")
337
+ return False
338
+
339
+ if not isinstance(tool_call.get("id"), str):
340
+ logger.warning(f"Tool call 'id' must be string, got {type(tool_call.get('id'))}")
341
+ return False
342
+
343
+ # Validate args if present
344
+ if "args" in tool_call:
345
+ if not isinstance(tool_call["args"], dict):
346
+ # Check if it's a JSON string (OpenAI format)
347
+ if isinstance(tool_call["args"], str):
348
+ try:
349
+ json.loads(tool_call["args"])
350
+ except json.JSONDecodeError:
351
+ logger.warning(f"Tool call 'args' is not valid JSON: {tool_call['args']}")
352
+ return False
353
+ else:
354
+ logger.warning(f"Tool call 'args' must be dict or JSON string, got {type(tool_call['args'])}")
355
+ return False
356
+
357
+ return True
358
+
359
+ @staticmethod
360
+ def normalize_and_correct(tool_call: Any, provider: str = None, llm_content: str = None) -> Dict[str, Any]:
361
+ """Normalize and correct tool call in a single pass.
362
+
363
+ This combines format normalization and field corrections for optimal performance.
364
+
365
+ Handles:
366
+ - OpenAI format (nested 'function' field)
367
+ - Anthropic format (direct fields)
368
+ - Flat format (direct fields with args)
369
+ - Object-based formats (using getattr)
370
+ - JSON string arguments
371
+ - Field name corrections (file_name → file_path, message → text, etc.)
372
+
373
+ Args:
374
+ tool_call: Tool call in any supported format (dict or object)
375
+ provider: Optional provider name for provider-specific handling
376
+ llm_content: Unused, kept for API compatibility
377
+
378
+ Returns:
379
+ Normalized and corrected tool call dict with fields: name, args, id, type
380
+ """
381
+ # Step 1: Normalize format (existing logic)
382
+ normalized = ToolCallBuilder.normalize_tool_call(tool_call)
383
+
384
+ # Step 2: Apply field corrections immediately
385
+ from agents.task.utils_json import apply_action_field_corrections
386
+
387
+ tool_name = normalized['name']
388
+ corrected_args = apply_action_field_corrections(tool_name, normalized['args'])
389
+ normalized['args'] = corrected_args
390
+
391
+ return normalized
392
+
393
+ @staticmethod
394
+ def normalize_tool_call(tool_call: Any) -> Dict[str, Any]:
395
+ """Normalize a tool call to standard format - FORMAT ONLY.
396
+
397
+ SINGULAR RESPONSIBILITY: Handle provider format differences ONLY.
398
+ Does NOT apply field corrections - use normalize_and_correct() for that.
399
+
400
+ Handles:
401
+ - OpenAI format (nested 'function' field)
402
+ - Anthropic format (direct fields)
403
+ - Flat format (direct fields with args)
404
+ - Object-based formats (using getattr)
405
+ - JSON string arguments
406
+
407
+ Args:
408
+ tool_call: Tool call in any supported format (dict or object)
409
+
410
+ Returns:
411
+ Normalized tool call dict with guaranteed fields: name, args, id, type
412
+ """
413
+ try:
414
+ # Initialize with safe defaults
415
+ normalized = {
416
+ "name": "unknown",
417
+ "args": {},
418
+ "id": str(uuid4()),
419
+ "type": "function"
420
+ }
421
+
422
+ # Handle object formats (convert to dict first)
423
+ if not isinstance(tool_call, dict):
424
+ # Try to extract fields from object
425
+ tool_dict = {}
426
+
427
+ # Check for OpenAI-style nested function
428
+ if hasattr(tool_call, 'function'):
429
+ func = tool_call.function
430
+ tool_dict['function'] = {
431
+ 'name': getattr(func, 'name', 'unknown'),
432
+ 'arguments': getattr(func, 'arguments', '{}')
433
+ }
434
+ tool_dict['id'] = getattr(tool_call, 'id', str(uuid4()))
435
+ else:
436
+ # Direct attributes
437
+ tool_dict['name'] = getattr(tool_call, 'name', 'unknown')
438
+ tool_dict['args'] = getattr(tool_call, 'args', getattr(tool_call, 'arguments', {}))
439
+ tool_dict['id'] = getattr(tool_call, 'id', str(uuid4()))
440
+ tool_dict['type'] = getattr(tool_call, 'type', 'function')
441
+
442
+ tool_call = tool_dict
443
+
444
+ # Now handle dict format
445
+ if "function" in tool_call and isinstance(tool_call.get("function"), dict):
446
+ # OpenAI format with nested 'function'
447
+ func = tool_call["function"]
448
+ normalized["name"] = func.get("name", "unknown")
449
+
450
+ # Parse arguments if they're a JSON string
451
+ args = func.get("arguments", {})
452
+ logger.info(f"[NORMALIZE_TOOL_CALL] {normalized['name']}: raw args type={type(args).__name__}, value={str(args)[:300]}")
453
+
454
+ if isinstance(args, str):
455
+ try:
456
+ parsed_args = json.loads(args) if args else {}
457
+ # GROK 4.1 FIX: Deep parse to handle nested JSON strings
458
+ parsed_args = _deep_parse_json_strings(parsed_args)
459
+ logger.info(f"[NORMALIZE_TOOL_CALL] {normalized['name']}: parsed args (deep)={parsed_args}")
460
+ normalized["args"] = parsed_args
461
+ except (json.JSONDecodeError, ValueError) as e:
462
+ logger.warning(f"Failed to parse JSON arguments: {e}. Using raw string.")
463
+ normalized["args"] = {"raw": args}
464
+ else:
465
+ # Already a dict (native Anthropic/OpenAI path). Convert protobuf
466
+ # types (Gemini MapComposite) to serializable Python. Do NOT
467
+ # deep-parse string VALUES here: a legit string arg that merely
468
+ # looks like JSON (e.g. write_file content='{"k":"v"}') would be
469
+ # turned into a dict and then fail str-field validation, silently
470
+ # dropping the action. (Deep-parse stays on the string-args path
471
+ # above, where the whole blob was JSON — the real Grok case.)
472
+ normalized["args"] = _convert_to_serializable(args or {})
473
+
474
+ normalized["id"] = tool_call.get("id", str(uuid4()))
475
+ else:
476
+ # Direct format (Anthropic, etc.)
477
+ normalized["name"] = tool_call.get("name", "unknown")
478
+
479
+ # Handle various argument field names
480
+ args = tool_call.get("args") or tool_call.get("arguments") or tool_call.get("input", {})
481
+
482
+ # Parse if string
483
+ if isinstance(args, str):
484
+ try:
485
+ parsed_args = json.loads(args) if args else {}
486
+ # GROK 4.1 FIX: Deep parse to handle nested JSON strings
487
+ normalized["args"] = _deep_parse_json_strings(parsed_args)
488
+ except (json.JSONDecodeError, ValueError) as e:
489
+ logger.warning(f"Failed to parse JSON arguments: {e}. Using raw string.")
490
+ normalized["args"] = {"raw": args}
491
+ else:
492
+ # Already a dict (native path). Convert protobuf types to
493
+ # serializable Python. Do NOT deep-parse string VALUES here — a
494
+ # legit JSON-looking string arg would be corrupted into a dict/list
495
+ # and fail str-field validation. (See the OpenAI branch above.)
496
+ normalized["args"] = _convert_to_serializable(args or {})
497
+
498
+ normalized["id"] = tool_call.get("id", str(uuid4()))
499
+ normalized["type"] = tool_call.get("type", "function")
500
+
501
+ # Validate the normalized result
502
+ if not normalized["name"] or normalized["name"] == "unknown":
503
+ logger.warning(f"Tool call missing name: {tool_call}")
504
+ # Fall back to a safe default name
505
+ normalized["name"] = "error_unknown_tool"
506
+
507
+ # Ensure name is never empty (Anthropic rejects empty names)
508
+ if not normalized["name"] or not normalized["name"].strip():
509
+ logger.error(f"Tool call has empty name, using fallback: {tool_call}")
510
+ normalized["name"] = "error_empty_name"
511
+
512
+ # Ensure args is always a dict
513
+ if not isinstance(normalized["args"], dict):
514
+ logger.warning(f"Converting non-dict args to dict: {type(normalized['args'])}")
515
+ normalized["args"] = {"value": normalized["args"]}
516
+
517
+ return normalized
518
+
519
+ except Exception as e:
520
+ logger.error(f"Failed to normalize tool call: {e}. Input: {tool_call}")
521
+ # Return a safe fallback
522
+ return {
523
+ "name": "error",
524
+ "args": {"error": str(e), "original": str(tool_call)[:200]},
525
+ "id": str(uuid4()),
526
+ "type": "function"
527
+ }
528
+
529
+
530
+
531
+ # --- Backward-compat re-exports (P9) -------------------------------------------
532
+ # Message-sequence repair/validation moved to tool_message_repair.py; keep these
533
+ # importable from here (filters.py, service.py, modules/llm/openai_client.py).
534
+ from agents.task.agent.message_manager.tool_message_repair import ( # noqa: E402
535
+ detect_and_remove_duplicate_tool_calls,
536
+ repair_tool_message_pairs,
537
+ validate_tool_message_pairs,
538
+ repair_and_normalize,
539
+ )