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,96 @@
1
+ """Generic OAuth2 auth-code + refresh-token provider (Item 4).
2
+
3
+ Driven by a plain config dict (client_id/secret/auth_url/token_url/scopes/
4
+ redirect_uri). The HTTP token POST is injectable (``http_post``) so the provider is
5
+ unit-testable WITHOUT hitting any live endpoint; the default uses ``httpx``.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import time
10
+ from typing import Any, Awaitable, Callable, Dict, Optional
11
+ from urllib.parse import urlencode
12
+
13
+ from tools.oauth.provider import OAuthError, OAuthProvider, OAuthToken
14
+
15
+ # (url, form_data) -> token-endpoint JSON dict
16
+ HttpPost = Callable[[str, Dict[str, Any]], Awaitable[Dict[str, Any]]]
17
+
18
+
19
+ class GenericOAuth2Provider(OAuthProvider):
20
+ def __init__(self, name: str, config: Dict[str, Any], *, http_post: Optional[HttpPost] = None) -> None:
21
+ self.name = name
22
+ self.client_id = config["client_id"]
23
+ self.client_secret = config.get("client_secret", "")
24
+ self.auth_url = config["auth_url"]
25
+ self.token_url = config["token_url"]
26
+ self.scopes = config.get("scopes") or []
27
+ self.redirect_uri = config.get("redirect_uri")
28
+ self._http_post = http_post
29
+
30
+ def authorize_url(self, *, state: Optional[str] = None, redirect_uri: Optional[str] = None) -> str:
31
+ params = {
32
+ "response_type": "code",
33
+ "client_id": self.client_id,
34
+ "redirect_uri": redirect_uri or self.redirect_uri or "",
35
+ }
36
+ if self.scopes:
37
+ params["scope"] = " ".join(self.scopes)
38
+ if state:
39
+ params["state"] = state
40
+ sep = "&" if "?" in self.auth_url else "?"
41
+ return f"{self.auth_url}{sep}{urlencode(params)}"
42
+
43
+ async def exchange_code(self, code: str, *, redirect_uri: Optional[str] = None) -> OAuthToken:
44
+ data = {
45
+ "grant_type": "authorization_code",
46
+ "code": code,
47
+ "client_id": self.client_id,
48
+ "client_secret": self.client_secret,
49
+ "redirect_uri": redirect_uri or self.redirect_uri or "",
50
+ }
51
+ return self._token_from_response(await self._post_token(data))
52
+
53
+ async def refresh(self, token: OAuthToken) -> OAuthToken:
54
+ if not token.refresh_token:
55
+ raise OAuthError(f"no refresh_token to refresh provider '{self.name}'")
56
+ data = {
57
+ "grant_type": "refresh_token",
58
+ "refresh_token": token.refresh_token,
59
+ "client_id": self.client_id,
60
+ "client_secret": self.client_secret,
61
+ }
62
+ new = self._token_from_response(await self._post_token(data))
63
+ # Many providers don't rotate the refresh token — keep the old one if absent.
64
+ if not new.refresh_token:
65
+ new.refresh_token = token.refresh_token
66
+ return new
67
+
68
+ # -- internals ------------------------------------------------------------
69
+
70
+ async def _post_token(self, data: Dict[str, Any]) -> Dict[str, Any]:
71
+ if self._http_post is not None:
72
+ return await self._http_post(self.token_url, data)
73
+ import httpx # lazy: only needed for the live path
74
+
75
+ async with httpx.AsyncClient(timeout=30.0) as client:
76
+ resp = await client.post(self.token_url, data=data)
77
+ resp.raise_for_status()
78
+ return resp.json()
79
+
80
+ @staticmethod
81
+ def _token_from_response(resp: Dict[str, Any]) -> OAuthToken:
82
+ if not resp or "access_token" not in resp:
83
+ raise OAuthError(f"token endpoint returned no access_token: {resp}")
84
+ expires_at = None
85
+ if "expires_in" in resp:
86
+ try:
87
+ expires_at = time.time() + float(resp["expires_in"])
88
+ except (TypeError, ValueError):
89
+ expires_at = None
90
+ return OAuthToken(
91
+ access_token=resp["access_token"],
92
+ refresh_token=resp.get("refresh_token"),
93
+ expires_at=expires_at,
94
+ token_type=resp.get("token_type", "Bearer"),
95
+ scope=resp.get("scope"),
96
+ )
@@ -0,0 +1,280 @@
1
+ import logging
2
+ from typing import List, Optional, Dict, Any
3
+ import json
4
+ import aiohttp
5
+ import ssl
6
+ import certifi
7
+ from core.exceptions import ConfigurationError, APIError, AuthenticationError, ToolError
8
+ from core.config import BotConfig
9
+ from .base_tool import BaseTool
10
+ from tools.controller.views import PerplexitySearchAction, PerplexityAnalyzeAction, PerplexitySourcesAction
11
+
12
+ class PerplexityTool(BaseTool):
13
+ """Service for interacting with Perplexity API."""
14
+
15
+ @property
16
+ def required_services(self) -> Dict[str, str]:
17
+ """Get required services."""
18
+ return {
19
+ 'rate_limit_manager': 'Rate limit management'
20
+ }
21
+
22
+ @property
23
+ def optional_services(self) -> Dict[str, str]:
24
+ """Get optional services."""
25
+ return {
26
+ 'cache_manager': 'Cache for API responses' # Only need caching
27
+ }
28
+
29
+ def __init__(self, name: str, config: BotConfig, container: Optional[Any] = None):
30
+ """Initialize Perplexity service."""
31
+ super().__init__(name=name, config=config, container=container)
32
+
33
+ # Initialize SSL context during construction
34
+ self.ssl_context = ssl.create_default_context(cafile=certifi.where())
35
+ self.base_url = "https://api.perplexity.ai/chat/completions"
36
+
37
+ # Enable if API key is present
38
+ self._enabled = bool(getattr(config, 'perplexity_api_key', None))
39
+ if not self._enabled:
40
+ self.logger.warning("Perplexity API key not found, service will be disabled")
41
+
42
+ @property
43
+ def required_config(self) -> Dict[str, str]:
44
+ """Get required configuration keys."""
45
+ return {
46
+ 'perplexity_api_key': 'Perplexity API key'
47
+ }
48
+
49
+ async def _initialize(self) -> None:
50
+ """Initialize Perplexity service."""
51
+ try:
52
+ # Services are automatically injected by BaseService
53
+ # Just validate API key BEFORE registering actions
54
+ if not self.config.perplexity_api_key or self.config.perplexity_api_key == "placeholder":
55
+ self.logger.warning("Perplexity API key not configured")
56
+ self._enabled = False
57
+ # Mark service as unavailable but don't raise exception
58
+ from tools.base_tool import ToolStatus
59
+ self._status = ToolStatus.FAILED
60
+ # Don't register actions for disabled service
61
+ return
62
+
63
+ # Only register actions if we have a valid API key
64
+ # Call parent's _initialize to register decorated actions
65
+ await super()._initialize()
66
+
67
+ # Test API connection
68
+ try:
69
+ await self._test_api_connection()
70
+ except Exception as e:
71
+ self.logger.error(f"Failed to connect to Perplexity API: {e}")
72
+ self._enabled = False
73
+ from tools.base_tool import ToolStatus
74
+ self._status = ToolStatus.FAILED
75
+ return
76
+
77
+ except Exception as e:
78
+ self.logger.error(f"Failed to initialize Perplexity service: {e}")
79
+ # Don't raise for optional services - mark as unavailable
80
+ from tools.base_tool import ToolStatus
81
+ self._status = ToolStatus.FAILED
82
+ self._enabled = False
83
+
84
+ async def _test_api_connection(self) -> None:
85
+ """Test API connection."""
86
+ try:
87
+ # Make a minimal test request
88
+ messages = [
89
+ {"role": "user", "content": "test"}
90
+ ]
91
+ result = await self._make_request(messages)
92
+ if "error" in result:
93
+ raise APIError(f"API test failed: {result['error']}")
94
+ self.logger.info("Perplexity API connection test successful")
95
+ except AuthenticationError as e:
96
+ self.logger.error(f"Perplexity API authentication failed: {e}")
97
+ self.logger.error("The API key appears to be invalid. Please update PERPLEXITY_API_KEY in your .env file")
98
+ self.logger.error("You can generate a new API key at: https://www.perplexity.ai/settings/api")
99
+ raise
100
+ except Exception as e:
101
+ self.logger.error(f"Perplexity API connection test failed: {e}")
102
+ raise
103
+
104
+ async def _cleanup(self) -> None:
105
+ """Cleanup Perplexity service resources."""
106
+ try:
107
+ # Close any open connections
108
+ if hasattr(self, 'session') and self.session:
109
+ await self.session.close()
110
+
111
+ self.logger.info("Perplexity service cleaned up successfully")
112
+
113
+ except Exception as e:
114
+ self.logger.error(f"Error during Perplexity service cleanup: {e}")
115
+ raise ToolError(f"Failed to cleanup Perplexity service: {e}")
116
+
117
+ @BaseTool.action(
118
+ 'Search for information using Perplexity AI',
119
+ param_model=PerplexitySearchAction
120
+ )
121
+ async def perplexity_search(self, params: PerplexitySearchAction):
122
+ """Search using Perplexity API."""
123
+ await self.ensure_initialized()
124
+
125
+ if not self._enabled:
126
+ raise ConfigurationError("Perplexity service is not enabled")
127
+
128
+ try:
129
+ query = params.query.strip()
130
+ if not query:
131
+ return "Please provide a search query."
132
+
133
+ messages = [
134
+ {"role": "system", "content": "You are a helpful AI assistant that provides accurate and concise information."},
135
+ {"role": "user", "content": query}
136
+ ]
137
+
138
+ result = await self._make_request(messages)
139
+ if "error" in result:
140
+ return f"Error: {result['error']}"
141
+
142
+ try:
143
+ content = result["choices"][0]["message"]["content"]
144
+ # Limit response size to prevent overwhelming the agent
145
+ MAX_RESPONSE_LENGTH = 50000 # 50k chars (reasonable for search responses)
146
+ if len(content) > MAX_RESPONSE_LENGTH:
147
+ self.logger.warning(f"Perplexity response truncated from {len(content)} to {MAX_RESPONSE_LENGTH} characters")
148
+ content = content[:MAX_RESPONSE_LENGTH] + "\n\n[Response truncated due to size...]"
149
+ return content
150
+ except (KeyError, IndexError) as e:
151
+ return f"Error parsing response: {str(e)}"
152
+
153
+ except AuthenticationError as e:
154
+ self.logger.error(f"Perplexity authentication error: {str(e)}")
155
+ self.logger.error("The API key is invalid. Please update PERPLEXITY_API_KEY in config/.env.development")
156
+ self.logger.error("Generate a new key at: https://www.perplexity.ai/settings/api")
157
+ raise APIError(f"Search failed: Invalid API key. Please update PERPLEXITY_API_KEY in the configuration.")
158
+ except Exception as e:
159
+ self.logger.error(f"Perplexity search error: {str(e)}")
160
+ raise APIError(f"Search failed: {str(e)}")
161
+
162
+ @BaseTool.action(
163
+ 'Get detailed analysis on a topic using Perplexity AI',
164
+ param_model=PerplexityAnalyzeAction
165
+ )
166
+ async def perplexity_analyze(self, params: PerplexityAnalyzeAction):
167
+ """Get detailed analysis from Perplexity."""
168
+ await self.ensure_initialized()
169
+
170
+ try:
171
+ topic = params.topic.strip()
172
+ if not topic:
173
+ return "Please provide a topic to analyze."
174
+
175
+ messages = [
176
+ {"role": "system", "content": "You are an analytical AI assistant. Provide a detailed analysis of the given topic."},
177
+ {"role": "user", "content": f"Please provide a detailed analysis of: {topic}"}
178
+ ]
179
+
180
+ result = await self._make_request(messages)
181
+ if "error" in result:
182
+ return result["error"]
183
+
184
+ try:
185
+ content = result["choices"][0]["message"]["content"]
186
+ # Limit response size to prevent overwhelming the agent
187
+ MAX_RESPONSE_LENGTH = 50000 # 50k chars (reasonable for analysis responses)
188
+ if len(content) > MAX_RESPONSE_LENGTH:
189
+ self.logger.warning(f"Perplexity response truncated from {len(content)} to {MAX_RESPONSE_LENGTH} characters")
190
+ content = content[:MAX_RESPONSE_LENGTH] + "\n\n[Response truncated due to size...]"
191
+ return content
192
+ except (KeyError, IndexError) as e:
193
+ return f"Error parsing response: {str(e)}"
194
+ except Exception as e:
195
+ self.logger.error(f"Perplexity analyze error: {str(e)}")
196
+ raise
197
+
198
+ @BaseTool.action(
199
+ 'Get trusted sources and references on a topic using Perplexity AI',
200
+ param_model=PerplexitySourcesAction
201
+ )
202
+ async def perplexity_sources(self, params: PerplexitySourcesAction):
203
+ """Get sources for a query."""
204
+ await self.ensure_initialized()
205
+
206
+ query = params.topic.strip()
207
+ if not query:
208
+ return "Please provide a query to get sources for."
209
+
210
+ messages = [
211
+ {"role": "system", "content": "You are a research assistant. Provide relevant sources and references for the query."},
212
+ {"role": "user", "content": f"Please provide sources and references for: {query}"}
213
+ ]
214
+
215
+ result = await self._make_request(messages)
216
+ if "error" in result:
217
+ return result["error"]
218
+
219
+ try:
220
+ return result["choices"][0]["message"]["content"]
221
+ except (KeyError, IndexError) as e:
222
+ return f"Error parsing response: {str(e)}"
223
+
224
+ async def ensure_initialized(self) -> None:
225
+ """Ensure service is initialized."""
226
+ if not self._initialized:
227
+ await self.initialize()
228
+
229
+ async def test_connection(self) -> bool:
230
+ """Test if the Perplexity API is accessible and properly configured"""
231
+ if not self.config.perplexity_api_key:
232
+ return False
233
+
234
+ try:
235
+ # Make a minimal test request
236
+ messages = [
237
+ {"role": "user", "content": "test"}
238
+ ]
239
+ result = await self._make_request(messages)
240
+ return "error" not in result
241
+ except Exception as e:
242
+ self.logger.warning(f"Perplexity API connection test failed: {str(e)}")
243
+ return False
244
+
245
+ async def _make_request(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
246
+ """Make a request to the Perplexity API."""
247
+ if not self.config.perplexity_api_key:
248
+ raise ConfigurationError("Perplexity API key not configured")
249
+
250
+ headers = {
251
+ "Authorization": f"Bearer {self.config.perplexity_api_key}",
252
+ "Content-Type": "application/json"
253
+ }
254
+
255
+ data = {
256
+ "model": "sonar",
257
+ "messages": messages
258
+ }
259
+
260
+ try:
261
+ # Use SSL context in ClientSession
262
+ conn = aiohttp.TCPConnector(ssl=self.ssl_context)
263
+ async with aiohttp.ClientSession(connector=conn) as session:
264
+ async with session.post(
265
+ self.base_url,
266
+ headers=headers,
267
+ json=data,
268
+ ssl=self.ssl_context
269
+ ) as response:
270
+ if response.status == 200:
271
+ return await response.json()
272
+ elif response.status == 401:
273
+ raise AuthenticationError("Invalid Perplexity API key")
274
+ else:
275
+ error_text = await response.text()
276
+ self.logger.error(f"Perplexity API error: {error_text}")
277
+ raise APIError(f"API error: {response.status} - {error_text}")
278
+ except aiohttp.ClientError as e:
279
+ self.logger.error(f"Request error: {str(e)}")
280
+ raise APIError(f"Request failed: {str(e)}")
@@ -0,0 +1,41 @@
1
+ """
2
+ Polymarket Integration Package.
3
+
4
+ Provides access to Polymarket prediction markets through PolymarketTool,
5
+ which uses direct API integration with Gamma + CLOB APIs.
6
+
7
+ Usage:
8
+ polymarket.search_markets(query="...")
9
+ polymarket.get_trending_markets(limit=10)
10
+ polymarket.get_market_details(market_id="...")
11
+ """
12
+
13
+ from tools.polymarket.service import PolymarketTool, PolymarketDataTool
14
+
15
+ from tools.polymarket.models import (
16
+ PolymarketCredentials,
17
+ TradingLimits,
18
+ ExecutionResult,
19
+ ApiCredentials,
20
+ POLYGON_MAINNET,
21
+ POLYGON_AMOY_TESTNET,
22
+ SIGNATURE_TYPE_EOA,
23
+ SIGNATURE_TYPE_MAGIC,
24
+ SIGNATURE_TYPE_PROXY,
25
+ MIN_ORDER_VALUE_USD,
26
+ )
27
+
28
+ __all__ = [
29
+ "PolymarketTool",
30
+ "PolymarketDataTool",
31
+ "PolymarketCredentials",
32
+ "TradingLimits",
33
+ "ExecutionResult",
34
+ "ApiCredentials",
35
+ "POLYGON_MAINNET",
36
+ "POLYGON_AMOY_TESTNET",
37
+ "SIGNATURE_TYPE_EOA",
38
+ "SIGNATURE_TYPE_MAGIC",
39
+ "SIGNATURE_TYPE_PROXY",
40
+ "MIN_ORDER_VALUE_USD",
41
+ ]
@@ -0,0 +1,76 @@
1
+ """Single seam for the Polymarket CLOB trading client.
2
+
3
+ Polymarket archived `py-clob-client` in 2026 ("no longer functional"). The current
4
+ maintained low-level client is `py-clob-client-v2`. This module is the ONE place that
5
+ import happens, so a missing/incompatible client is a LOUD, actionable failure instead
6
+ of silently dropping every trading action (the prior behavior, which degraded the tool
7
+ to read-only with no signal).
8
+
9
+ Contract:
10
+ - The READ surface never imports this module.
11
+ - The TRADE surface calls ``require_clob()`` before any order action; absence yields a
12
+ typed ``PolymarketDependencyError`` with an install hint, surfaced to the operator.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from typing import Any, Dict, Optional
17
+
18
+ from core.exceptions import ToolError
19
+
20
+ INSTALL_HINT = "pip install py-clob-client-v2"
21
+
22
+ # The archived ``py_clob_client`` is intentionally NOT imported here — it is no longer
23
+ # functional. Only the maintained v2 client is supported.
24
+ try:
25
+ from py_clob_client_v2.client import ClobClient # type: ignore
26
+ from py_clob_client_v2.clob_types import ( # type: ignore
27
+ ApiCreds,
28
+ OrderArgs,
29
+ OrderType,
30
+ BalanceAllowanceParams,
31
+ AssetType,
32
+ )
33
+
34
+ CLOB_AVAILABLE = True
35
+ CLOB_IMPORT_ERROR: Optional[str] = None
36
+ try: # version is best-effort, never load-bearing
37
+ from importlib.metadata import version as _pkg_version
38
+
39
+ CLOB_VERSION: Optional[str] = _pkg_version("py-clob-client-v2")
40
+ except Exception: # pragma: no cover - metadata absent
41
+ CLOB_VERSION = None
42
+ except Exception as _exc: # ImportError or any transitive failure
43
+ ClobClient = None # type: ignore
44
+ ApiCreds = None # type: ignore
45
+ OrderArgs = None # type: ignore
46
+ OrderType = None # type: ignore
47
+ BalanceAllowanceParams = None # type: ignore
48
+ AssetType = None # type: ignore
49
+ CLOB_AVAILABLE = False
50
+ CLOB_IMPORT_ERROR = f"{type(_exc).__name__}: {_exc}"
51
+ CLOB_VERSION = None
52
+
53
+
54
+ class PolymarketDependencyError(ToolError):
55
+ """Raised when a Polymarket trade action needs the CLOB client but it is absent."""
56
+
57
+
58
+ def trade_capability() -> Dict[str, Any]:
59
+ """Report whether the trade surface can function, with an actionable hint."""
60
+ if CLOB_AVAILABLE:
61
+ return {"available": True, "reason": "py-clob-client-v2 importable", "install_hint": INSTALL_HINT, "version": CLOB_VERSION}
62
+ return {
63
+ "available": False,
64
+ "reason": f"Polymarket trading client unavailable ({CLOB_IMPORT_ERROR})",
65
+ "install_hint": INSTALL_HINT,
66
+ "version": None,
67
+ }
68
+
69
+
70
+ def require_clob() -> None:
71
+ """Raise a typed, actionable error if the CLOB trading client is missing."""
72
+ if not CLOB_AVAILABLE:
73
+ raise PolymarketDependencyError(
74
+ f"Polymarket trading requires py-clob-client-v2 ({INSTALL_HINT}); "
75
+ f"import failed: {CLOB_IMPORT_ERROR}"
76
+ )
@@ -0,0 +1,182 @@
1
+ """
2
+ Polymarket data models.
3
+
4
+ Defines credentials, trading limits, and execution results.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Any, Dict, List, Optional
9
+
10
+
11
+ # Chain IDs
12
+ POLYGON_MAINNET = 137
13
+ POLYGON_AMOY_TESTNET = 80002
14
+
15
+ # Signature Types for CLOB client authentication
16
+ SIGNATURE_TYPE_EOA = 0 # Standard EOA (MetaMask, hardware wallets)
17
+ SIGNATURE_TYPE_MAGIC = 1 # Email/Magic wallet (delegated signing)
18
+ SIGNATURE_TYPE_PROXY = 2 # Browser wallet proxy (Polymarket website users)
19
+
20
+ # Trading Constants
21
+ MIN_ORDER_VALUE_USD = 1.00
22
+
23
+ # Polymarket Contract Addresses (Polygon)
24
+ USDC_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
25
+ CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"
26
+ EXCHANGE_ADDRESS = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"
27
+ NEG_RISK_EXCHANGE = "0xC5d563A36AE78145C45a50134d48A1215220f80a"
28
+ NEG_RISK_ADAPTER = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"
29
+
30
+
31
+ @dataclass
32
+ class TradingLimits:
33
+ """
34
+ Trading safety limits for Polymarket operations.
35
+
36
+ All limits are enforced at the tool level before
37
+ making API calls.
38
+ """
39
+ max_order_size_usd: int = 1000
40
+ max_total_exposure_usd: int = 5000
41
+ max_position_per_market_usd: int = 2000
42
+ min_liquidity_required: int = 10000
43
+ max_spread_tolerance: float = 0.05
44
+ require_confirmation_above_usd: int = 500
45
+ enable_autonomous_trading: bool = False
46
+ allowed_categories: List[str] = field(default_factory=lambda: ["*"])
47
+ blocked_markets: List[str] = field(default_factory=list)
48
+
49
+ def to_dict(self) -> Dict[str, Any]:
50
+ """Convert to dictionary for storage."""
51
+ return {
52
+ "max_order_size_usd": self.max_order_size_usd,
53
+ "max_total_exposure_usd": self.max_total_exposure_usd,
54
+ "max_position_per_market_usd": self.max_position_per_market_usd,
55
+ "min_liquidity_required": self.min_liquidity_required,
56
+ "max_spread_tolerance": self.max_spread_tolerance,
57
+ "require_confirmation_above_usd": self.require_confirmation_above_usd,
58
+ "enable_autonomous_trading": self.enable_autonomous_trading,
59
+ "allowed_categories": self.allowed_categories,
60
+ "blocked_markets": self.blocked_markets,
61
+ }
62
+
63
+ @classmethod
64
+ def from_dict(cls, data: Dict[str, Any]) -> "TradingLimits":
65
+ """Create from dictionary."""
66
+ if not data:
67
+ return cls()
68
+ return cls(
69
+ max_order_size_usd=data.get("max_order_size_usd", 1000),
70
+ max_total_exposure_usd=data.get("max_total_exposure_usd", 5000),
71
+ max_position_per_market_usd=data.get("max_position_per_market_usd", 2000),
72
+ min_liquidity_required=data.get("min_liquidity_required", 10000),
73
+ max_spread_tolerance=data.get("max_spread_tolerance", 0.05),
74
+ require_confirmation_above_usd=data.get("require_confirmation_above_usd", 500),
75
+ enable_autonomous_trading=data.get("enable_autonomous_trading", False),
76
+ allowed_categories=data.get("allowed_categories", ["*"]),
77
+ blocked_markets=data.get("blocked_markets", []),
78
+ )
79
+
80
+
81
+ @dataclass
82
+ class ApiCredentials:
83
+ """
84
+ L2 API credentials for authenticated CLOB operations.
85
+
86
+ These are created via the CLOB API using the private key,
87
+ and stored encrypted for reuse.
88
+ """
89
+ api_key: str
90
+ api_secret: str # Same as passphrase in some contexts
91
+ api_passphrase: str
92
+ created_at: Optional[str] = None
93
+
94
+ def to_dict(self) -> Dict[str, Any]:
95
+ """Convert to dictionary (secrets included for encrypted storage)."""
96
+ return {
97
+ "api_key": self.api_key,
98
+ "api_secret": self.api_secret,
99
+ "api_passphrase": self.api_passphrase,
100
+ "created_at": self.created_at,
101
+ }
102
+
103
+ @classmethod
104
+ def from_dict(cls, data: Dict[str, Any]) -> Optional["ApiCredentials"]:
105
+ """Create from dictionary."""
106
+ if not data or not data.get("api_key"):
107
+ return None
108
+ return cls(
109
+ api_key=data.get("api_key", ""),
110
+ api_secret=data.get("api_secret", ""),
111
+ api_passphrase=data.get("api_passphrase", ""),
112
+ created_at=data.get("created_at"),
113
+ )
114
+
115
+
116
+ @dataclass
117
+ class PolymarketCredentials:
118
+ """
119
+ User's Polymarket credentials with proxy wallet support.
120
+
121
+ The private_key is stored encrypted in the database and only
122
+ decrypted in memory when needed for API authentication.
123
+
124
+ L2 API credentials (api_credentials) are created via CLOB API
125
+ and stored for reuse to avoid repeated key creation.
126
+
127
+ Polymarket uses a proxy wallet system:
128
+ - wallet_address: EOA address (derived from private key)
129
+ - proxy_wallet_address: Smart contract wallet (shown on Polymarket profile)
130
+
131
+ Most Polymarket website users need signature_type=2 (PROXY).
132
+ """
133
+ user_id: str
134
+ wallet_address: Optional[str] = None # EOA address (derived from private key)
135
+ proxy_wallet_address: Optional[str] = None # Smart contract wallet (from Polymarket profile)
136
+ private_key: Optional[str] = field(default=None, repr=False) # Never log this
137
+ signature_type: int = SIGNATURE_TYPE_PROXY # Default to proxy (2) - most common
138
+ demo_mode: bool = True
139
+ enabled: bool = True
140
+ chain_id: int = POLYGON_MAINNET
141
+ trading_limits: TradingLimits = field(default_factory=TradingLimits)
142
+ api_credentials: Optional[ApiCredentials] = field(default=None, repr=False)
143
+ allowances_verified: bool = False # Track if allowances are configured
144
+
145
+ @property
146
+ def funder_address(self) -> Optional[str]:
147
+ """
148
+ Get the address that holds funds (proxy wallet if set, else EOA).
149
+
150
+ This is the address used for balance checks and as the 'funder'
151
+ parameter in CLOB client for proxy wallet users.
152
+ """
153
+ return self.proxy_wallet_address or self.wallet_address
154
+
155
+ def is_configured(self) -> bool:
156
+ """Check if credentials are properly configured."""
157
+ if self.demo_mode:
158
+ return True
159
+ return bool(self.wallet_address and self.private_key)
160
+
161
+ def has_api_credentials(self) -> bool:
162
+ """Check if L2 API credentials are available."""
163
+ return self.api_credentials is not None
164
+
165
+ def can_trade(self) -> bool:
166
+ """Check if user can perform trading operations."""
167
+ return (
168
+ not self.demo_mode
169
+ and self.enabled
170
+ and self.is_configured()
171
+ and self.trading_limits.enable_autonomous_trading
172
+ )
173
+
174
+
175
+ @dataclass
176
+ class ExecutionResult:
177
+ """Result from executing a Polymarket action."""
178
+ success: bool
179
+ data: Optional[Any] = None
180
+ error: Optional[str] = None
181
+ tool_name: Optional[str] = None
182
+ execution_time_ms: float = 0.0