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,444 @@
1
+ """Payment API endpoints for credit management and deposits."""
2
+
3
+ from fastapi import APIRouter, HTTPException, Depends, Request
4
+ from pydantic import BaseModel
5
+ from typing import List, Optional, Dict, Any
6
+ import logging
7
+ from api.auth_constants import ADMIN_ROLES
8
+ from modules.credits.pricing import pricing, WELCOME_BONUS, DEN_SIGNUP_ALLOWANCE
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ router = APIRouter(prefix="/payments", tags=["payments"])
13
+
14
+
15
+ class DepositAddressResponse(BaseModel):
16
+ """Deposit address response."""
17
+ user_id: str
18
+ deposit_address: str
19
+ chains: List[str]
20
+ qr_code_url: Optional[str] = None
21
+ instructions: str
22
+
23
+
24
+ class CreditBalanceResponse(BaseModel):
25
+ """Credit balance response."""
26
+ user_id: str
27
+ balance: int
28
+ lifetime_earned: int
29
+ lifetime_spent: int
30
+ tier: str
31
+
32
+
33
+ class TransactionResponse(BaseModel):
34
+ """Credit transaction response."""
35
+ id: int
36
+ user_id: str
37
+ amount: int
38
+ transaction_type: str
39
+ reason: str
40
+ session_id: Optional[str]
41
+ balance_before: int
42
+ balance_after: int
43
+ timestamp: str
44
+
45
+
46
+ class PaginatedTransactionsResponse(BaseModel):
47
+ """Paginated transactions response with metadata."""
48
+ transactions: List[TransactionResponse]
49
+ total: int
50
+ limit: int
51
+ offset: int
52
+ has_more: bool
53
+
54
+
55
+ class CryptoPaymentResponse(BaseModel):
56
+ """Crypto payment response."""
57
+ id: int
58
+ chain: str
59
+ token_symbol: str
60
+ amount: str
61
+ amount_usd: float
62
+ credits_purchased: int
63
+ status: str
64
+ detected_at: str
65
+
66
+
67
+ # Helper to get authenticated user from JWT — strict policy (rejects api_user / authenticated_api_user).
68
+ # Delegates to the canonical implementation in api.dependencies.
69
+ from api.dependencies import get_user_strict as get_authenticated_user
70
+
71
+
72
+ @router.get("/deposit-address", response_model=DepositAddressResponse)
73
+ async def get_deposit_address(
74
+ request: Request,
75
+ user_id: str = Depends(get_authenticated_user)
76
+ ):
77
+ """Get or create deposit address for user.
78
+
79
+ Returns a unique deposit address for receiving crypto payments.
80
+ Credits are automatically added when deposits are detected.
81
+ """
82
+ try:
83
+ from core.container import DependencyContainer
84
+ container = DependencyContainer.get_instance()
85
+
86
+ wallet_gen = container.get_service('wallet_generator')
87
+ db_manager = container.get_service('database_manager')
88
+
89
+ # Check if user is admin - admins don't need deposit addresses
90
+ role = getattr(request.state, 'role', 'user')
91
+ is_admin = role in ADMIN_ROLES
92
+
93
+ if not wallet_gen or not db_manager:
94
+ # If services not available and user is admin, return placeholder
95
+ if is_admin:
96
+ logger.info(f"Wallet generator unavailable, returning placeholder for admin {user_id}")
97
+ return {
98
+ "user_id": user_id,
99
+ "deposit_address": "N/A - Admin account has unlimited credits",
100
+ "chains": [],
101
+ "qr_code_url": None,
102
+ "instructions": "As an admin, you have unlimited credits and do not need to deposit funds."
103
+ }
104
+ raise HTTPException(
105
+ status_code=503,
106
+ detail="Payment services temporarily unavailable"
107
+ )
108
+
109
+ # Check if user already has deposit address
110
+ existing = await db_manager.fetch_one("""
111
+ SELECT deposit_address, generated_at
112
+ FROM user_deposit_addresses
113
+ WHERE user_id = ?
114
+ """, (user_id,))
115
+
116
+ if existing:
117
+ deposit_address = existing['deposit_address']
118
+ logger.info(f"Retrieved existing deposit address for user {user_id}")
119
+ else:
120
+ # Generate new deposit address
121
+ deposit_address = wallet_gen.generate_deposit_address(user_id)
122
+
123
+ # Store in database (user should exist with deterministic ID)
124
+ try:
125
+ await db_manager.execute("""
126
+ INSERT INTO user_deposit_addresses (
127
+ user_id, deposit_address, generated_at
128
+ ) VALUES (?, ?, datetime('now'))
129
+ """, (user_id, deposit_address))
130
+ logger.info(f"Generated new deposit address for user {user_id}: {deposit_address}")
131
+ except Exception as e:
132
+ # FK constraint - user doesn't exist, trigger re-auth
133
+ logger.error(f"Failed to create deposit address - user {user_id} not found: {e}")
134
+ raise HTTPException(
135
+ status_code=401,
136
+ detail="Session expired. Please sign in again with your wallet."
137
+ )
138
+
139
+ return DepositAddressResponse(
140
+ user_id=user_id,
141
+ deposit_address=deposit_address,
142
+ chains=["ethereum", "sepolia"],
143
+ qr_code_url=f"https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl={deposit_address}",
144
+ instructions=(
145
+ f"Send USDC, USDT, or ETH to this address on Ethereum mainnet or Sepolia testnet. "
146
+ f"Credits will be added automatically (1 credit = $0.01 USD). "
147
+ f"Minimum deposit: $5 USD."
148
+ )
149
+ )
150
+
151
+ except HTTPException:
152
+ raise
153
+ except Exception as e:
154
+ logger.error(f"Error getting deposit address for user {user_id}: {e}", exc_info=True)
155
+ raise HTTPException(
156
+ status_code=500,
157
+ detail="Failed to generate deposit address. Please try again later."
158
+ )
159
+
160
+
161
+ @router.get("/balance")
162
+ async def get_credit_balance(
163
+ request: Request,
164
+ user_id: str = Depends(get_authenticated_user)
165
+ ):
166
+ """Get current credit balance and usage statistics.
167
+
168
+ Shows:
169
+ - Current balance
170
+ - Lifetime earned/spent
171
+ - Tier
172
+ """
173
+ try:
174
+ from core.container import DependencyContainer
175
+ container = DependencyContainer.get_instance()
176
+
177
+ balance_mgr = container.get_service('balance_manager')
178
+ tier_mgr = container.get_service('tier_manager')
179
+
180
+ # Check if user is admin - admins get unlimited credits
181
+ role = getattr(request.state, 'role', 'user')
182
+ is_admin = role in ADMIN_ROLES
183
+
184
+ if not balance_mgr:
185
+ # If balance manager not available and user is admin, return unlimited balance
186
+ if is_admin:
187
+ logger.info(f"Balance manager unavailable, returning unlimited balance for admin {user_id}")
188
+ return {
189
+ "user_id": user_id,
190
+ "balance": 999999,
191
+ "lifetime_earned": 999999,
192
+ "lifetime_spent": 0,
193
+ "tier": "admin"
194
+ }
195
+ raise HTTPException(
196
+ status_code=503,
197
+ detail="Balance service temporarily unavailable"
198
+ )
199
+
200
+ # Get balance
201
+ balance_info = await balance_mgr.get_balance(user_id)
202
+
203
+ # Get tier
204
+ tier = "free"
205
+ if tier_mgr:
206
+ try:
207
+ tier = await tier_mgr.get_user_tier(user_id)
208
+ except HTTPException:
209
+ pass
210
+
211
+ return CreditBalanceResponse(
212
+ user_id=user_id,
213
+ balance=balance_info['balance'],
214
+ lifetime_earned=balance_info['lifetime_earned'],
215
+ lifetime_spent=balance_info['lifetime_spent'],
216
+ tier=tier
217
+ )
218
+
219
+ except HTTPException:
220
+ raise
221
+ except Exception as e:
222
+ logger.error(f"Error getting balance for user {user_id}: {e}", exc_info=True)
223
+ raise HTTPException(
224
+ status_code=500,
225
+ detail="Failed to retrieve balance. Please try again later."
226
+ )
227
+
228
+
229
+ @router.get("/transactions")
230
+ async def get_transactions(
231
+ request: Request,
232
+ user_id: str = Depends(get_authenticated_user),
233
+ limit: int = 100,
234
+ offset: int = 0,
235
+ paginated: bool = False
236
+ ):
237
+ """Get credit transaction history.
238
+
239
+ Shows all credit additions and deductions with reasons.
240
+
241
+ Args:
242
+ limit: Max transactions to return (default 100, max 500)
243
+ offset: Skip first N transactions for pagination
244
+ paginated: If true, returns {transactions, total, has_more} format
245
+ """
246
+ try:
247
+ from core.container import DependencyContainer
248
+ container = DependencyContainer.get_instance()
249
+
250
+ db_manager = container.get_service('database_manager')
251
+
252
+ # Check if user is admin
253
+ role = getattr(request.state, 'role', 'user')
254
+ is_admin = role in ADMIN_ROLES
255
+
256
+ if not db_manager:
257
+ # If database not available and user is admin, return empty
258
+ if is_admin:
259
+ logger.info(f"Database unavailable, returning empty transactions for admin {user_id}")
260
+ if paginated:
261
+ return {"transactions": [], "total": 0, "limit": limit, "offset": offset, "has_more": False}
262
+ return []
263
+ raise HTTPException(status_code=503, detail="Database temporarily unavailable")
264
+
265
+ # Enforce max limit to prevent abuse
266
+ limit = min(limit, 500)
267
+
268
+ # Get total count for pagination
269
+ total_result = await db_manager.fetch_one("""
270
+ SELECT COUNT(*) as total FROM credit_transactions WHERE user_id = ?
271
+ """, (user_id,))
272
+ total = total_result['total'] if total_result else 0
273
+
274
+ # Fetch transactions
275
+ transactions = await db_manager.fetch_all("""
276
+ SELECT
277
+ id,
278
+ user_id,
279
+ amount,
280
+ transaction_type,
281
+ reason,
282
+ session_id,
283
+ balance_before,
284
+ balance_after,
285
+ timestamp
286
+ FROM credit_transactions
287
+ WHERE user_id = ?
288
+ ORDER BY timestamp DESC
289
+ LIMIT ? OFFSET ?
290
+ """, (user_id, limit, offset))
291
+
292
+ tx_list = [
293
+ TransactionResponse(
294
+ id=tx['id'],
295
+ user_id=tx['user_id'],
296
+ amount=tx['amount'],
297
+ transaction_type=tx['transaction_type'],
298
+ reason=tx['reason'],
299
+ session_id=tx['session_id'],
300
+ balance_before=tx['balance_before'] or 0,
301
+ balance_after=tx['balance_after'] or 0,
302
+ timestamp=tx['timestamp']
303
+ )
304
+ for tx in transactions
305
+ ]
306
+
307
+ # Return paginated or simple list based on request
308
+ if paginated:
309
+ return PaginatedTransactionsResponse(
310
+ transactions=tx_list,
311
+ total=total,
312
+ limit=limit,
313
+ offset=offset,
314
+ has_more=(offset + len(tx_list)) < total
315
+ )
316
+
317
+ return tx_list
318
+
319
+ except HTTPException:
320
+ raise
321
+ except Exception as e:
322
+ logger.error(f"Error getting transactions for user {user_id}: {e}", exc_info=True)
323
+ raise HTTPException(
324
+ status_code=500,
325
+ detail="Failed to retrieve transactions. Please try again later."
326
+ )
327
+
328
+
329
+ @router.get("/deposits")
330
+ async def get_deposits(
331
+ request: Request,
332
+ user_id: str = Depends(get_authenticated_user),
333
+ limit: int = 100,
334
+ offset: int = 0
335
+ ):
336
+ """Get crypto deposit history.
337
+
338
+ Shows all detected deposits from blockchain.
339
+
340
+ Args:
341
+ limit: Max deposits to return (default 100, max 500)
342
+ offset: Skip first N deposits for pagination
343
+ """
344
+ try:
345
+ from core.container import DependencyContainer
346
+ container = DependencyContainer.get_instance()
347
+
348
+ db_manager = container.get_service('database_manager')
349
+
350
+ # Check if user is admin
351
+ role = getattr(request.state, 'role', 'user')
352
+ is_admin = role in ADMIN_ROLES
353
+
354
+ if not db_manager:
355
+ # If database not available and user is admin, return empty list
356
+ if is_admin:
357
+ logger.info(f"Database unavailable, returning empty deposits for admin {user_id}")
358
+ return []
359
+ raise HTTPException(status_code=503, detail="Database temporarily unavailable")
360
+
361
+ # Enforce max limit
362
+ limit = min(limit, 500)
363
+
364
+ # Fetch deposits
365
+ deposits = await db_manager.fetch_all("""
366
+ SELECT
367
+ id,
368
+ chain,
369
+ token_symbol,
370
+ amount,
371
+ amount_usd,
372
+ credits_purchased,
373
+ status,
374
+ detected_at
375
+ FROM crypto_payments
376
+ WHERE user_id = ?
377
+ ORDER BY detected_at DESC
378
+ LIMIT ? OFFSET ?
379
+ """, (user_id, limit, offset))
380
+
381
+ return [
382
+ CryptoPaymentResponse(
383
+ id=dep['id'],
384
+ chain=dep['chain'],
385
+ token_symbol=dep['token_symbol'],
386
+ amount=dep['amount'],
387
+ amount_usd=dep['amount_usd'],
388
+ credits_purchased=dep['credits_purchased'],
389
+ status=dep['status'],
390
+ detected_at=dep['detected_at']
391
+ )
392
+ for dep in deposits
393
+ ]
394
+
395
+ except HTTPException:
396
+ raise
397
+ except Exception as e:
398
+ logger.error(f"Error getting deposits for user {user_id}: {e}", exc_info=True)
399
+ raise HTTPException(
400
+ status_code=500,
401
+ detail="Failed to retrieve deposits. Please try again later."
402
+ )
403
+
404
+
405
+ @router.get("/pricing")
406
+ async def get_pricing():
407
+ """Get credit pricing information.
408
+
409
+ Public endpoint - no auth required.
410
+ """
411
+ return {
412
+ "credit_rate": pricing.CREDIT_VALUE_USD, # Dynamic from pricing config
413
+ "minimum_deposit_usd": 5.00,
414
+ "supported_tokens": [
415
+ {
416
+ "symbol": "USDC",
417
+ "name": "USD Coin",
418
+ "chains": ["ethereum", "sepolia"]
419
+ },
420
+ {
421
+ "symbol": "USDT",
422
+ "name": "Tether USD",
423
+ "chains": ["ethereum", "sepolia"]
424
+ },
425
+ {
426
+ "symbol": "ETH",
427
+ "name": "Ethereum",
428
+ "chains": ["ethereum", "sepolia"]
429
+ }
430
+ ],
431
+ "tier_allowances": {
432
+ "free": {
433
+ "welcome_bonus": WELCOME_BONUS,
434
+ "signup_allowance": 0,
435
+ "requires_den_token": False
436
+ },
437
+ "holder": {
438
+ "welcome_bonus": WELCOME_BONUS,
439
+ "signup_allowance_per_token": DEN_SIGNUP_ALLOWANCE,
440
+ "requires_den_token": True,
441
+ "den_tokens_required": "1+"
442
+ }
443
+ }
444
+ }
@@ -0,0 +1,138 @@
1
+ """Unified payment verification supporting credits and x402."""
2
+
3
+ from fastapi import Request, HTTPException
4
+ from typing import Tuple, Dict, Any
5
+ import logging
6
+ from api.auth_constants import is_admin, extract_admin_info
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ async def verify_payment_for_request(
12
+ request: Request,
13
+ cost_credits: int = 1
14
+ ) -> Tuple[str, Dict[str, Any]]:
15
+ """Verify payment via credits OR x402 OR admin bypass.
16
+
17
+ Returns:
18
+ Tuple of (payment_method: str, details: dict)
19
+
20
+ Raises:
21
+ HTTPException: If no valid payment found
22
+ """
23
+
24
+ # OPTION 0: Admin bypass (if enabled)
25
+ user_id = getattr(request.state, 'user_id', None)
26
+
27
+ # Use centralized admin detection
28
+ is_admin_user, role, wallet_address = extract_admin_info(request.state)
29
+
30
+ logger.info(f"🔍 Payment verification: user={user_id}, role={role}, wallet={wallet_address}, is_admin={is_admin_user}")
31
+
32
+ # Check if admin bypass is enabled
33
+ from core.container import DependencyContainer
34
+ container = DependencyContainer.get_instance()
35
+ config = container.config if container else None
36
+
37
+ # Admin bypass is enabled by default (config defaults to True)
38
+ bypass_enabled = True
39
+ if config:
40
+ bypass_enabled = getattr(config, 'bypass_payment_for_admins', True)
41
+
42
+ if bypass_enabled and is_admin_user:
43
+ logger.info(f"✅ Admin {user_id} (role: {role}, wallet: {wallet_address}) bypassed payment check")
44
+ return "admin_bypass", {
45
+ "user_id": user_id,
46
+ "role": role,
47
+ "wallet": wallet_address,
48
+ "bypass_reason": "admin_privilege",
49
+ "endpoint": request.url.path
50
+ }
51
+
52
+ # OPTION 1: Check for x402 payment FIRST (middleware already verified)
53
+ # This must come before credit check because x402 users have user_id set
54
+ payment_method = getattr(request.state, 'payment_method', None)
55
+
56
+ if payment_method == "x402":
57
+ # Payment verified by X402PaymentMiddleware
58
+ payer_address = getattr(request.state, 'payer_address', 'unknown')
59
+ logger.info(f"✅ x402 payment already verified for {payer_address[:10]}...")
60
+ return "x402", {
61
+ "payer_address": payer_address,
62
+ "user_id": user_id,
63
+ "endpoint": request.url.path
64
+ }
65
+
66
+ # OPTION 2: Check for JWT (credit system)
67
+ if user_id and user_id not in ['api_user', 'authenticated_api_user']:
68
+ # User authenticated → use credit system.
69
+ # C1: AUTHORIZE ONLY. Do NOT deduct here — the per-token LLMUsageTracker
70
+ # (modules/credits/usage_tracker.py::record_llm_usage) is the single
71
+ # deduction path, billed on ACTUAL token usage once the call completes.
72
+ # Deducting a flat cost_credits here too was double-billing every request.
73
+ from core.container import DependencyContainer
74
+ container = DependencyContainer.get_instance()
75
+ balance_mgr = container.get_service('balance_manager')
76
+
77
+ if not balance_mgr:
78
+ raise HTTPException(status_code=503, detail="Credit system unavailable")
79
+
80
+ has_credits = await balance_mgr.has_sufficient_balance(user_id, cost_credits)
81
+
82
+ if not has_credits:
83
+ raise HTTPException(
84
+ status_code=402,
85
+ detail="Insufficient credits. Deposit more or use x402."
86
+ )
87
+
88
+ return "credits", {
89
+ "user_id": user_id,
90
+ "credits_deducted": 0,
91
+ "endpoint": request.url.path
92
+ }
93
+
94
+ # OPTION 3: No payment → Return 402
95
+ raise HTTPException(
96
+ status_code=402,
97
+ detail="Payment required. Use credits (login) or x402 (pay-per-request)."
98
+ )
99
+
100
+
101
+ def payment_required_response(
102
+ request: Request,
103
+ cost_credits: int = 1
104
+ ) -> Dict[str, Any]:
105
+ """Generate 402 response with both payment options."""
106
+ from modules.x402.x402_integration import get_x402_price_usd
107
+
108
+ cost_usd = cost_credits * 0.01
109
+ # C2: single price SSOT — the quoted x402 price MUST equal the live charge.
110
+ x402_cost_usd = get_x402_price_usd()
111
+
112
+ # Get x402 handler from app state
113
+ payment_handler = getattr(request.app.state, 'x402_handler', None)
114
+
115
+ x402_details = {}
116
+ if payment_handler:
117
+ x402_response = payment_handler.create_payment_required_response(
118
+ amount_usd=x402_cost_usd,
119
+ asset="usdc",
120
+ metadata={"endpoint": request.url.path, "cost_credits": cost_credits}
121
+ )
122
+ x402_details = x402_response['body']['payment']
123
+
124
+ return {
125
+ "error": "Payment Required",
126
+ "code": "PAYMENT_REQUIRED",
127
+ "payment_options": {
128
+ "credits": {
129
+ "cost_usd": cost_usd,
130
+ "cost_credits": cost_credits,
131
+ "instructions": "Login with wallet at /api/auth/verify"
132
+ },
133
+ "x402": {
134
+ "cost_usd": x402_cost_usd,
135
+ "payment_details": x402_details
136
+ }
137
+ }
138
+ }