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
api/auth_constants.py ADDED
@@ -0,0 +1,28 @@
1
+ """Server-side auth constants — re-exports from core.constants.
2
+
3
+ The canonical definitions live in core/constants.py so agent-side code can
4
+ import them without depending on the api/ package. This shim preserves
5
+ existing `from api.auth_constants import ...` call sites.
6
+ """
7
+
8
+ from core.constants import ( # noqa: F401
9
+ VALID_ROLES,
10
+ ADMIN_ROLES,
11
+ ASSIGNABLE_ROLES,
12
+ MANAGEMENT_ROLES,
13
+ ROLE_MANAGEMENT_ROLES,
14
+ VALID_TIERS,
15
+ FULL_ACCESS_TIERS,
16
+ PAID_TIERS,
17
+ is_admin_role,
18
+ is_admin_wallet,
19
+ is_admin,
20
+ can_manage_users,
21
+ can_change_roles,
22
+ extract_admin_info,
23
+ has_full_access,
24
+ requires_payment,
25
+ validate_role,
26
+ validate_assignable_role,
27
+ validate_tier,
28
+ )
api/auth_endpoints.py ADDED
@@ -0,0 +1,402 @@
1
+ """Free wallet authentication endpoints using SIWE (Sign-In with Ethereum).
2
+
3
+ Replaces expensive Privy with free industry-standard SIWE.
4
+ """
5
+
6
+ from fastapi import APIRouter, HTTPException, Depends, Request
7
+ from fastapi.responses import JSONResponse
8
+ from pydantic import BaseModel, field_validator
9
+ import jwt
10
+ from datetime import datetime, timedelta
11
+ import logging
12
+ import os
13
+ import re
14
+ from api.auth_constants import is_admin_wallet
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ router = APIRouter(tags=["authentication"])
19
+
20
+ _ADDR_RE = re.compile(r"^0x[0-9a-fA-F]{40}$")
21
+
22
+
23
+ def normalize_eth_address(value: str) -> str:
24
+ """Validate + EIP-55 checksum-normalize an Ethereum address.
25
+
26
+ Normalizing canonicalizes casing so the same wallet maps to ONE identity
27
+ (the address becomes the JWT sub / user_id / admin-allowlist key downstream).
28
+ Raises ValueError on a malformed address (-> 422 at the request boundary).
29
+ """
30
+ if not isinstance(value, str) or not _ADDR_RE.match(value.strip()):
31
+ raise ValueError("invalid Ethereum address")
32
+ try:
33
+ from eth_utils import to_checksum_address
34
+ return to_checksum_address(value.strip())
35
+ except Exception:
36
+ # eth_utils missing/odd input: keep validation, skip checksum normalization.
37
+ return value.strip()
38
+
39
+
40
+ class NonceRequest(BaseModel):
41
+ """Request nonce for wallet authentication."""
42
+ wallet_address: str
43
+ chain_id: int = 1 # Ethereum mainnet by default
44
+
45
+ @field_validator("wallet_address")
46
+ @classmethod
47
+ def _check_addr(cls, v: str) -> str:
48
+ return normalize_eth_address(v)
49
+
50
+
51
+ class NonceResponse(BaseModel):
52
+ """SIWE message and nonce."""
53
+ message: str
54
+ nonce: str
55
+ issued_at: str
56
+ expiration: str
57
+
58
+
59
+ class VerifyRequest(BaseModel):
60
+ """Verify wallet signature."""
61
+ wallet_address: str
62
+ message: str
63
+ signature: str
64
+ nonce: str
65
+ chain: str = 'ethereum'
66
+
67
+ @field_validator("wallet_address")
68
+ @classmethod
69
+ def _check_addr(cls, v: str) -> str:
70
+ return normalize_eth_address(v)
71
+
72
+
73
+ class AuthResponse(BaseModel):
74
+ """Authentication response with JWT."""
75
+ token: str
76
+ user_id: str
77
+ wallet_address: str
78
+ role: str
79
+ tier: str
80
+ expires_at: str
81
+
82
+
83
+ @router.post("/nonce", response_model=NonceResponse)
84
+ async def get_nonce(request: NonceRequest):
85
+ """
86
+ Generate SIWE message and nonce for wallet authentication.
87
+
88
+ FREE - no third-party service needed!
89
+
90
+ Example:
91
+ POST /api/auth/nonce
92
+ {
93
+ "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
94
+ "chain_id": 137
95
+ }
96
+ """
97
+
98
+ from core.container import DependencyContainer
99
+ import os
100
+
101
+ container = DependencyContainer.get_instance()
102
+ siwe_auth = container.get_service('siwe_authenticator')
103
+
104
+ if not siwe_auth:
105
+ raise HTTPException(status_code=500, detail="SIWE authenticator not initialized")
106
+
107
+ # Get domain from environment or default to the local webview
108
+ domain = os.environ.get("WEBVIEW_DOMAIN", "localhost:3000")
109
+ uri = f"https://{domain}"
110
+
111
+ # Create SIWE message
112
+ result = await siwe_auth.create_siwe_message(
113
+ wallet_address=request.wallet_address,
114
+ domain=domain,
115
+ uri=uri,
116
+ chain_id=request.chain_id
117
+ )
118
+
119
+ return NonceResponse(**result)
120
+
121
+
122
+ @router.post("/verify")
123
+ async def verify_signature(request: VerifyRequest):
124
+ """Verify wallet signature and return JWT with HTTP-only cookie."""
125
+
126
+ from core.container import DependencyContainer
127
+ container = DependencyContainer.get_instance()
128
+
129
+ siwe_auth = container.get_service('siwe_authenticator')
130
+ identity_mapper = container.get_service('identity_mapper')
131
+ db = container.get_service('database_manager')
132
+
133
+ if not siwe_auth or not identity_mapper or not db:
134
+ raise HTTPException(status_code=500, detail="Auth services unavailable")
135
+
136
+ # Verify signature
137
+ is_valid = await siwe_auth.verify_signature(
138
+ wallet_address=request.wallet_address,
139
+ message=request.message,
140
+ signature=request.signature,
141
+ nonce=request.nonce
142
+ )
143
+
144
+ if not is_valid:
145
+ raise HTTPException(status_code=401, detail="Invalid signature")
146
+
147
+ # Get or create user - ALL wallets can sign in (tier set based on token ownership)
148
+ # Feature-level access control enforced elsewhere (task sessions, etc.)
149
+ user_id = await identity_mapper.get_or_create_user(
150
+ wallet_address=request.wallet_address,
151
+ chain=request.chain
152
+ )
153
+
154
+ # Get user details
155
+ user = await db.fetch_one("""
156
+ SELECT role, tier FROM user_profiles WHERE user_id = ?
157
+ """, (user_id,))
158
+
159
+ role = user['role'] if user else 'user'
160
+ tier = user['tier'] if user else 'free'
161
+
162
+ # SECURITY FIX: Do NOT auto-escalate admin role
163
+ # Admin wallets get admin PRIVILEGES during the session but NOT persistent role change
164
+ # This prevents silent role escalation without audit trail
165
+ # To make someone a permanent admin, use the /admin/users/{id}/role endpoint
166
+ is_admin_by_wallet = is_admin_wallet(request.wallet_address)
167
+ if is_admin_by_wallet:
168
+ logger.info(f"Admin wallet detected: {request.wallet_address[:6]}...{request.wallet_address[-4:]} (granting session privileges, not changing role)")
169
+ try:
170
+ from modules.database.audit_log import AuditLogger
171
+ await AuditLogger(db).log_admin_wallet_auth(
172
+ wallet_address=request.wallet_address, user_id=user_id,
173
+ )
174
+ except Exception as audit_exc:
175
+ logger.warning(f"Failed to write admin-wallet audit trail: {audit_exc}")
176
+
177
+ # Create JWT
178
+ jwt_secret = container.config.jwt_secret_key
179
+ if not jwt_secret:
180
+ raise HTTPException(status_code=500, detail="JWT not configured")
181
+
182
+ # SECURITY FIX: Never log JWT secret, even partially
183
+ # Logging even 20 chars significantly reduces brute-force difficulty
184
+
185
+ expires_at = datetime.utcnow() + timedelta(days=7)
186
+
187
+ # Include admin_wallet flag in token for session-based admin privileges
188
+ # This allows admin wallets to have privileges without modifying the role in DB
189
+ token_payload = {
190
+ "sub": request.wallet_address, # WALLET AS PRIMARY! ✅
191
+ "user_id": user_id, # Internal DB ID
192
+ "chain": request.chain,
193
+ "tier": tier,
194
+ "role": role,
195
+ "admin_wallet": is_admin_by_wallet, # Session-based admin flag
196
+ "iat": datetime.utcnow(),
197
+ "exp": expires_at
198
+ }
199
+
200
+ token = jwt.encode(token_payload, jwt_secret, algorithm="HS256")
201
+
202
+ # Log authentication success without sensitive details
203
+ admin_note = " [ADMIN WALLET]" if is_admin_by_wallet else ""
204
+ logger.info(f"✅ Authenticated wallet {request.wallet_address[:6]}...{request.wallet_address[-4:]} (role: {role}, tier: {tier}){admin_note}")
205
+
206
+ # Create response with auth data
207
+ response_data = {
208
+ "token": token,
209
+ "user_id": user_id,
210
+ "wallet_address": request.wallet_address,
211
+ "role": role,
212
+ "tier": tier,
213
+ "is_admin": is_admin_by_wallet or role == 'admin',
214
+ "expires_at": expires_at.isoformat()
215
+ }
216
+
217
+ # Create JSON response
218
+ response = JSONResponse(content=response_data)
219
+
220
+ # SERVER-SIDE COOKIE SETTING (more reliable than client-side)
221
+ # SECURITY: Use secure cookie settings based on environment
222
+ # - secure=True: Cookie only sent over HTTPS (nginx terminates SSL)
223
+ # - httponly=True: Prevents JavaScript access (XSS protection)
224
+ # - samesite="lax": CSRF protection while allowing navigation
225
+ is_production = os.environ.get("ENVIRONMENT", "production") == "production"
226
+ secure = is_production # Only require HTTPS in production
227
+ httponly = True
228
+ samesite = "lax"
229
+ max_age = 7 * 24 * 60 * 60 # 7 days
230
+
231
+ logger.debug(f"🍪 Setting auth cookie: secure={secure}, httponly={httponly}, samesite={samesite}, is_production={is_production}")
232
+
233
+ response.set_cookie(
234
+ key="auth_token",
235
+ value=token,
236
+ max_age=max_age,
237
+ path="/",
238
+ # domain parameter intentionally omitted - defaults to current domain
239
+ secure=secure,
240
+ httponly=httponly,
241
+ samesite=samesite
242
+ )
243
+
244
+ return response
245
+
246
+
247
+ @router.get("/me")
248
+ async def get_current_user(request: Request):
249
+ """
250
+ Get current authenticated user info.
251
+
252
+ Requires Authorization: Bearer <token> header.
253
+ """
254
+
255
+ # user_id is added to request.state by middleware
256
+ if not hasattr(request.state, 'user_id'):
257
+ raise HTTPException(status_code=401, detail="Not authenticated")
258
+
259
+ from core.container import DependencyContainer
260
+ container = DependencyContainer.get_instance()
261
+ tier_manager = container.get_service('tier_manager')
262
+
263
+ if tier_manager:
264
+ user_info = await tier_manager.get_user_info(request.state.user_id)
265
+ return user_info
266
+ else:
267
+ return {"user_id": request.state.user_id}
268
+
269
+
270
+ # =============================================================================
271
+ # API Key Management (Option A: Self-Service for AI Agents)
272
+ # =============================================================================
273
+
274
+ class CreateAPIKeyRequest(BaseModel):
275
+ """Request to create a new API key."""
276
+ name: str = "Default"
277
+ expires_days: int = None # None = never expires
278
+
279
+
280
+ class APIKeyResponse(BaseModel):
281
+ """Response with API key (shown only once!)."""
282
+ api_key: str
283
+ name: str
284
+ prefix: str
285
+ expires_at: str = None
286
+ created_at: str
287
+ warning: str
288
+
289
+
290
+ class APIKeyInfo(BaseModel):
291
+ """API key info (without the actual key)."""
292
+ prefix: str
293
+ name: str
294
+ created_at: str
295
+ last_used: str = None
296
+ expires_at: str = None
297
+ is_active: bool
298
+
299
+
300
+ @router.post("/api-keys", response_model=APIKeyResponse)
301
+ async def create_api_key(request: Request, key_request: CreateAPIKeyRequest):
302
+ """
303
+ Create a new API key for programmatic access.
304
+
305
+ **IMPORTANT**: The full API key is only shown ONCE. Store it securely!
306
+
307
+ Requires:
308
+ - Authenticated user (JWT from wallet login)
309
+ - DEN token ownership (verified via tier)
310
+
311
+ Example:
312
+ POST /api/auth/api-keys
313
+ Authorization: Bearer <your-jwt>
314
+ {
315
+ "name": "My Agent",
316
+ "expires_days": 90
317
+ }
318
+
319
+ Response:
320
+ {
321
+ "api_key": "rob_abc123...", // SAVE THIS!
322
+ "name": "My Agent",
323
+ "prefix": "rob_abc123",
324
+ "expires_at": "2025-03-05T12:00:00",
325
+ "warning": "Store this key securely - it won't be shown again!"
326
+ }
327
+
328
+ Usage:
329
+ curl -H "X-API-KEY: rob_abc123..." https://your-polyrob-host.example/a2a/rpc
330
+ """
331
+ if not hasattr(request.state, 'user_id'):
332
+ raise HTTPException(status_code=401, detail="Authentication required. Login with wallet first.")
333
+
334
+ user_id = request.state.user_id
335
+
336
+ from core.container import DependencyContainer
337
+ container = DependencyContainer.get_instance()
338
+ api_key_manager = container.get_service('api_key_manager')
339
+
340
+ if not api_key_manager:
341
+ raise HTTPException(status_code=503, detail="API key service unavailable")
342
+
343
+ try:
344
+ result = await api_key_manager.generate_api_key(
345
+ user_id=user_id,
346
+ name=key_request.name,
347
+ expires_days=key_request.expires_days
348
+ )
349
+ return APIKeyResponse(**result)
350
+ except ValueError as e:
351
+ raise HTTPException(status_code=403, detail=str(e))
352
+
353
+
354
+ @router.get("/api-keys", response_model=list[APIKeyInfo])
355
+ async def list_api_keys(request: Request):
356
+ """
357
+ List all API keys for the current user.
358
+
359
+ Note: Only shows key prefixes, not full keys.
360
+ """
361
+ if not hasattr(request.state, 'user_id'):
362
+ raise HTTPException(status_code=401, detail="Authentication required")
363
+
364
+ user_id = request.state.user_id
365
+
366
+ from core.container import DependencyContainer
367
+ container = DependencyContainer.get_instance()
368
+ api_key_manager = container.get_service('api_key_manager')
369
+
370
+ if not api_key_manager:
371
+ raise HTTPException(status_code=503, detail="API key service unavailable")
372
+
373
+ keys = await api_key_manager.list_user_keys(user_id)
374
+ return [APIKeyInfo(**k) for k in keys]
375
+
376
+
377
+ @router.delete("/api-keys/{key_prefix}")
378
+ async def revoke_api_key(request: Request, key_prefix: str):
379
+ """
380
+ Revoke an API key.
381
+
382
+ Args:
383
+ key_prefix: The key prefix (e.g., "rob_abc123")
384
+ """
385
+ if not hasattr(request.state, 'user_id'):
386
+ raise HTTPException(status_code=401, detail="Authentication required")
387
+
388
+ user_id = request.state.user_id
389
+
390
+ from core.container import DependencyContainer
391
+ container = DependencyContainer.get_instance()
392
+ api_key_manager = container.get_service('api_key_manager')
393
+
394
+ if not api_key_manager:
395
+ raise HTTPException(status_code=503, detail="API key service unavailable")
396
+
397
+ success = await api_key_manager.revoke_key(user_id, key_prefix)
398
+
399
+ if not success:
400
+ raise HTTPException(status_code=404, detail="API key not found")
401
+
402
+ return {"success": True, "message": f"API key {key_prefix} revoked"}
api/auth_state.py ADDED
@@ -0,0 +1,36 @@
1
+ """The canonical request.state auth contract (C4 SSOT).
2
+
3
+ Every middleware that authenticates a request — AuthenticationMiddleware,
4
+ JWTAuthMiddleware, X402PaymentMiddleware, and the fallback_auth_middleware in
5
+ api/app.py — MUST write these five fields via set_auth_state() instead of
6
+ setting attributes ad hoc. verify_payment_for_request and every
7
+ Depends(get_user_id) / get_user_strict / get_user_permissive consumer in
8
+ api/dependencies.py reads this contract.
9
+
10
+ Workstream B3 (owner-login session, POLYROB Console posture 1) writes the SAME
11
+ contract with role="owner" — this module is what B3 coordinates against.
12
+ """
13
+ from typing import Any, Optional
14
+
15
+
16
+ def set_auth_state(
17
+ state: Any,
18
+ *,
19
+ user_id: Optional[str],
20
+ tier: str = "free",
21
+ role: str = "user",
22
+ payment_method: Optional[str] = None,
23
+ authenticated: bool = True,
24
+ ) -> None:
25
+ """Write the canonical auth-state contract onto `state`.
26
+
27
+ `state` is typically a Starlette `Request.state` (a Starlette `State`
28
+ object) but any object that accepts attribute assignment works — this is
29
+ deliberately duck-typed so it's trivial to unit test with a bare
30
+ `types.SimpleNamespace()`.
31
+ """
32
+ state.user_id = user_id
33
+ state.tier = tier
34
+ state.role = role
35
+ state.payment_method = payment_method
36
+ state.authenticated = authenticated
api/chat_via_task.py ADDED
@@ -0,0 +1,42 @@
1
+ """Canonical handler for the HTTP chat endpoint (/api/chat/message).
2
+
3
+ Chat is served purely by the unified task agent's synchronous ``chat_once`` — the
4
+ legacy ``ChatAgent`` was retired (HANDOFF-C, 2026-06-19). There is no legacy path
5
+ to fall back to, so any failure returns a graceful ``MessageResponse(success=False)``
6
+ rather than ``None``. This module is the single seam between the public HTTP chat
7
+ contract and the one agent core.
8
+ """
9
+ import logging
10
+ from typing import Optional
11
+
12
+ from api.models import MessageResponse
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ _UNAVAILABLE_TEXT = "Chat is temporarily unavailable. Please try again."
17
+ _ERROR_TEXT = "Sorry, something went wrong handling your message."
18
+
19
+
20
+ async def handle_chat_via_task_agent(
21
+ container,
22
+ user_id: str,
23
+ text: str,
24
+ chat_id: Optional[str],
25
+ ) -> MessageResponse:
26
+ """Serve one chat turn from the unified task agent.
27
+
28
+ Always returns a ``MessageResponse``: ``success=True`` with the agent reply,
29
+ or ``success=False`` with a user-safe message when the agent is unavailable
30
+ or ``chat_once`` raises (logged as ``chat.error`` — there is no legacy agent
31
+ to fall back to).
32
+ """
33
+ try:
34
+ task_agent = container.get_agent("task_agent") if container else None
35
+ if not task_agent or not hasattr(task_agent, "chat_once"):
36
+ logger.error("chat.error: task_agent unavailable")
37
+ return MessageResponse(success=False, text=_UNAVAILABLE_TEXT)
38
+ reply = await task_agent.chat_once(user_id=user_id, text=text, chat_id=chat_id)
39
+ return MessageResponse(success=True, text=str(reply or ""))
40
+ except Exception as e:
41
+ logger.error(f"chat.error: {e}", exc_info=True)
42
+ return MessageResponse(success=False, text=_ERROR_TEXT)