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,115 @@
1
+ """One persona resolver for every surface (T1-07, 2026-07-06 structural review).
2
+
3
+ Before this module, two live persona SSOTs were split by surface: the chat_once
4
+ path rendered the default character JSON (via CharacterManager +
5
+ render_persona_block) while the CLI rendered the ``templates.py`` persona for
6
+ ``POLYROB_PERSONA`` — same instance, contradictory voice, and template-only
7
+ guidance (e.g. trading's "Never executes trades") shipped on one surface only.
8
+
9
+ Single precedence, applied identically everywhere:
10
+
11
+ 1. Gate: ``task_personality_block_enabled()`` off -> ``""``.
12
+ 2. Explicit ``POLYROB_PERSONA``: a known template key renders that template's
13
+ persona; any other non-empty value is used as LITERAL free-form persona text.
14
+ 3. The default character, rendered by the pure ``render_persona_block``:
15
+ - async path (:func:`resolve_persona`): the container's ``character_manager``;
16
+ - sync path (:func:`resolve_persona_sync`): the default character JSON read
17
+ directly, mirroring CharacterManager's directory precedence
18
+ (``<data_dir>/characters`` when it holds ``*.character.json``, else the
19
+ package ``agents/personality/characters``).
20
+ 4. ``""`` — byte-identical off-path.
21
+
22
+ Note the pinned SELF-CONTEXT stays authoritative over any persona text
23
+ (<identity>/<source-precedence>, T1-08); persona only styles the voice.
24
+ """
25
+ import json
26
+ import logging
27
+ import os
28
+ from pathlib import Path
29
+ from typing import Optional
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ def _gate_on() -> bool:
35
+ try:
36
+ from agents.task.constants import task_personality_block_enabled
37
+ return task_personality_block_enabled()
38
+ except Exception:
39
+ return False
40
+
41
+
42
+ def _explicit_persona() -> Optional[str]:
43
+ """Tier 2: the operator-chosen POLYROB_PERSONA (template key or literal)."""
44
+ val = (os.environ.get("POLYROB_PERSONA") or "").strip()
45
+ if not val:
46
+ return None
47
+ try:
48
+ from agents.task.templates import TEMPLATES, resolve_template_persona
49
+ if val in TEMPLATES:
50
+ return resolve_template_persona(val)
51
+ except Exception:
52
+ pass
53
+ # A non-template value is LITERAL persona text (free-form), rather than
54
+ # silently degrading to the "general" template.
55
+ return val
56
+
57
+
58
+ def _characters_dir() -> Path:
59
+ """Mirror CharacterManager's precedence without requiring the container."""
60
+ data_dir = (os.environ.get("POLYROB_DATA_DIR") or "data").strip() or "data"
61
+ data_chars = Path(data_dir) / "characters"
62
+ try:
63
+ if data_chars.exists() and list(data_chars.glob("*.character.json")):
64
+ return data_chars
65
+ except Exception:
66
+ pass
67
+ return Path(__file__).parent / "characters"
68
+
69
+
70
+ def _default_character_dict() -> Optional[dict]:
71
+ """Tier 3 (sync): read the default character JSON directly, fail-open."""
72
+ name = (os.environ.get("PERSONALITY_DEFAULT_CHARACTER") or "rob").strip() or "rob"
73
+ char_file = _characters_dir() / f"{name}.character.json"
74
+ try:
75
+ if not char_file.is_file():
76
+ return None
77
+ return json.loads(char_file.read_text(encoding="utf-8"))
78
+ except Exception as e:
79
+ logger.debug(f"default character read skipped: {e}")
80
+ return None
81
+
82
+
83
+ def resolve_persona_sync() -> str:
84
+ """Persona text for surfaces without an initialized container (CLI/REPL)."""
85
+ if not _gate_on():
86
+ return ""
87
+ explicit = _explicit_persona()
88
+ if explicit is not None:
89
+ return explicit
90
+ from agents.personality.persona_render import render_persona_block
91
+ return render_persona_block(_default_character_dict())
92
+
93
+
94
+ async def resolve_persona(container=None) -> str:
95
+ """Persona text for async surfaces (chat front door). Same precedence; the
96
+ character tier prefers the container's CharacterManager (honors config-driven
97
+ default-character selection), falling back to the direct file read."""
98
+ if not _gate_on():
99
+ return ""
100
+ explicit = _explicit_persona()
101
+ if explicit is not None:
102
+ return explicit
103
+ from agents.personality.persona_render import render_persona_block
104
+ char_dict = None
105
+ try:
106
+ cm = container.get_service("character_manager") if container else None
107
+ if cm is not None and hasattr(cm, "get_default_character"):
108
+ character = await cm.get_default_character()
109
+ if character:
110
+ char_dict = character.to_dict() if hasattr(character, "to_dict") else character
111
+ except Exception as e:
112
+ logger.debug(f"character_manager persona resolve skipped: {e}")
113
+ if char_dict is None:
114
+ char_dict = _default_character_dict()
115
+ return render_persona_block(char_dict)
@@ -0,0 +1,58 @@
1
+ """Prompt management and composition system.
2
+
3
+ This module provides a comprehensive prompt management system including:
4
+ - Base prompt management functionality
5
+ - JSON-based prompt storage and retrieval
6
+ - Unified prompt composition for different LLM types
7
+ - System prompt management with character integration
8
+ """
9
+
10
+ from .base_prompt import BasePromptManager
11
+ from .system import SystemPromptManager
12
+ from pathlib import Path
13
+
14
+ # H8: UnifiedPromptComposer + PromptPackage (agents/prompt/prompt_composer.py) were
15
+ # dead code — exported but never instantiated anywhere. Retired in the prompt-stack
16
+ # consolidation. The live prompt path is SystemPromptManager / the task agent's
17
+ # SystemPrompt builder.
18
+
19
+ __all__ = [
20
+ 'BasePromptManager',
21
+ 'SystemPromptManager'
22
+ ]
23
+
24
+ # Version info
25
+ from core.version import __version__ # noqa: F401 (project version SSOT)
26
+
27
+ # Default configuration
28
+ DEFAULT_CONFIG = {
29
+ 'max_history_messages': 5,
30
+ 'max_knowledge_tokens': 1500,
31
+ 'min_relevance_score': 0.6,
32
+ 'default_response_type': 'factual'
33
+ }
34
+
35
+ # Response types supported by the system
36
+ RESPONSE_TYPES = {
37
+ 'factual': 'Precise and factual responses with source citations',
38
+ 'creative': 'Creative responses while maintaining factual accuracy',
39
+ 'analysis': 'Detailed analysis with structured format',
40
+ 'conversational': 'Natural, engaging dialogue while maintaining accuracy'
41
+ }
42
+
43
+ # Agent types supported by the system
44
+ AGENT_TYPES = {
45
+ 'chat_agent': 'Primary conversational agent',
46
+ 'task_agent': 'Task automation agent'
47
+ }
48
+
49
+ # Model types supported by the system
50
+ MODEL_TYPES = {
51
+ 'anthropic': 'Anthropic Claude models',
52
+ 'openai': 'OpenAI GPT models',
53
+ 'default': 'Default text completion models'
54
+ }
55
+
56
+ # Default prompts directory — anchored to the install/repo root (this file is
57
+ # agents/prompt/__init__.py -> parents[2]), NOT the process CWD.
58
+ DEFAULT_PROMPTS_DIR = Path(__file__).resolve().parents[2] / "data" / "prompts"
@@ -0,0 +1,92 @@
1
+ """Base class for prompt management."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any, Dict, Optional, Union
5
+ from agents.personality.character import Character
6
+
7
+
8
+ class BasePromptManager(ABC):
9
+ """Abstract base class for prompt management.
10
+
11
+ This class defines the interface for prompt management systems. All prompt managers
12
+ must implement these methods to ensure consistent behavior across the system.
13
+ """
14
+
15
+ @abstractmethod
16
+ async def initialize(self) -> None:
17
+ """Initialize the prompt manager.
18
+
19
+ This method should be called before any other operations.
20
+ It should handle loading prompts from storage and setting up any required resources.
21
+ """
22
+ pass
23
+
24
+ @abstractmethod
25
+ async def cleanup(self) -> None:
26
+ """Clean up resources.
27
+
28
+ This method should be called when the prompt manager is no longer needed.
29
+ It should handle saving prompts and cleaning up any resources.
30
+ """
31
+ pass
32
+
33
+ @abstractmethod
34
+ async def get_prompt(
35
+ self,
36
+ role: str,
37
+ model_type: Optional[str] = None,
38
+ character: Optional[Character] = None
39
+ ) -> Union[str, Dict[str, Any]]:
40
+ """Get a prompt formatted for the specified model type.
41
+
42
+ Args:
43
+ role: The role/key identifying the prompt to retrieve
44
+ model_type: Optional model type to format the prompt for
45
+ character: Optional character to integrate into the prompt
46
+
47
+ Returns:
48
+ The prompt string or a model-specific prompt structure
49
+ """
50
+ pass
51
+
52
+ @abstractmethod
53
+ async def set_prompt(self, role: str, prompt: str) -> None:
54
+ """Set a prompt for a specific role.
55
+
56
+ Args:
57
+ role: The role/key identifying where to store the prompt
58
+ prompt: The prompt text to store
59
+ """
60
+ pass
61
+
62
+ @abstractmethod
63
+ async def format_for_model(
64
+ self,
65
+ prompt: str,
66
+ model_type: str,
67
+ character: Optional[Character] = None
68
+ ) -> Union[str, Dict[str, Any]]:
69
+ """Format a prompt for a specific model type.
70
+
71
+ Args:
72
+ prompt: The prompt text to format
73
+ model_type: The type of model to format for (e.g., "openai", "anthropic")
74
+ character: Optional character to integrate
75
+
76
+ Returns:
77
+ Formatted prompt structure for the specified model
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ async def integrate_character(self, prompt: str, character: Character) -> str:
83
+ """Integrate character information into a prompt.
84
+
85
+ Args:
86
+ prompt: The base prompt to integrate the character into
87
+ character: The character information to integrate
88
+
89
+ Returns:
90
+ The prompt with character information integrated
91
+ """
92
+ pass
@@ -0,0 +1,417 @@
1
+ """System prompt management with character integration."""
2
+
3
+ import json
4
+ import logging
5
+ import asyncio
6
+ from typing import Optional, Dict, Any, Union, TYPE_CHECKING
7
+ from pathlib import Path
8
+ from core.config import BotConfig
9
+ from core.base_component import BaseComponent
10
+ from agents.personality.character import Character
11
+ from .base_prompt import BasePromptManager
12
+ from utils.message_utils import send_long_message
13
+ from utils.markdown_utils import escape_markdown
14
+
15
+ # aiogram (Telegram SDK) is heavy (~1.3s import) and used ONLY in the Telegram-only
16
+ # display_prompts() path. Keep it out of module load so importing this prompt system
17
+ # (every CLI invocation / server worker boot) does not pull aiogram.
18
+ # See docs/plans/2026-06-26-runtime-architecture-finalization-FUSION.md (P0a).
19
+ if TYPE_CHECKING:
20
+ from aiogram import types
21
+
22
+
23
+ class SystemPromptManager(BaseComponent, BasePromptManager):
24
+ """Manages system prompts with character integration."""
25
+
26
+ def __init__(self, name: str, config: BotConfig, container=None, json_file_path: Optional[str] = None):
27
+ """Initialize the system prompt manager."""
28
+ if not name:
29
+ raise ValueError("Name is required")
30
+ if not config:
31
+ raise ValueError("Config is required")
32
+
33
+ super().__init__(name=name, config=config, container=container)
34
+ self.config = config
35
+
36
+ # Determine the correct file path
37
+ if json_file_path:
38
+ self.json_file_path = Path(json_file_path)
39
+ elif config and hasattr(config, 'data_dir'):
40
+ self.json_file_path = Path(config.data_dir) / "prompts" / "system_prompts.json"
41
+ else:
42
+ self.json_file_path = (
43
+ Path(__file__).resolve().parents[2] / "data" / "prompts" / "system_prompts.json"
44
+ )
45
+
46
+ self._prompts = {}
47
+ self._lock = asyncio.Lock()
48
+
49
+ # Log the file path being used
50
+ self.logger.info(f"Using prompts file path: {self.json_file_path}")
51
+
52
+ # Default prompts as fallback
53
+ self.default_prompts = {
54
+ "chat_agent": """You are an AI assistant focused on helpful and accurate responses.
55
+
56
+ Key Guidelines:
57
+ 1. Provide clear and accurate information
58
+ 2. Be helpful while maintaining appropriate boundaries
59
+ 3. Acknowledge uncertainty when present
60
+ 4. Stay focused on the user's needs
61
+ 5. Maintain a professional and friendly tone"""
62
+ }
63
+
64
+ async def _initialize(self) -> None:
65
+ """Initialize the prompt manager."""
66
+ try:
67
+ # Ensure the prompts directory exists
68
+ self.json_file_path.parent.mkdir(parents=True, exist_ok=True)
69
+
70
+ # Load prompts from JSON if file exists
71
+ if self.json_file_path.exists():
72
+ try:
73
+ self.logger.info(f"Loading prompts from: {self.json_file_path}")
74
+ with self.json_file_path.open('r', encoding='utf-8') as f:
75
+ loaded_prompts = json.load(f)
76
+ # Store loaded prompts
77
+ self._prompts = loaded_prompts
78
+ self.logger.info(f"Loaded {len(self._prompts)} prompts from {self.json_file_path}")
79
+
80
+ # Log loaded roles
81
+ for role in self._prompts:
82
+ self.logger.info(f"Loaded prompt for role: {role}")
83
+ except json.JSONDecodeError as e:
84
+ self.logger.error(f"Error parsing prompts file {self.json_file_path}: {e}")
85
+ self._prompts = {}
86
+ except Exception as e:
87
+ self.logger.error(f"Error loading prompts from {self.json_file_path}: {e}")
88
+ self._prompts = {}
89
+ else:
90
+ self.logger.warning(f"No prompts file found at {self.json_file_path}")
91
+ self._prompts = {}
92
+
93
+ except Exception as e:
94
+ self.logger.error(f"Failed to initialize system prompt manager: {e}")
95
+ raise
96
+
97
+ async def _cleanup(self) -> None:
98
+ """Clean up resources."""
99
+ try:
100
+ # Save any pending changes
101
+ await self._save_prompts()
102
+ self._prompts.clear()
103
+ self.logger.info("System prompt manager cleaned up successfully")
104
+ except Exception as e:
105
+ self.logger.error(f"Error during cleanup: {e}")
106
+ raise
107
+
108
+ def _read_prompts_sync(self) -> Dict[str, str]:
109
+ """Read prompts from JSON file synchronously."""
110
+ try:
111
+ if not self.json_file_path.exists():
112
+ return {}
113
+
114
+ with self.json_file_path.open('r', encoding='utf-8') as f:
115
+ return json.load(f)
116
+ except Exception as e:
117
+ self.logger.error(f"Error reading prompts: {e}")
118
+ return {}
119
+
120
+ async def _save_prompts(self) -> None:
121
+ """Save prompts to JSON file."""
122
+ try:
123
+ async with self._lock:
124
+ with self.json_file_path.open('w', encoding='utf-8') as f:
125
+ json.dump(self._prompts, f, indent=2)
126
+ self.logger.debug(f"Saved prompts to {self.json_file_path}")
127
+ except Exception as e:
128
+ self.logger.error(f"Error saving prompts: {e}")
129
+ raise
130
+
131
+ async def get_prompt(
132
+ self,
133
+ role: str,
134
+ model_type: Optional[str] = None,
135
+ character: Optional[Character] = None,
136
+ metadata: Optional[Dict[str, Any]] = None
137
+ ) -> str:
138
+ """Get system prompt with optional character integration."""
139
+ try:
140
+ if not self._initialized:
141
+ await self.initialize()
142
+
143
+ # First try to get prompt from JSON-loaded prompts
144
+ base_prompt = self._prompts.get(role)
145
+
146
+ # Log prompt source
147
+ if base_prompt:
148
+ self.logger.debug(f"Using JSON prompt for role: {role}")
149
+ else:
150
+ self.logger.warning(f"No JSON prompt found for role {role}, using default")
151
+ base_prompt = self.default_prompts.get(role, "")
152
+
153
+ if not base_prompt:
154
+ self.logger.warning(f"No prompt template found for role: {role}")
155
+ return ""
156
+
157
+ # Start building final prompt
158
+ prompt_parts = []
159
+
160
+ # Add system prompt if available
161
+ if base_prompt:
162
+ prompt_parts.append(base_prompt)
163
+
164
+ # Add character context if available
165
+ if character:
166
+ char_context = self._build_character_context(character)
167
+ if char_context:
168
+ prompt_parts.append(char_context)
169
+ self.logger.debug(f"Added character context for {character.name}")
170
+
171
+ # Add additional context from metadata if available
172
+ if metadata:
173
+ context = self._build_additional_context(metadata)
174
+ if context:
175
+ prompt_parts.append(context)
176
+ self.logger.debug("Added metadata context")
177
+
178
+ # Combine all parts with proper spacing
179
+ final_prompt = "\n\n".join(filter(None, prompt_parts))
180
+
181
+ # Log prompt statistics for troubleshooting
182
+ self.logger.debug(f"Built system prompt for {role}, length: {len(final_prompt)} chars, with character: {character.name if character else 'None'}")
183
+
184
+ return final_prompt
185
+
186
+ except Exception as e:
187
+ self.logger.error(f"Error building system prompt: {e}")
188
+ return self.default_prompts.get(role, "")
189
+
190
+ def _build_character_context(self, character: Character) -> str:
191
+ """Build character context section."""
192
+ try:
193
+ sections = [
194
+ f"CHARACTER PROFILE:",
195
+ f"Name: {character.name}",
196
+ f"Background: {character.bio}"
197
+ ]
198
+
199
+ if character.adjectives:
200
+ sections.append(f"Personality: {', '.join(character.adjectives)}")
201
+
202
+ if character.style.get('speaking'):
203
+ sections.append("Speaking Style:")
204
+ sections.extend(f"- {style}" for style in character.style['speaking'])
205
+
206
+ if character.knowledge:
207
+ sections.append("Knowledge & Expertise:")
208
+ sections.extend(f"- {k}" for k in character.knowledge)
209
+
210
+ if character.topics:
211
+ sections.append("Topics of Interest:")
212
+ sections.extend(f"- {t}" for t in character.topics)
213
+
214
+ return "\n".join(sections)
215
+
216
+ except Exception as e:
217
+ self.logger.error(f"Error building character context: {e}")
218
+ return ""
219
+
220
+ def _build_additional_context(self, metadata: Dict[str, Any]) -> str:
221
+ """Build additional context section from metadata."""
222
+ try:
223
+ sections = []
224
+
225
+ # Add knowledge context if available
226
+ if metadata.get('knowledge_context'):
227
+ sections.append("RELEVANT KNOWLEDGE:")
228
+ sections.append(metadata['knowledge_context'])
229
+
230
+ # Add conversation mode if available
231
+ if metadata.get('conversation_mode'):
232
+ sections.append(f"Conversation Mode: {metadata['conversation_mode']}")
233
+
234
+ # Add any other relevant metadata
235
+ if metadata.get('additional_instructions'):
236
+ sections.append("Additional Instructions:")
237
+ sections.append(metadata['additional_instructions'])
238
+
239
+ return "\n\n".join(sections) if sections else ""
240
+
241
+ except Exception as e:
242
+ self.logger.error(f"Error building additional context: {e}")
243
+ return ""
244
+
245
+ async def set_prompt(self, role: str, prompt: str) -> None:
246
+ """Set a prompt for a specific role."""
247
+ try:
248
+ if not self._initialized:
249
+ await self.initialize()
250
+
251
+ # First, reload any existing prompts from disk to avoid overwriting
252
+ try:
253
+ with self.json_file_path.open('r', encoding='utf-8') as f:
254
+ current_prompts = json.load(f)
255
+ # Update our in-memory prompts with latest from disk
256
+ self._prompts.update(current_prompts)
257
+ self.logger.info(f"Reloaded current prompts before update, found {len(current_prompts)} roles")
258
+ except (FileNotFoundError, json.JSONDecodeError) as e:
259
+ self.logger.warning(f"Could not reload existing prompts: {e}")
260
+
261
+ async with self._lock:
262
+ # Log previous value if available for debugging
263
+ previous = self._prompts.get(role, "")
264
+ self.logger.info(f"Updating prompt for role '{role}', previous length: {len(previous)}, new length: {len(prompt)}")
265
+
266
+ # Update prompt in memory
267
+ self._prompts[role] = prompt.strip()
268
+
269
+ # Save to file, ensuring directory exists
270
+ self.json_file_path.parent.mkdir(parents=True, exist_ok=True)
271
+
272
+ # Log what we're about to save
273
+ self.logger.info(f"Saving {len(self._prompts)} prompts to {self.json_file_path}")
274
+ for r in self._prompts:
275
+ self.logger.debug(f"Saving prompt for role: {r}, length: {len(self._prompts[r])}")
276
+
277
+ # Save to file
278
+ with self.json_file_path.open('w', encoding='utf-8') as f:
279
+ json.dump(self._prompts, f, indent=2, ensure_ascii=False)
280
+ self.logger.info(f"Successfully saved prompt for role: {role}")
281
+
282
+ except Exception as e:
283
+ self.logger.error(f"Error setting prompt for role '{role}': {e}", exc_info=True)
284
+ raise
285
+
286
+ def _format_for_model_sync(
287
+ self,
288
+ prompt: str,
289
+ model_type: str
290
+ ) -> Union[str, Dict[str, Any]]:
291
+ """Format the prompt for specific model types synchronously."""
292
+ try:
293
+ if model_type == "anthropic":
294
+ return {
295
+ "system": prompt,
296
+ "messages": []
297
+ }
298
+ elif model_type == "openai":
299
+ return {
300
+ "messages": [
301
+ {"role": "system", "content": prompt}
302
+ ]
303
+ }
304
+ else:
305
+ return prompt
306
+
307
+ except Exception as e:
308
+ self.logger.error(f"Error formatting prompt for model {model_type}: {e}")
309
+ return prompt
310
+
311
+ def _integrate_character_sync(self, prompt: str, character: Character) -> str:
312
+ """Integrate character information into the prompt synchronously."""
313
+ try:
314
+ # Build character profile section
315
+ char_sections = [
316
+ "\nCHARACTER PROFILE:",
317
+ f"Name: {character.name}",
318
+ f"Background: {character.bio}",
319
+ f"Personality: {', '.join(character.adjectives)}",
320
+ "\nSPEAKING STYLE:",
321
+ *[f"- {style}" for style in character.style.get('speaking', [])],
322
+ "\nKNOWLEDGE & EXPERTISE:",
323
+ *[f"- {k}" for k in character.knowledge],
324
+ "\nTOPICS OF INTEREST:",
325
+ *[f"- {t}" for t in character.topics],
326
+ "\nEXAMPLE MESSAGES:",
327
+ *[f"- {ex}" for ex in character.messageExamples[:3]]
328
+ ]
329
+
330
+ char_profile = "\n".join(char_sections)
331
+
332
+ # Add behavior guidelines
333
+ guidelines = [
334
+ "\nBEHAVIOR GUIDELINES:",
335
+ "1. Maintain this personality consistently",
336
+ "2. Use your expertise and knowledge naturally",
337
+ "3. Express yourself in your unique voice",
338
+ "4. Stay true to your character while being helpful",
339
+ "5. Keep your responses focused and relevant"
340
+ ]
341
+
342
+ guidelines_text = "\n".join(guidelines)
343
+
344
+ # Combine all sections
345
+ return f"{prompt}\n\n{char_profile}\n\n{guidelines_text}"
346
+
347
+ except Exception as e:
348
+ self.logger.error(f"Error integrating character: {e}")
349
+ return prompt
350
+
351
+ # Keep these methods for backward compatibility
352
+ async def format_for_model(
353
+ self,
354
+ prompt: str,
355
+ model_type: str,
356
+ character: Optional[Character] = None
357
+ ) -> Union[str, Dict[str, Any]]:
358
+ """Format the prompt for specific model types."""
359
+ return self._format_for_model_sync(prompt, model_type)
360
+
361
+ async def integrate_character(self, prompt: str, character: Character) -> str:
362
+ """Integrate character information into the prompt."""
363
+ return self._integrate_character_sync(prompt, character)
364
+
365
+ async def format_prompts_display(self) -> str:
366
+ """Format current prompts for display with proper markdown."""
367
+ try:
368
+ if not self._initialized:
369
+ await self.initialize()
370
+
371
+ sections = ["*System Prompts Management*\n\n*Current Prompts:*\n"]
372
+
373
+ # Format main agent prompt
374
+ sections.append("*Main Agent:*")
375
+ main_prompt = self._prompts.get('chat_agent')
376
+ if main_prompt:
377
+ sections.append("```\n" + main_prompt + "\n```")
378
+ else:
379
+ sections.append("_Not set_")
380
+
381
+ # Format simulator agent prompt
382
+ sections.append("\n*Simulator Agent:*")
383
+ sim_prompt = self._prompts.get('simulator_agent')
384
+ if sim_prompt:
385
+ sections.append("```\n" + sim_prompt + "\n```")
386
+ else:
387
+ sections.append("_Not set_")
388
+
389
+ # Combine all sections with proper escaping
390
+ formatted_text = "\n".join(sections)
391
+ return escape_markdown(formatted_text)
392
+
393
+ except Exception as e:
394
+ self.logger.error(f"Error formatting prompts display: {e}")
395
+ return "❌ Error formatting prompts display"
396
+
397
+ async def display_prompts(self, message: "types.Message") -> None:
398
+ """Display current prompts using message utils for proper formatting."""
399
+ from aiogram import types
400
+ from aiogram.enums import ParseMode
401
+ try:
402
+ formatted_text = await self.format_prompts_display()
403
+
404
+ # Use send_long_message to handle long prompts
405
+ await send_long_message(
406
+ message=message,
407
+ text=formatted_text,
408
+ parse_mode=ParseMode.MARKDOWN_V2,
409
+ link_preview_options=types.LinkPreviewOptions(is_disabled=True)
410
+ )
411
+
412
+ except Exception as e:
413
+ self.logger.error(f"Error displaying prompts: {e}")
414
+ await message.answer(
415
+ "❌ Error displaying prompts",
416
+ link_preview_options=types.LinkPreviewOptions(is_disabled=True)
417
+ )