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
agents/base_agent.py ADDED
@@ -0,0 +1,743 @@
1
+ """Base agent class for all bot agents."""
2
+
3
+ import logging
4
+ import asyncio
5
+ from typing import List, Optional, Dict, Any, Union, TYPE_CHECKING, Awaitable
6
+ from abc import abstractmethod
7
+
8
+ from core.base_component import BaseComponent
9
+ from core.config import BotConfig
10
+ from core.container import DependencyContainer
11
+ from core.exceptions import AgentError
12
+ from core.version import get_version
13
+ from modules.memory.models import ConversationContext, Message
14
+ from modules.llm.llm_client import LLMClient
15
+
16
+ if TYPE_CHECKING:
17
+ from agents.personality.character import Character
18
+ from modules.memory.memory_manager import MemoryManager
19
+ # Already imported above, just keeping for type checking
20
+ # from modules.llm.llm_client import LLMClient
21
+
22
+ class BaseAgent(BaseComponent):
23
+ """Base class for all agents."""
24
+
25
+ def __init__(self, *, config: BotConfig, container: DependencyContainer, name: str):
26
+ """Initialize base agent."""
27
+ super().__init__(name=name, config=config, container=container)
28
+
29
+ # Initialize core components - all will be properly set in _initialize()
30
+ self.llm_client = None
31
+ self.knowledge_base = None
32
+ self.character_manager = None
33
+ self.active_character = None
34
+ self.memory_manager = None
35
+ self.system_prompt_manager = None
36
+
37
+ # Initialize state
38
+ self._initialized = False
39
+ self._enabled = True
40
+ self._last_result = None
41
+ self._init_lock = asyncio.Lock()
42
+ self.llm_available = False
43
+
44
+ # LLM config with character settings if available
45
+ self.llm_config = {
46
+ 'model_type': 'claude-sonnet-4-5',
47
+ 'temperature': 0.7,
48
+ 'max_tokens': 4096,
49
+ 'top_p': 0.9,
50
+ 'top_k': 50
51
+ }
52
+
53
+ # Update with character settings if available
54
+ if self.active_character and hasattr(self.active_character, 'settings'):
55
+ self.llm_config.update(self.active_character.settings)
56
+
57
+ def _get_default_character(self):
58
+ """Get default character configuration."""
59
+ return {
60
+ 'name': 'Assistant',
61
+ 'personality': 'Helpful and professional',
62
+ 'background': 'An AI assistant designed to help users with various tasks',
63
+ 'traits': ['helpful', 'knowledgeable', 'professional']
64
+ }
65
+
66
+ async def _initialize(self) -> None:
67
+ """Initialize agent base components."""
68
+ try:
69
+ # Initialize LLM client
70
+ if self.container:
71
+ # Try getting preferred LLM client first, falling back to any available client
72
+ self.llm_client = self._get_preferred_llm_client()
73
+ if not self.llm_client:
74
+ self.logger.warning("Preferred LLM client not available, will try to use any available client")
75
+ self.llm_client = self.container.get_service('llm')
76
+ if not self.llm_client:
77
+ self.logger.warning("Primary LLM client not available, will try alternative clients")
78
+ # Try specific clients in order of preference
79
+ for client_name in ['anthropic_client', 'openai_client']:
80
+ if self.container.has_service(client_name):
81
+ self.llm_client = self.container.get_service(client_name)
82
+ self.logger.info(f"Using {client_name} as fallback")
83
+ break
84
+
85
+ if not self.llm_client:
86
+ self.llm_available = False
87
+ self.logger.error("No LLM client available - agent functionality will be limited")
88
+ else:
89
+ self.llm_available = True
90
+
91
+ # Get optional services properly, only if they exist
92
+ if self.container.has_service('memory_manager'):
93
+ self.memory_manager = self.container.get_service('memory_manager')
94
+
95
+ if self.container.has_service('system_prompt_manager'):
96
+ self.system_prompt_manager = self.container.get_service('system_prompt_manager')
97
+
98
+ if self.container.has_service('knowledge_base'):
99
+ self.knowledge_base = self.container.get_service('knowledge_base')
100
+
101
+ # Get character manager and role-specific character
102
+ if self.container.has_service('character_manager'):
103
+ self.character_manager = self.container.get_service('character_manager')
104
+ try:
105
+ self.active_character = await self.character_manager.get_character_for_role(self.name)
106
+ if self.active_character:
107
+ self.logger.debug(f"Using character: {self.active_character.name}")
108
+ else:
109
+ self.logger.debug("No specific character assigned, using default settings")
110
+ except Exception as e:
111
+ self.logger.debug(f"Could not load character: {e}, using default settings")
112
+
113
+ self.logger.info(f"{self.name} agent base components initialized")
114
+ else:
115
+ raise AgentError("Container is required for agent initialization")
116
+
117
+ except Exception as e:
118
+ self.logger.error(f"Error initializing agent base components: {e}")
119
+ raise
120
+
121
+ def _get_preferred_llm_client(self) -> Optional[LLMClient]:
122
+ """Get preferred LLM client based on agent configuration."""
123
+ if not self.container:
124
+ return None
125
+
126
+ # Get preferred client name from config
127
+ preferred_client = self.config.get(self.name, {}).get('llm_client')
128
+ if not preferred_client:
129
+ # Default preference - use GPT-4.1 for task agent
130
+ if self.name == 'task_agent':
131
+ preferred_client = 'openai_client'
132
+ else:
133
+ preferred_client = 'anthropic_client'
134
+
135
+ # Try to get the preferred client
136
+ client = self.container.get_service(preferred_client)
137
+ if client:
138
+ self.logger.info(f"Using preferred LLM client: {preferred_client}")
139
+ return client
140
+
141
+ # If preferred client is not available, use any client
142
+ return None
143
+
144
+ async def _cleanup(self) -> None:
145
+ """Clean up agent resources."""
146
+ try:
147
+ if self.llm_client:
148
+ await self.llm_client.cleanup()
149
+ self.logger.info(f"{self.name} agent cleaned up")
150
+ except Exception as e:
151
+ self.logger.error(f"Error cleaning up {self.name} agent: {e}")
152
+
153
+ @property
154
+ def required_services(self) -> Dict[str, str]:
155
+ """Get required services."""
156
+ return {
157
+ 'llm_client': 'LLM service for text generation',
158
+ 'memory_manager': 'Memory management service'
159
+ }
160
+
161
+ @property
162
+ def is_enabled(self) -> bool:
163
+ """Check if agent is enabled."""
164
+ return self._initialized and self.llm_available
165
+
166
+ async def initialize(self) -> None:
167
+ """Initialize agent and its components."""
168
+ try:
169
+ await self._initialize()
170
+ if self.active_character:
171
+ self.logger.info(f"{self.name} agent initialized with character {self.active_character.name}")
172
+ else:
173
+ self.logger.info(f"{self.name} agent initialized")
174
+ except Exception as e:
175
+ self.logger.error(f"Failed to initialize {self.name} agent: {str(e)}")
176
+ raise AgentError(f"Failed to initialize agent: {str(e)}")
177
+
178
+ async def set_character(self, character: 'Character') -> None:
179
+ """Set or update the agent's character."""
180
+ try:
181
+ self.active_character = character
182
+ if self.llm_client:
183
+ self.llm_client.active_character = character
184
+ if self.system_prompt_manager:
185
+ await self.system_prompt_manager.set_prompt(
186
+ self.name,
187
+ self.system_prompt_manager.get_prompt(self.name, character=character)
188
+ )
189
+ self.logger.info(f"Updated {self.name} agent character to {character.name}")
190
+ except Exception as e:
191
+ self.logger.error(f"Error setting character: {e}")
192
+ raise AgentError(f"Failed to set character: {str(e)}")
193
+
194
+ async def get_character_info(self) -> Optional[Dict[str, Any]]:
195
+ """Get information about the current character."""
196
+ if not self.active_character:
197
+ return None
198
+
199
+ return {
200
+ "name": self.active_character.name,
201
+ "bio": self.active_character.bio,
202
+ "style": self.active_character.style,
203
+ "knowledge": self.active_character.knowledge,
204
+ "topics": self.active_character.topics,
205
+ "adjectives": self.active_character.adjectives
206
+ }
207
+
208
+ async def set_llm_client(self, client_name: str) -> bool:
209
+ """Set the LLM client to use for this agent.
210
+
211
+ Args:
212
+ client_name: Name of the client to use ('anthropic_client', 'openai_client', 'llm')
213
+
214
+ Returns:
215
+ bool: True if client was set successfully, False otherwise
216
+ """
217
+ if not self.container:
218
+ self.logger.error("No container available to get LLM client")
219
+ return False
220
+
221
+ # Get the client from container
222
+ client = self.container.get_service(client_name)
223
+ if not client:
224
+ self.logger.error(f"LLM client '{client_name}' not found")
225
+ return False
226
+
227
+ # Set client and log
228
+ self.llm_client = client
229
+ self.logger.info(f"Switched to LLM client: {client_name} ({client.__class__.__name__})")
230
+
231
+ return True
232
+
233
+ async def get_llm_settings(self) -> Dict[str, Any]:
234
+ """Get current LLM settings for this agent.
235
+
236
+ Returns:
237
+ Dict containing current LLM settings
238
+ """
239
+ if not self.llm_client:
240
+ return self.llm_config
241
+
242
+ # Get client type
243
+ client_type = self.llm_client.__class__.__name__.lower()
244
+
245
+ # Get model information
246
+ model_info = {
247
+ "name": getattr(self.llm_client, 'model_type', 'unknown'),
248
+ "provider": client_type.replace('client', ''),
249
+ "temperature": getattr(self.llm_client, 'temperature', self.llm_config.get('temperature', 0.7)),
250
+ "max_tokens": getattr(self.llm_client, 'max_tokens', self.llm_config.get('max_tokens', 1000)),
251
+ "initialized": getattr(self.llm_client, '_initialized', False),
252
+ "client_name": getattr(self.llm_client, 'name', client_type)
253
+ }
254
+
255
+ return model_info
256
+
257
+ async def refresh_llm_status(self) -> None:
258
+ """Refresh the current LLM client status.
259
+
260
+ This method ensures the current LLM client information is up-to-date.
261
+ Used primarily by the settings handler to get accurate client status.
262
+ """
263
+ if not self.llm_client:
264
+ self.logger.warning("No LLM client to refresh")
265
+ return
266
+
267
+ # If the client has an initialize method, check its status
268
+ if hasattr(self.llm_client, 'initialize') and not getattr(self.llm_client, '_initialized', False):
269
+ try:
270
+ self.logger.debug(f"Refreshing LLM client {self.llm_client.__class__.__name__}")
271
+ await self.llm_client.initialize()
272
+ except Exception as e:
273
+ self.logger.error(f"Error refreshing LLM client: {e}")
274
+
275
+ # Log the current status
276
+ status = {
277
+ "client": self.llm_client.__class__.__name__,
278
+ "name": getattr(self.llm_client, 'name', self.llm_client.__class__.__name__.lower()),
279
+ "initialized": getattr(self.llm_client, '_initialized', False)
280
+ }
281
+ self.logger.debug(f"LLM client status: {status}")
282
+
283
+ async def update_llm_settings(self, settings: Dict[str, Any]) -> bool:
284
+ """Update LLM settings for this agent.
285
+
286
+ Args:
287
+ settings: Dictionary of settings to update
288
+
289
+ Returns:
290
+ bool: True if settings were updated successfully
291
+ """
292
+ if not self.llm_client:
293
+ self.logger.error("No LLM client available")
294
+ return False
295
+
296
+ try:
297
+ # Extract client_name if provided
298
+ client_name = settings.pop('client_name', None)
299
+
300
+ # If client_name is provided, we may need to switch clients
301
+ if client_name and client_name != getattr(self.llm_client, 'name', None):
302
+ # Only switch if it's different from current
303
+ if self.container:
304
+ new_client = self.container.get_service(client_name)
305
+ if new_client:
306
+ # Ensure the client is initialized
307
+ if not getattr(new_client, '_initialized', False) and hasattr(new_client, 'initialize'):
308
+ await new_client.initialize()
309
+
310
+ self.llm_client = new_client
311
+ self.logger.info(f"Switched to client: {client_name}")
312
+ else:
313
+ self.logger.warning(f"Client {client_name} not found in container")
314
+ return False
315
+
316
+ # Try using update_settings method if available
317
+ if hasattr(self.llm_client, 'update_settings'):
318
+ self.llm_client.update_settings(settings)
319
+ self.logger.info(f"Updated settings via update_settings: {settings}")
320
+
321
+ # Also update our local config
322
+ self.llm_config.update(settings)
323
+ return True
324
+
325
+ # Otherwise update attributes directly
326
+ for key, value in settings.items():
327
+ # Update the client instance attributes directly
328
+ if hasattr(self.llm_client, key):
329
+ # Save previous value for logging
330
+ prev_val = getattr(self.llm_client, key)
331
+ # Set the new value
332
+ setattr(self.llm_client, key, value)
333
+ self.logger.info(f"Updated {key} = {value} (was {prev_val})")
334
+ else:
335
+ self.logger.warning(f"LLM client {self.llm_client.__class__.__name__} does not have attribute {key}")
336
+
337
+ # Also update our local config for future reference
338
+ self.llm_config[key] = value
339
+
340
+ return True
341
+ except Exception as e:
342
+ self.logger.error(f"Error updating LLM settings: {e}")
343
+ return False
344
+
345
+ async def get_available_llm_clients(self) -> Dict[str, Dict[str, Any]]:
346
+ """Get information about available LLM clients.
347
+
348
+ Returns:
349
+ Dict mapping client names to client information
350
+ """
351
+ result = {}
352
+
353
+ # Try to get LLM manager if available
354
+ llm_manager = self.container.get_service('llm_manager')
355
+ if llm_manager:
356
+ # Use LLM manager to get client info
357
+ return await llm_manager.get_available_clients()
358
+
359
+ # Manual fallback if LLM manager not available
360
+ for client_name in ['anthropic_client', 'openai_client', 'llm']:
361
+ client = self.container.get_service(client_name)
362
+ if client:
363
+ # Get basic information
364
+ result[client_name] = {
365
+ 'name': client_name,
366
+ 'model': getattr(client, 'model_type', 'unknown'),
367
+ 'initialized': getattr(client, '_initialized', False),
368
+ 'is_primary': (client_name == 'llm')
369
+ }
370
+
371
+ return result
372
+
373
+ async def _prepare_context(
374
+ self,
375
+ input_text: str,
376
+ metadata: Optional[Dict[str, Any]] = None
377
+ ) -> Dict[str, Any]:
378
+ """Prepare base context for response generation."""
379
+ try:
380
+ # Get system prompt
381
+ system_prompt = ""
382
+ if self.system_prompt_manager:
383
+ system_prompt = await self.system_prompt_manager.get_prompt(
384
+ role=self.name,
385
+ model_type=self.llm_client.__class__.__name__.lower().replace('client', ''),
386
+ character=self.active_character
387
+ )
388
+
389
+ # Get knowledge context if available
390
+ knowledge_context = ""
391
+ if self.memory_manager and self.memory_manager.knowledge_base:
392
+ entries = await self.memory_manager.knowledge_base.search(
393
+ query=input_text,
394
+ top_k=3
395
+ )
396
+ if entries:
397
+ knowledge_context = "\n\n".join(e["text"] for e in entries)
398
+
399
+ return {
400
+ "system": system_prompt,
401
+ "knowledge_context": knowledge_context,
402
+ "messages": []
403
+ }
404
+
405
+ except Exception as e:
406
+ self.logger.error(f"Error preparing context: {e}")
407
+ return {
408
+ "system": "",
409
+ "knowledge_context": "",
410
+ "messages": []
411
+ }
412
+
413
+ @abstractmethod
414
+ async def process_input(
415
+ self,
416
+ input_text: str,
417
+ context_id: Optional[str] = None,
418
+ user_id: Optional[str] = None,
419
+ chat_id: Optional[str] = None,
420
+ metadata: Optional[Dict[str, Any]] = None
421
+ ) -> str:
422
+ """Process input and generate response."""
423
+ raise NotImplementedError("Subclasses must implement process_input")
424
+
425
+ @abstractmethod
426
+ async def start_conversation(
427
+ self,
428
+ user_id: str,
429
+ chat_id: Optional[str] = None,
430
+ metadata: Optional[Dict[str, Any]] = None
431
+ ) -> bool:
432
+ """Start a new conversation."""
433
+ raise NotImplementedError("Subclasses must implement start_conversation")
434
+
435
+ def get_capabilities(self) -> Dict[str, Any]:
436
+ """Get agent capability information.
437
+
438
+ Returns:
439
+ Dictionary with agent capabilities
440
+ """
441
+ # Determine required and optional services
442
+ required_services = []
443
+ optional_services = []
444
+
445
+ # Check which services are available
446
+ if self.llm_client:
447
+ required_services.append("llm")
448
+ if self.memory_manager:
449
+ optional_services.append("memory")
450
+ if self.knowledge_base:
451
+ optional_services.append("knowledge_base")
452
+ if self.character_manager:
453
+ optional_services.append("character_manager")
454
+
455
+ # Build features list based on agent type
456
+ features = []
457
+ if hasattr(self, 'process_message'):
458
+ features.append("message_processing")
459
+ if hasattr(self, 'handle_command'):
460
+ features.append("command_handling")
461
+ if hasattr(self, 'process'):
462
+ features.append("generic_processing")
463
+ if hasattr(self, 'create_session'):
464
+ features.append("session_management")
465
+ if hasattr(self, 'run_automation'):
466
+ features.append("automation")
467
+
468
+ return {
469
+ "id": self.name,
470
+ "name": self.__class__.__name__,
471
+ "version": getattr(self, 'version', None) or get_version(),
472
+ "enabled": self._enabled,
473
+ "features": features,
474
+ "required_services": required_services,
475
+ "optional_services": optional_services,
476
+ "description": self.__doc__ or f"{self.__class__.__name__} agent",
477
+ "initialized": self._initialized,
478
+ "llm_available": self.llm_available,
479
+ "llm_config": self.llm_config if self.llm_config else {}
480
+ }
481
+
482
+ @abstractmethod
483
+ async def cleanup(self) -> None:
484
+ """Clean up agent resources."""
485
+ pass
486
+
487
+ async def generate_response(
488
+ self,
489
+ messages: Optional[List[Message]] = None,
490
+ prompt: Optional[Union[str, Dict[str, Any]]] = None,
491
+ system: Optional[str] = None,
492
+ context: Optional[Any] = None,
493
+ metadata: Optional[Dict[str, Any]] = None,
494
+ max_tokens: Optional[int] = None,
495
+ temperature: Optional[float] = None
496
+ ) -> str:
497
+ """Generate response using LLM."""
498
+ try:
499
+ if not self.llm_client:
500
+ self.logger.error("No LLM client available")
501
+ return "I'm unable to respond right now due to a configuration issue. Please try again later."
502
+
503
+ # Get character info if available
504
+ character_info = None
505
+ if self.active_character:
506
+ character_info = {
507
+ "name": self.active_character.name,
508
+ "bio": self.active_character.bio,
509
+ "traits": self.active_character.adjectives,
510
+ "style": self.active_character.style
511
+ }
512
+
513
+ # Determine appropriate parameters based on LLM client type
514
+ llm_client_type = self.llm_client.__class__.__name__.lower()
515
+ model_name = getattr(self.llm_client, 'model_type', 'unknown')
516
+
517
+ # Get current values from the client itself
518
+ client_temperature = getattr(self.llm_client, 'temperature', None)
519
+ client_max_tokens = getattr(self.llm_client, 'max_tokens', None)
520
+
521
+ # Use passed values, client values, or config defaults - in that order of priority
522
+ request_temperature = temperature or client_temperature or self.llm_config.get('temperature', 0.7)
523
+ request_max_tokens = max_tokens or client_max_tokens or self.llm_config.get('max_tokens', 4096)
524
+
525
+ # Log request parameters
526
+ self.logger.info(f"LLM Request | Model: {model_name} | Client: {llm_client_type} | " +
527
+ f"Temp: {request_temperature} | Max Tokens: {request_max_tokens}")
528
+
529
+ # Initialize with default parameters
530
+ llm_params = {
531
+ "temperature": request_temperature,
532
+ "max_tokens": request_max_tokens
533
+ }
534
+
535
+ # Format messages based on client type
536
+ formatted_messages = []
537
+
538
+ # If messages is a list of Message objects, convert to dict format
539
+ if messages and isinstance(messages, list):
540
+ if messages and isinstance(messages[0], dict):
541
+ # Already in dict format
542
+ formatted_messages = messages
543
+ else:
544
+ for msg in messages:
545
+ if hasattr(msg, 'to_dict'):
546
+ formatted_messages.append(msg.to_dict())
547
+ elif hasattr(msg, 'role') and hasattr(msg, 'content'):
548
+ formatted_messages.append({
549
+ 'role': msg.role,
550
+ 'content': msg.content
551
+ })
552
+ # If prompt is provided as a dict with messages
553
+ elif prompt and isinstance(prompt, dict) and 'messages' in prompt:
554
+ for msg in prompt['messages']:
555
+ if isinstance(msg, dict):
556
+ formatted_messages.append(msg)
557
+ elif hasattr(msg, 'to_dict'):
558
+ formatted_messages.append(msg.to_dict())
559
+ elif hasattr(msg, 'role') and hasattr(msg, 'content'):
560
+ formatted_messages.append({
561
+ 'role': msg.role,
562
+ 'content': msg.content
563
+ })
564
+
565
+ # If system is in prompt, use it
566
+ if 'system' in prompt and not system:
567
+ system = prompt['system']
568
+
569
+ # If prompt is a string, use it as a single user message
570
+ elif prompt and isinstance(prompt, str):
571
+ formatted_messages.append({
572
+ 'role': 'user',
573
+ 'content': prompt
574
+ })
575
+
576
+ # Add system message if available (but not for Anthropic)
577
+ system_content = system
578
+ if system and 'anthropic' not in llm_client_type:
579
+ # Check if there's already a system message
580
+ has_system = any(msg.get('role') == 'system' for msg in formatted_messages)
581
+ if not has_system:
582
+ formatted_messages.insert(0, {
583
+ 'role': 'system',
584
+ 'content': system
585
+ })
586
+
587
+ # Log how many messages we've prepared
588
+ self.logger.debug(f"Prepared {len(formatted_messages)} messages for LLM, system message: {'present' if system else 'absent'}")
589
+
590
+ # Make the request to the LLM client based on its specific API
591
+ response = None
592
+
593
+ if 'anthropic' in llm_client_type:
594
+ # For Anthropic, we need to:
595
+ # 1. Extract any system messages from the formatted_messages
596
+ # 2. Pass system content separately
597
+ # 3. Filter metadata for Anthropic API
598
+
599
+ # First check if there's a system message in formatted_messages and extract it
600
+ anthropic_messages = []
601
+ for msg in formatted_messages:
602
+ if msg.get('role') == 'system':
603
+ if not system_content: # Only use if we don't already have system content
604
+ system_content = msg.get('content', '')
605
+ else:
606
+ anthropic_messages.append(msg)
607
+
608
+ # Create a filtered version of metadata - Anthropic only allows specific metadata fields
609
+ anthropic_metadata = None
610
+ if metadata:
611
+ # Only pass through user_id which is allowed in Anthropic's metadata
612
+ anthropic_metadata = {}
613
+ if 'user_id' in metadata:
614
+ anthropic_metadata['user_id'] = metadata['user_id']
615
+
616
+ if len(anthropic_metadata) == 0:
617
+ anthropic_metadata = None
618
+
619
+ # Make the API call with system parameter separate from messages
620
+ response = await self.llm_client.generate_response(
621
+ messages=anthropic_messages, # Send messages WITHOUT system role
622
+ system=system_content, # Pass system content separately
623
+ metadata=anthropic_metadata, # Use filtered metadata
624
+ **llm_params
625
+ )
626
+
627
+ elif 'openai' in llm_client_type or 'llama' in llm_client_type:
628
+ # For OpenAI/Llama just pass messages with system message included
629
+
630
+ # For OpenAI specifically, make sure metadata is properly formatted
631
+ # (OpenAI expects character to be a string, not an object)
632
+ if 'openai' in llm_client_type and metadata:
633
+ openai_metadata = {}
634
+
635
+ # Handle special case for 'character'
636
+ if 'character' in metadata and isinstance(metadata['character'], dict):
637
+ if 'name' in metadata['character']:
638
+ openai_metadata['character'] = metadata['character']['name']
639
+
640
+ # Copy other simple values
641
+ for k, v in metadata.items():
642
+ if k != 'character' and isinstance(v, (str, int, float, bool)):
643
+ openai_metadata[k] = v
644
+
645
+ # Check if we have any metadata to send
646
+ if openai_metadata:
647
+ # For OpenAI, we need to decide if we want to enable store
648
+ # Based on settings, we may choose to enable or disable metadata
649
+ use_metadata = True
650
+ # Get store setting from llm_params if available
651
+ store_enabled = llm_params.get('store', False)
652
+
653
+ if use_metadata and not store_enabled:
654
+ # We could enable store here, but for now let's just log and skip metadata
655
+ self.logger.warning("OpenAI metadata provided but store not enabled, skipping metadata")
656
+ # Skip sending metadata
657
+ response = await self.llm_client.generate_response(
658
+ messages=formatted_messages,
659
+ **llm_params
660
+ )
661
+ else:
662
+ # Send metadata along with store=True
663
+ response = await self.llm_client.generate_response(
664
+ messages=formatted_messages,
665
+ metadata=openai_metadata,
666
+ store=True,
667
+ **llm_params
668
+ )
669
+ else:
670
+ # No metadata to send
671
+ response = await self.llm_client.generate_response(
672
+ messages=formatted_messages,
673
+ **llm_params
674
+ )
675
+ else:
676
+ # For Llama or when no metadata present
677
+ response = await self.llm_client.generate_response(
678
+ messages=formatted_messages,
679
+ metadata=metadata,
680
+ **llm_params
681
+ )
682
+
683
+ else:
684
+ # Generic fallback - try to be robust for any client type
685
+ # Pass both system and messages separately, and as a combined 'prompt'
686
+ prompt_bundle = {
687
+ "messages": formatted_messages,
688
+ "system": system
689
+ }
690
+
691
+ response = await self.llm_client.generate_response(
692
+ messages=formatted_messages,
693
+ system=system,
694
+ prompt=prompt_bundle,
695
+ metadata=metadata,
696
+ **llm_params
697
+ )
698
+
699
+ # Log response details
700
+ client_type = self.llm_client.__class__.__name__
701
+ model_name = getattr(self.llm_client, 'model_type', 'unknown')
702
+
703
+ # Try to extract token usage from response if available
704
+ token_info = "Token usage unknown"
705
+ try:
706
+ if hasattr(response, 'usage'):
707
+ usage = response.usage
708
+ token_info = f"Tokens: {usage.prompt_tokens} prompt + {usage.completion_tokens} completion = {usage.total_tokens} total"
709
+ elif hasattr(self.llm_client, 'last_response') and hasattr(self.llm_client.last_response, 'usage'):
710
+ usage = self.llm_client.last_response.usage
711
+ token_info = f"Tokens: {usage.prompt_tokens} prompt + {usage.completion_tokens} completion = {usage.total_tokens} total"
712
+ except Exception as e:
713
+ self.logger.debug(f"Could not extract token usage: {e}")
714
+
715
+ # Log consolidated information
716
+ self.logger.info(f"LLM Response | Model: {model_name} | Client: {client_type} | {token_info}")
717
+
718
+ # Extract string content from response
719
+ if isinstance(response, str):
720
+ return response
721
+ elif hasattr(response, 'content'):
722
+ return response.content
723
+ elif hasattr(response, 'choices') and len(response.choices) > 0:
724
+ # OpenAI-style response
725
+ return response.choices[0].message.content
726
+ elif hasattr(response, 'text'):
727
+ return response.text
728
+ else:
729
+ # Try to convert to string as last resort
730
+ return str(response)
731
+
732
+ except Exception as e:
733
+ self.logger.error(f"Error generating response: {e}")
734
+ raise
735
+
736
+ def _validate_dependencies(self) -> None:
737
+ """Validate dependencies."""
738
+ if not self.llm_client:
739
+ self.llm_available = False
740
+ self.logger.warning(f"{self.name} agent initialized without LLM - some features will be disabled")
741
+ return
742
+ self.llm_available = True
743
+