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,644 @@
1
+ import aiosqlite
2
+ import asyncio
3
+ import logging
4
+ from typing import Optional, List, Any, Dict, Union, Tuple
5
+ import json
6
+ from datetime import datetime
7
+ import sqlite3
8
+ import os
9
+ from pathlib import Path
10
+
11
+ from core.exceptions import DatabaseError
12
+
13
+ class DatabaseConnection:
14
+ """Manages SQLite database connection."""
15
+
16
+ def __init__(self, db_path: Path, logger: Optional[logging.Logger] = None):
17
+ """Initialize database connection.
18
+
19
+ Args:
20
+ db_path: Path to SQLite database file
21
+ logger: Optional logger instance
22
+ """
23
+ self.db_path = db_path
24
+ self.logger = logger or logging.getLogger(__name__)
25
+ self.connection: Optional[sqlite3.Connection] = None
26
+ self._lock = asyncio.Lock()
27
+ self._transaction_lock = asyncio.Lock()
28
+ self._in_transaction = False
29
+
30
+ async def connect(self) -> None:
31
+ """Establish database connection."""
32
+ try:
33
+ self.connection = sqlite3.connect(
34
+ self.db_path,
35
+ check_same_thread=False
36
+ )
37
+ self.connection.row_factory = sqlite3.Row
38
+
39
+ # SECURITY FIX: Enable foreign key constraints for data integrity
40
+ # FK constraints ensure referential integrity and prevent orphaned records
41
+ # Schema uses ON DELETE CASCADE to handle user deletions properly
42
+ self.connection.execute("PRAGMA foreign_keys = ON")
43
+
44
+ # Clean up orphaned records that might have accumulated when FK was disabled
45
+ await self._cleanup_orphaned_records()
46
+
47
+ self.logger.debug(f"Connected to database at {self.db_path} (FK: ON)")
48
+
49
+ except Exception as e:
50
+ self.logger.error(f"Failed to connect to database: {e}")
51
+ raise DatabaseError(f"Failed to connect to database: {e}")
52
+
53
+ async def close(self) -> None:
54
+ """Close database connection."""
55
+ if self.connection:
56
+ self.connection.close()
57
+ self.connection = None
58
+ self.logger.debug("Database connection closed")
59
+
60
+ async def _cleanup_orphaned_records(self) -> None:
61
+ """Clean up orphaned records that accumulated while FK constraints were disabled.
62
+
63
+ This runs once at connection time to fix any data integrity issues
64
+ before FK constraints are enforced.
65
+ """
66
+ try:
67
+ # Temporarily disable FK for cleanup (we're fixing old data)
68
+ self.connection.execute("PRAGMA foreign_keys = OFF")
69
+
70
+ # Tables with FK to user_profiles(user_id)
71
+ orphan_tables = [
72
+ 'api_keys',
73
+ 'user_credits',
74
+ 'credit_transactions',
75
+ 'usage_records',
76
+ 'user_deposit_addresses',
77
+ 'crypto_payments',
78
+ 'pending_sweeps',
79
+ 'wallet_history',
80
+ 'blocked_users',
81
+ 'conversation_contexts',
82
+ ]
83
+
84
+ total_cleaned = 0
85
+ for table in orphan_tables:
86
+ try:
87
+ # Check if table exists before cleaning
88
+ cursor = self.connection.execute(
89
+ "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
90
+ (table,)
91
+ )
92
+ if cursor.fetchone() is None:
93
+ continue
94
+
95
+ # Delete orphaned records
96
+ cursor = self.connection.execute(f"""
97
+ DELETE FROM {table}
98
+ WHERE user_id IS NOT NULL
99
+ AND user_id NOT IN (SELECT user_id FROM user_profiles)
100
+ """)
101
+ if cursor.rowcount > 0:
102
+ total_cleaned += cursor.rowcount
103
+ self.logger.info(f"Cleaned {cursor.rowcount} orphaned records from {table}")
104
+ except Exception as e:
105
+ # Log but continue - some tables may not exist yet
106
+ self.logger.debug(f"Could not clean {table}: {e}")
107
+
108
+ # Clean up expired nonces
109
+ try:
110
+ cursor = self.connection.execute("""
111
+ DELETE FROM auth_nonces WHERE expires_at < datetime('now')
112
+ """)
113
+ if cursor.rowcount > 0:
114
+ self.logger.info(f"Cleaned {cursor.rowcount} expired nonces")
115
+ except Exception:
116
+ pass # Table may not exist
117
+
118
+ self.connection.commit()
119
+
120
+ # Re-enable FK constraints
121
+ self.connection.execute("PRAGMA foreign_keys = ON")
122
+
123
+ if total_cleaned > 0:
124
+ self.logger.info(f"✅ Cleaned up {total_cleaned} total orphaned records")
125
+
126
+ except Exception as e:
127
+ self.logger.warning(f"Error during orphan cleanup: {e}")
128
+ # Re-enable FK even if cleanup failed
129
+ self.connection.execute("PRAGMA foreign_keys = ON")
130
+
131
+ async def _initialize_base_schema(self) -> None:
132
+ """Initialize base tables schema."""
133
+ try:
134
+ # Create base tables first
135
+ base_tables = """
136
+ CREATE TABLE IF NOT EXISTS schema_version (
137
+ version INTEGER PRIMARY KEY,
138
+ applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
139
+ );
140
+
141
+ CREATE TABLE IF NOT EXISTS user_profiles (
142
+ user_id TEXT PRIMARY KEY,
143
+ is_bot BOOLEAN,
144
+ first_name TEXT,
145
+ last_name TEXT,
146
+ username TEXT,
147
+ language_code TEXT,
148
+ role TEXT DEFAULT 'user',
149
+ preferences TEXT,
150
+ wallet_address TEXT,
151
+ den_password TEXT,
152
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
153
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
154
+ );
155
+
156
+ CREATE TABLE IF NOT EXISTS conversation_contexts (
157
+ conversation_id TEXT PRIMARY KEY,
158
+ type TEXT NOT NULL,
159
+ user_id TEXT,
160
+ chat_id TEXT NOT NULL,
161
+ chat_name TEXT,
162
+ messages TEXT NOT NULL,
163
+ metadata TEXT,
164
+ last_interaction TIMESTAMP,
165
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
166
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
167
+ FOREIGN KEY (user_id) REFERENCES user_profiles(user_id)
168
+ );
169
+ """
170
+
171
+ # Execute base tables creation
172
+ statements = [stmt.strip() for stmt in base_tables.split(';') if stmt.strip()]
173
+ for statement in statements:
174
+ if statement:
175
+ self.connection.execute(statement)
176
+ self.connection.commit()
177
+
178
+ # Add role column if it doesn't exist
179
+ try:
180
+ self.connection.execute("""
181
+ ALTER TABLE user_profiles ADD COLUMN role TEXT DEFAULT 'user'
182
+ """)
183
+ self.connection.commit()
184
+ except Exception as e:
185
+ # Column might already exist, which is fine
186
+ if 'duplicate column name' not in str(e).lower():
187
+ raise
188
+
189
+ except Exception as e:
190
+ self.logger.error(f"Error initializing base schema: {e}")
191
+ raise
192
+
193
+ async def _ensure_required_columns(self) -> None:
194
+ """Ensure all required columns exist in tables."""
195
+ try:
196
+ # Get current columns in conversation_contexts
197
+ cursor = self.connection.execute("PRAGMA table_info(conversation_contexts)")
198
+ columns = {col[1] for col in cursor.fetchall()}
199
+
200
+ # Add missing columns
201
+ required_columns = [
202
+ ('mode', 'TEXT DEFAULT "active"'),
203
+ ('mode_metadata', 'TEXT'),
204
+ ('keywords', 'TEXT')
205
+ ]
206
+
207
+ for column, definition in required_columns:
208
+ if column not in columns:
209
+ try:
210
+ self.connection.execute(
211
+ f"ALTER TABLE conversation_contexts ADD COLUMN {column} {definition}"
212
+ )
213
+ self.logger.debug(f"Added column {column} to conversation_contexts")
214
+ except Exception as e:
215
+ if "duplicate column name" not in str(e).lower():
216
+ raise
217
+
218
+ self.connection.commit()
219
+
220
+ except Exception as e:
221
+ self.logger.error(f"Error ensuring required columns: {e}")
222
+ raise
223
+
224
+ async def _initialize_remaining_schema(self) -> None:
225
+ """Initialize remaining schema after base tables and columns are set up."""
226
+ try:
227
+ schema_path = Path(__file__).parent / 'schema.sql'
228
+ with open(schema_path, 'r') as f:
229
+ schema = f.read()
230
+
231
+ # Execute remaining schema statements
232
+ statements = [stmt.strip() for stmt in schema.split(';') if stmt.strip()]
233
+ for statement in statements:
234
+ if statement and (
235
+ statement.upper().startswith('CREATE TABLE') or
236
+ statement.upper().startswith('CREATE INDEX')
237
+ ):
238
+ try:
239
+ self.connection.execute(statement)
240
+ except Exception as e:
241
+ if "already exists" not in str(e):
242
+ raise
243
+
244
+ self.connection.commit()
245
+ self.logger.info("Remaining schema initialized successfully")
246
+
247
+ except Exception as e:
248
+ self.logger.error(f"Error initializing remaining schema: {e}")
249
+ raise
250
+
251
+ def _split_sql_statements(self, sql: str) -> List[str]:
252
+ """Carefully split SQL statements preserving triggers and functions."""
253
+ if not sql or not isinstance(sql, str):
254
+ self.logger.error("❌ Invalid SQL input for splitting")
255
+ raise ValueError("Invalid SQL input")
256
+
257
+ statements = []
258
+ current_statement = []
259
+ in_string = False
260
+ string_char = None
261
+
262
+ try:
263
+ # Add check for None before processing
264
+ if sql is None:
265
+ return []
266
+
267
+ for line in sql.splitlines():
268
+ line = line.strip()
269
+
270
+ # Skip empty lines
271
+ if not line:
272
+ continue
273
+
274
+ # Handle comments
275
+ if line.startswith('--'):
276
+ continue
277
+
278
+ # Remove inline comments while preserving strings
279
+ cleaned_line = ''
280
+ i = 0
281
+ while i < len(line):
282
+ if line[i:i+2] == '--' and not in_string:
283
+ break
284
+ elif line[i] in ["'", '"'] and (i == 0 or line[i-1] != '\\'):
285
+ if not in_string:
286
+ in_string = True
287
+ string_char = line[i]
288
+ elif line[i] == string_char:
289
+ in_string = False
290
+ cleaned_line += line[i]
291
+ i += 1
292
+
293
+ if cleaned_line:
294
+ current_statement.append(cleaned_line)
295
+
296
+ # Check if statement is complete
297
+ if cleaned_line.rstrip().endswith(';') and not in_string:
298
+ full_statement = ' '.join(current_statement).strip()
299
+ if full_statement:
300
+ statements.append(full_statement)
301
+ current_statement = []
302
+
303
+ # Handle any remaining statement
304
+ if current_statement:
305
+ full_statement = ' '.join(current_statement).strip()
306
+ if full_statement:
307
+ if not full_statement.endswith(';'):
308
+ full_statement += ';'
309
+ statements.append(full_statement)
310
+
311
+ self.logger.debug(f"Successfully split SQL into {len(statements)} statements")
312
+ return statements
313
+
314
+ except Exception as e:
315
+ self.logger.error(f"SQL splitting error: {str(e)}")
316
+ return []
317
+
318
+ async def begin_transaction(self) -> None:
319
+ """Begin a new transaction.
320
+
321
+ SECURITY FIX: Set _in_transaction flag BEFORE calling execute() to prevent
322
+ race condition where another coroutine could auto-commit during the window
323
+ between execute() returning and flag being set.
324
+ """
325
+ async with self._transaction_lock:
326
+ if self._in_transaction:
327
+ raise DatabaseError("Transaction already in progress")
328
+ # Set flag BEFORE execute to close race window
329
+ self._in_transaction = True
330
+ try:
331
+ await self.execute("BEGIN TRANSACTION")
332
+ except Exception:
333
+ # Rollback state on failure
334
+ self._in_transaction = False
335
+ raise
336
+
337
+ async def commit(self) -> None:
338
+ """Commit the current transaction."""
339
+ async with self._transaction_lock:
340
+ if not self._in_transaction:
341
+ raise DatabaseError("No transaction in progress")
342
+ try:
343
+ await self.execute("COMMIT")
344
+ finally:
345
+ # Always clear flag, even on error (transaction is no longer active)
346
+ self._in_transaction = False
347
+
348
+ async def rollback(self) -> None:
349
+ """Rollback the current transaction."""
350
+ async with self._transaction_lock:
351
+ if not self._in_transaction:
352
+ return # Silently ignore if no transaction
353
+ try:
354
+ await self.execute("ROLLBACK")
355
+ finally:
356
+ # Always clear flag, even on error
357
+ self._in_transaction = False
358
+
359
+ async def in_transaction(self) -> bool:
360
+ """Check if currently in a transaction."""
361
+ return self._in_transaction
362
+
363
+ async def execute(self, query: str, params: Union[tuple, Dict[str, Any]] = ()) -> aiosqlite.Cursor:
364
+ """Execute SQL query."""
365
+ try:
366
+ async with self._lock:
367
+ if not self.connection:
368
+ await self.connect()
369
+
370
+ # Debug logging
371
+ self.logger.debug(f"Execute query: {query}")
372
+ self.logger.debug(f"Params before processing: {params}")
373
+
374
+ # Handle JSON serialization in parameters
375
+ if isinstance(params, dict):
376
+ processed_params = {k: json.dumps(v) if isinstance(v, (dict, list)) else v
377
+ for k, v in params.items()}
378
+ else:
379
+ processed_params = tuple(json.dumps(p) if isinstance(p, (dict, list)) else p
380
+ for p in params)
381
+
382
+ # Debug logging
383
+ self.logger.debug(f"Processed params: {processed_params}")
384
+
385
+ cursor = self.connection.execute(query, processed_params)
386
+
387
+ # Only auto-commit if NOT in an explicit transaction AND not a transaction control statement
388
+ query_upper = query.strip().upper()
389
+ is_transaction_control = query_upper.startswith(('BEGIN', 'COMMIT', 'ROLLBACK'))
390
+ if not self._in_transaction and not is_transaction_control:
391
+ self.connection.commit()
392
+
393
+ return cursor
394
+ except Exception as e:
395
+ self.logger.error(f"Execute error: {e} | Query: {query} | Params: {params}")
396
+ self.logger.exception("Detailed error:")
397
+ raise
398
+
399
+ async def fetch_one(self, query: str, params: Union[tuple, Dict[str, Any]] = ()) -> Optional[Dict[str, Any]]:
400
+ """Fetch a single row from the database."""
401
+ try:
402
+ async with self._lock:
403
+ if not self.connection:
404
+ await self.connect()
405
+
406
+ # Debug logging
407
+ self.logger.debug(f"Fetch one query: {query}")
408
+ self.logger.debug(f"Params before processing: {params}")
409
+
410
+ # Handle JSON serialization in parameters
411
+ if isinstance(params, dict):
412
+ processed_params = {k: json.dumps(v) if isinstance(v, (dict, list)) else v
413
+ for k, v in params.items()}
414
+ else:
415
+ processed_params = tuple(json.dumps(p) if isinstance(p, (dict, list)) else p
416
+ for p in params)
417
+
418
+ # Debug logging
419
+ self.logger.debug(f"Processed params: {processed_params}")
420
+
421
+ cursor = self.connection.execute(query, processed_params)
422
+ row = cursor.fetchone()
423
+ cursor.close()
424
+
425
+ if row:
426
+ columns = [description[0] for description in cursor.description]
427
+ # Handle potential JSON fields
428
+ result = {}
429
+ for col, val in zip(columns, row):
430
+ try:
431
+ if val is not None and isinstance(val, str) and col in ('preferences', 'metadata', 'messages', 'mode_metadata', 'verification_status'):
432
+ try:
433
+ result[col] = json.loads(val)
434
+ except json.JSONDecodeError:
435
+ result[col] = val
436
+ else:
437
+ # For NULL JSON fields, use empty dict/list based on field name
438
+ if val is None and col in ('preferences', 'metadata', 'messages', 'mode_metadata', 'verification_status'):
439
+ if col == 'messages':
440
+ result[col] = []
441
+ elif col == 'verification_status':
442
+ result[col] = {"email": False, "phone": False}
443
+ else:
444
+ result[col] = {}
445
+ else:
446
+ result[col] = val
447
+ except Exception as e:
448
+ self.logger.error(f"Error processing column {col} with value {val}: {e}")
449
+ result[col] = val
450
+ return result
451
+ return None
452
+ except Exception as e:
453
+ self.logger.error(f"Fetch one error: {e} | Query: {query} | Params: {params}")
454
+ self.logger.exception("Detailed error:")
455
+ raise
456
+
457
+ async def fetch_all(self, query: str, params: Union[tuple, Dict[str, Any]] = ()) -> List[Dict[str, Any]]:
458
+ """Fetch all rows and return as list of dictionaries."""
459
+ try:
460
+ async with self._lock:
461
+ if not self.connection:
462
+ await self.connect()
463
+
464
+ # Debug logging
465
+ self.logger.debug(f"Fetch all query: {query}")
466
+ self.logger.debug(f"Params before processing: {params}")
467
+
468
+ # Handle JSON serialization in parameters
469
+ if isinstance(params, dict):
470
+ processed_params = {k: json.dumps(v) if isinstance(v, (dict, list)) else v
471
+ for k, v in params.items()}
472
+ else:
473
+ processed_params = tuple(json.dumps(p) if isinstance(p, (dict, list)) else p
474
+ for p in params)
475
+
476
+ # Debug logging
477
+ self.logger.debug(f"Processed params: {processed_params}")
478
+
479
+ cursor = self.connection.execute(query, processed_params)
480
+ rows = cursor.fetchall()
481
+ columns = [description[0] for description in cursor.description]
482
+ cursor.close()
483
+
484
+ # Debug logging
485
+ self.logger.debug(f"Raw rows: {rows}")
486
+ self.logger.debug(f"Columns: {columns}")
487
+
488
+ # Convert rows to dicts and handle JSON fields
489
+ result = []
490
+ for row in rows:
491
+ row_dict = {}
492
+ for col, val in zip(columns, row):
493
+ try:
494
+ # Handle potential JSON fields
495
+ if val is not None and isinstance(val, str) and col in ('preferences', 'metadata', 'messages', 'mode_metadata', 'verification_status'):
496
+ try:
497
+ row_dict[col] = json.loads(val)
498
+ except json.JSONDecodeError:
499
+ row_dict[col] = val
500
+ else:
501
+ # For NULL JSON fields, use empty dict/list based on field name
502
+ if val is None and col in ('preferences', 'metadata', 'messages', 'mode_metadata', 'verification_status'):
503
+ if col == 'messages':
504
+ row_dict[col] = []
505
+ elif col == 'verification_status':
506
+ row_dict[col] = {"email": False, "phone": False}
507
+ else:
508
+ row_dict[col] = {}
509
+ else:
510
+ row_dict[col] = val
511
+ except Exception as e:
512
+ self.logger.error(f"Error processing column {col} with value {val}: {e}")
513
+ row_dict[col] = val
514
+ result.append(row_dict)
515
+
516
+ # Debug logging
517
+ self.logger.debug(f"Processed result: {result}")
518
+
519
+ return result
520
+ except Exception as e:
521
+ self.logger.error(f"Fetch all error: {e} | Query: {query} | Params: {params}")
522
+ self.logger.exception("Detailed error:")
523
+ return []
524
+
525
+ async def executemany(self, query: str, param_list: List[Union[tuple, Dict[str, Any]]]) -> None:
526
+ """Execute multiple queries with parameter lists."""
527
+ try:
528
+ async with self._lock:
529
+ if not self.connection:
530
+ await self.connect()
531
+
532
+ # Handle JSON serialization in parameters
533
+ processed_params = []
534
+ for params in param_list:
535
+ if isinstance(params, dict):
536
+ processed = {k: json.dumps(v) if isinstance(v, (dict, list)) else v
537
+ for k, v in params.items()}
538
+ else:
539
+ processed = tuple(json.dumps(p) if isinstance(p, (dict, list)) else p
540
+ for p in params)
541
+ processed_params.append(processed)
542
+
543
+ self.connection.executemany(query, processed_params)
544
+ self.connection.commit()
545
+ self.logger.debug(f"Executed multiple queries: {query}")
546
+ except Exception as e:
547
+ self.logger.error(f"Executemany error: {e} | Query: {query}")
548
+ raise
549
+
550
+ async def get_table_names(self) -> List[str]:
551
+ """Get all table names from the database."""
552
+ query = "SELECT name FROM sqlite_master WHERE type='table';"
553
+ rows = await self.fetch_all(query)
554
+ return [row['name'] for row in rows]
555
+
556
+ async def __aenter__(self):
557
+ """Async context manager entry."""
558
+ if not self.connection:
559
+ await self.connect()
560
+ return self
561
+
562
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
563
+ """Async context manager exit."""
564
+ if exc_type:
565
+ self.connection.rollback()
566
+ else:
567
+ self.connection.commit()
568
+ await self.close()
569
+
570
+ async def transaction(self, statements: List[Tuple[str, tuple]]) -> None:
571
+ """Execute multiple statements in a transaction.
572
+
573
+ Args:
574
+ statements: List of (query, params) tuples to execute
575
+ """
576
+ try:
577
+ async with self._transaction_lock:
578
+ if not self.connection:
579
+ await self.connect()
580
+
581
+ # Start transaction
582
+ self.connection.execute("BEGIN TRANSACTION")
583
+
584
+ try:
585
+ # Execute all statements
586
+ for query, params in statements:
587
+ self.connection.execute(query, params)
588
+
589
+ # Commit if all successful
590
+ self.connection.commit()
591
+
592
+ except Exception as e:
593
+ # Rollback on any error
594
+ self.connection.rollback()
595
+ self.logger.error(f"Transaction failed, rolling back: {e}")
596
+ raise DatabaseError(f"Transaction failed: {e}")
597
+
598
+ except Exception as e:
599
+ self.logger.error(f"Error executing transaction: {e}")
600
+ raise DatabaseError(f"Failed to execute transaction: {e}")
601
+
602
+ def _auto_db_path() -> str:
603
+ """Path to auto.db, anchored to the install/repo root (not the process CWD)."""
604
+ from pathlib import Path
605
+ return str(Path(__file__).resolve().parents[2] / "data" / "auto.db")
606
+
607
+
608
+ def get_db_connection():
609
+ """Get database connection."""
610
+ return sqlite3.connect(_auto_db_path())
611
+
612
+ def init_auto_tables():
613
+ """Initialize auto-related tables."""
614
+ conn = get_db_connection()
615
+ cursor = conn.cursor()
616
+
617
+ # Create tables from schema
618
+ cursor.executescript('''
619
+ CREATE TABLE IF NOT EXISTS auto_knowledge (
620
+ id TEXT PRIMARY KEY,
621
+ topic TEXT NOT NULL,
622
+ content TEXT NOT NULL,
623
+ created_at TIMESTAMP,
624
+ updated_at TIMESTAMP
625
+ );
626
+
627
+ CREATE TABLE IF NOT EXISTS auto_interactions (
628
+ id TEXT PRIMARY KEY,
629
+ action TEXT NOT NULL,
630
+ result TEXT NOT NULL,
631
+ created_at TIMESTAMP
632
+ );
633
+
634
+ CREATE TABLE IF NOT EXISTS auto_cycles (
635
+ id TEXT PRIMARY KEY,
636
+ started_at TIMESTAMP,
637
+ ended_at TIMESTAMP,
638
+ status TEXT,
639
+ metrics JSON
640
+ );
641
+ ''')
642
+
643
+ conn.commit()
644
+ conn.close()