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
cli/commands/skills.py ADDED
@@ -0,0 +1,128 @@
1
+ """polyrob skills commands (P3 cli/commands split)."""
2
+ from typing import Optional
3
+
4
+ import click
5
+
6
+
7
+ def get_skill_manager():
8
+ """Lazy accessor (kept module-level so it stays cheap to import and patchable)."""
9
+ from agents.task.agent.skill_manager import get_skill_manager as _gsm
10
+ return _gsm()
11
+
12
+
13
+ @click.group()
14
+ def skills():
15
+ """List and validate agent skills."""
16
+ pass
17
+
18
+
19
+ @skills.command("list")
20
+ def skills_list():
21
+ """List registered skill IDs."""
22
+ try:
23
+ mgr = get_skill_manager()
24
+ except Exception as e:
25
+ click.echo(click.style("[polyrob] ERROR: ", fg="red") + f"skill manager unavailable: {e}")
26
+ raise SystemExit(1)
27
+ if hasattr(mgr, "_ensure_rules_loaded"):
28
+ mgr._ensure_rules_loaded()
29
+ skill_ids = sorted(getattr(mgr, "skill_rules", {}).keys())
30
+ if not skill_ids:
31
+ click.echo("No skills registered.")
32
+ return
33
+ click.echo(f"Registered skills ({len(skill_ids)}):")
34
+ for sid in skill_ids:
35
+ click.echo(f" - {sid}")
36
+
37
+
38
+ @skills.command("validate")
39
+ @click.argument("skill_id", required=False)
40
+ def skills_validate(skill_id: Optional[str]):
41
+ """Validate one skill (by id) or the whole authored library.
42
+
43
+ With SKILL_ID: the legacy per-skill check (rules.json wiring + content shape).
44
+ With no argument: the strict agentskills.io frontmatter compliance check
45
+ (``SkillManager.validate_all_authored``) run across every shipped skill —
46
+ the same gate `polyrob doctor` and CI's library-invariant test use.
47
+ """
48
+ try:
49
+ mgr = get_skill_manager()
50
+ except Exception as e:
51
+ click.echo(click.style("[polyrob] ERROR: ", fg="red") + f"skill manager unavailable: {e}")
52
+ raise SystemExit(1)
53
+ if skill_id:
54
+ # F7: an unknown skill id should fail clearly, not crash with a raw
55
+ # KeyError/traceback. Validate against the known ids first.
56
+ if hasattr(mgr, "_ensure_rules_loaded"):
57
+ try:
58
+ mgr._ensure_rules_loaded()
59
+ except Exception:
60
+ pass
61
+ known = getattr(mgr, "skill_rules", {}) or {}
62
+ if known and skill_id not in known:
63
+ click.echo(
64
+ click.style("[polyrob] ERROR: ", fg="red")
65
+ + f"no such skill: {skill_id!r} (try `polyrob skills list`)"
66
+ )
67
+ raise SystemExit(1)
68
+ try:
69
+ result = mgr.validate_skill(skill_id)
70
+ except Exception as e:
71
+ click.echo(
72
+ click.style("[polyrob] ERROR: ", fg="red")
73
+ + f"no such skill: {skill_id!r} ({e})"
74
+ )
75
+ raise SystemExit(1)
76
+ status = click.style("valid", fg="green") if result.is_valid else click.style("INVALID", fg="red")
77
+ click.echo(f"{result.skill_id:<24} {status}")
78
+ for err in getattr(result, "errors", []) or []:
79
+ click.echo(f" error: {err}")
80
+ for warn in getattr(result, "warnings", []) or []:
81
+ click.echo(f" warn: {warn}")
82
+ if not result.is_valid:
83
+ raise SystemExit(1)
84
+ return
85
+
86
+ # No skill_id: strict library-wide compliance check (Task 4).
87
+ issues_by_skill = mgr.validate_all_authored()
88
+ error_count = 0
89
+ for sid in sorted(issues_by_skill):
90
+ click.echo(f"{sid}:")
91
+ for issue in issues_by_skill[sid]:
92
+ if issue.level == "error":
93
+ error_count += 1
94
+ click.echo(f" {issue.level}: {issue.code} — {issue.msg}")
95
+ if not issues_by_skill:
96
+ click.echo(click.style("All authored skills are compliant.", fg="green"))
97
+ else:
98
+ click.echo(
99
+ f"\n{len(issues_by_skill)} skill(s) with issues ({error_count} error(s))"
100
+ )
101
+ if error_count:
102
+ raise SystemExit(1)
103
+
104
+
105
+ @skills.command("export")
106
+ @click.argument("skill_id")
107
+ @click.option("--to", "dest", default=None, help="Target dir (default: ~/.agents/skills)")
108
+ def skills_export(skill_id: str, dest: Optional[str]):
109
+ """Copy a skill folder (SKILL.md + resources) to a portable location.
110
+
111
+ Defaults to ``~/.agents/skills/<skill_id>`` (agentskills.io-style layout);
112
+ ``--to DIR`` targets ``DIR/<skill_id>`` instead. Refuses to clobber an
113
+ existing target directory.
114
+ """
115
+ import shutil
116
+ from pathlib import Path
117
+
118
+ mgr = get_skill_manager()
119
+ src = mgr.resolve_skill_dir(skill_id)
120
+ if not src or not (src / "SKILL.md").exists():
121
+ raise click.ClickException(f"no such skill: {skill_id!r} (try `polyrob skills list`)")
122
+ target_root = Path(dest) if dest else (Path.home() / ".agents" / "skills")
123
+ out = target_root / skill_id
124
+ if out.exists():
125
+ raise click.ClickException(f"already exists: {out} (remove it first)")
126
+ out.parent.mkdir(parents=True, exist_ok=True)
127
+ shutil.copytree(src, out)
128
+ click.echo(f"exported {skill_id} -> {out}")
@@ -0,0 +1,100 @@
1
+ """POLYROB subagents commands — inspect background delegations.
2
+
3
+ Delegations are session-scoped and in-memory. This command can show:
4
+ - General delegation capability info
5
+ - Active delegations for a live session (if attached/queried)
6
+ - Static info about limits and configuration
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from typing import Optional
12
+
13
+ import click
14
+
15
+ from agents.task.constants import TimeoutConfig
16
+
17
+
18
+ @click.group("subagents")
19
+ def subagents():
20
+ """Inspect agent delegation and subagent activity."""
21
+ pass
22
+
23
+
24
+ @subagents.command("info")
25
+ @click.option("--json", "as_json", is_flag=True, help="Print machine-readable JSON.")
26
+ def subagents_info(as_json: bool):
27
+ """Show delegation capability and limits."""
28
+ # Lazy import to avoid heavy tools import chain
29
+ try:
30
+ from tools.controller.delegation import get_blocked_child_tools
31
+ blocked_tools = sorted(get_blocked_child_tools())
32
+ except Exception:
33
+ # Fallback if tools import fails
34
+ blocked_tools = []
35
+
36
+ info = {
37
+ "delegation_enabled": TimeoutConfig.get_sub_agents_enabled(),
38
+ "max_concurrent": TimeoutConfig.get_max_concurrent_sub_agents(),
39
+ "max_depth": TimeoutConfig.get_max_sub_agent_depth(),
40
+ "max_async": TimeoutConfig.get_max_async_sub_agents(),
41
+ "sync_timeout": TimeoutConfig.get_sub_agent_timeout(),
42
+ # The parallel/async (background) delegation path uses its own longer
43
+ # timeout, not the sync one — report the real value.
44
+ "async_timeout": TimeoutConfig.get_parallel_subtasks_timeout(),
45
+ "blocked_tools": blocked_tools,
46
+ }
47
+
48
+ if as_json:
49
+ click.echo(json.dumps(info, indent=2))
50
+ else:
51
+ click.echo("Delegation Capability:")
52
+ click.echo(f" Enabled: {info['delegation_enabled']}")
53
+ click.echo(f" Max concurrent: {info['max_concurrent']}")
54
+ click.echo(f" Max depth: {info['max_depth']}")
55
+ click.echo(f" Max background: {info['max_async']}")
56
+ click.echo(f" Sync timeout: {info['sync_timeout']}s")
57
+ click.echo(f" Async timeout: {info['async_timeout']}s")
58
+ if info['blocked_tools']:
59
+ click.echo(f" Blocked tools: {', '.join(info['blocked_tools'])}")
60
+
61
+
62
+ @subagents.command("list")
63
+ @click.option("--session-id", help="Session to inspect (requires live session or persisted data).")
64
+ @click.option("--json", "as_json", is_flag=True, help="Print machine-readable JSON.")
65
+ def subagents_list(session_id: Optional[str], as_json: bool):
66
+ """List subagents/delegations.
67
+
68
+ Note: Delegations are currently in-memory and session-scoped. Without a live
69
+ session connection, this can only show persisted records (future work).
70
+ """
71
+ if as_json:
72
+ click.echo(json.dumps({
73
+ "supported": False, "delegations": [],
74
+ "reason": "CLI delegation introspection is not yet persisted; "
75
+ "use /subagents in a live REPL session.",
76
+ }, indent=2))
77
+ return
78
+ if session_id:
79
+ click.echo(click.style("[polyrob] ", fg="yellow") +
80
+ "Live session introspection requires API/WebView connection")
81
+ click.echo("CLI will support persisted delegation records in a future update")
82
+ else:
83
+ click.echo("Use /subagents in an active REPL session, or specify --session-id for persisted records")
84
+
85
+
86
+ @subagents.command("show")
87
+ @click.argument("delegation_id")
88
+ @click.option("--json", "as_json", is_flag=True, help="Print machine-readable JSON.")
89
+ def subagents_show(delegation_id: str, as_json: bool):
90
+ """Show details for a specific delegation."""
91
+ if as_json:
92
+ click.echo(json.dumps({
93
+ "supported": False, "delegation_id": delegation_id,
94
+ "reason": "CLI delegation introspection is not yet persisted; "
95
+ "use /subagents in a live REPL session.",
96
+ }, indent=2))
97
+ return
98
+ click.echo(click.style("[polyrob] ", fg="yellow") +
99
+ "Live delegation introspection requires API/WebView connection")
100
+ click.echo("CLI will support persisted delegation records in a future update")
@@ -0,0 +1,81 @@
1
+ """polyrob surface — operator controls for per-surface circuit breakers.
2
+
3
+ The worker process owns an in-memory SurfaceCircuitBreaker that auto-opens after
4
+ K consecutive failures. The CLI process is SEPARATE, so pause/resume are persisted
5
+ to a tiny SQLite table (data/surface_state.db) that the worker reads on each
6
+ is_open() call (when its breaker is built with a CircuitStore attached).
7
+
8
+ Commands:
9
+ polyrob surface list Show all known surfaces and their paused state.
10
+ polyrob surface pause <id> Pause a surface (writes to the store).
11
+ polyrob surface resume <id> Resume a surface (clears the stored flag).
12
+ """
13
+ import os
14
+
15
+ import click
16
+
17
+
18
+ def _data_dir() -> str:
19
+ # Match the worker's data home (build_cli_container's _resolve_cli_data_home =
20
+ # <cwd>/.polyrob by default) so pause/resume write the SAME surface_state.db the
21
+ # worker reads — 'POLYROB_DATA_DIR or data' pointed the CLI at a divergent ./data.
22
+ from core.bootstrap import _resolve_cli_data_home
23
+ data_home, _, _ = _resolve_cli_data_home()
24
+ return str(data_home)
25
+
26
+
27
+ def _store():
28
+ from core.surfaces.circuit import CircuitStore
29
+ return CircuitStore(os.path.join(_data_dir(), "surface_state.db"))
30
+
31
+
32
+ def _warn_if_breaker_inert() -> None:
33
+ """pause/resume only bite if the worker built a CircuitStore-backed breaker, which
34
+ it does ONLY when OUTBOUND_QUEUE_ENABLED is on. Warn so the operator isn't misled
35
+ by a 'paused' that has no effect."""
36
+ from agents.task.surface_config import SurfaceConfig
37
+ if not SurfaceConfig.outbound_queue_enabled():
38
+ click.echo(click.style(
39
+ "note: OUTBOUND_QUEUE_ENABLED is off — the worker builds no circuit breaker, "
40
+ "so this is recorded but has NO effect until you enable it.", fg="yellow"))
41
+
42
+
43
+ @click.group()
44
+ def surface():
45
+ """Inspect and control per-surface circuit breakers."""
46
+
47
+
48
+ @surface.command("list")
49
+ def list_surfaces():
50
+ """List surfaces and their persisted paused state."""
51
+ rows = _store().list_all()
52
+ if not rows:
53
+ click.echo(click.style("no surface state entries", dim=True))
54
+ click.echo(click.style("(surfaces are auto-registered when first paused)", dim=True))
55
+ return
56
+ for r in rows:
57
+ status = (
58
+ click.style("PAUSED", fg="red", bold=True)
59
+ if r["paused"]
60
+ else click.style("active", fg="green")
61
+ )
62
+ click.echo(f" {r['surface_id']:<20} {status}")
63
+
64
+
65
+ @surface.command("pause")
66
+ @click.argument("surface_id")
67
+ def pause_surface(surface_id: str):
68
+ """Pause a surface — the worker will skip it until resumed."""
69
+ _store().pause(surface_id)
70
+ click.echo(click.style(f"paused: {surface_id}", fg="yellow"))
71
+ click.echo(click.style("worker will defer outbound messages for this surface", dim=True))
72
+ _warn_if_breaker_inert()
73
+
74
+
75
+ @surface.command("resume")
76
+ @click.argument("surface_id")
77
+ def resume_surface(surface_id: str):
78
+ """Resume a paused surface — the worker will start delivering again."""
79
+ _store().resume(surface_id)
80
+ click.echo(click.style(f"resumed: {surface_id}", fg="green"))
81
+ _warn_if_breaker_inert()
@@ -0,0 +1,185 @@
1
+ """polyrob telegram — chat with the agent over Telegram via local long-polling.
2
+
3
+ No webhook / public URL / SSL needed: this long-polls getUpdates on your machine and
4
+ runs the full Task agent (same front door as the API webhook path). Owner-locked via
5
+ ALLOWED_TELEGRAM_USER_IDS (raw Telegram numeric ids); with no allowlist set the bot
6
+ replies with your id so you can lock it.
7
+
8
+ Token: --token or TELEGRAM_BOT_TOKEN (process env or ./.polyrob/.env). Nothing is committed.
9
+ """
10
+ import asyncio
11
+ import os
12
+ import signal
13
+ import sys
14
+ from typing import Optional
15
+
16
+ import click
17
+
18
+
19
+ class TelegramTokenError(click.ClickException):
20
+ """No Telegram bot token available."""
21
+
22
+
23
+ def resolve_telegram_token(token_opt: Optional[str]) -> str:
24
+ """--token wins, else TELEGRAM_BOT_TOKEN env; raise a clear error if absent."""
25
+ tok = (token_opt or os.environ.get("TELEGRAM_BOT_TOKEN") or "").strip()
26
+ if not tok:
27
+ raise TelegramTokenError(
28
+ "No Telegram bot token. Pass --token, or set TELEGRAM_BOT_TOKEN "
29
+ "(process env or ./.polyrob/.env). Create a bot via @BotFather to get one."
30
+ )
31
+ return tok
32
+
33
+
34
+ @click.command()
35
+ @click.option("--token", default=None, help="Telegram bot token (else TELEGRAM_BOT_TOKEN env)")
36
+ @click.option("--verbose", "-v", is_flag=True, help="Show debug logging")
37
+ def telegram(token: Optional[str], verbose: bool):
38
+ """Run the agent as a Telegram bot (local long-polling)."""
39
+ asyncio.run(_run_telegram(token, verbose))
40
+
41
+
42
+ async def _run_telegram(token_opt: Optional[str], verbose: bool):
43
+ import logging as _logging
44
+
45
+ from core.bootstrap import build_cli_container, setup_project_path, setup_sqlite_compat
46
+
47
+ # The outbound bus + binding are gated on SINGULAR_CHAT_ENABLED; the surface gate
48
+ # is TELEGRAM_SURFACE_ENABLED. Default them ON for this command (explicit env wins),
49
+ # BEFORE the container build so the bus installs during TaskAgent construction too.
50
+ os.environ.setdefault("SINGULAR_CHAT_ENABLED", "true")
51
+ os.environ.setdefault("TELEGRAM_SURFACE_ENABLED", "true")
52
+
53
+ setup_project_path()
54
+ setup_sqlite_compat()
55
+
56
+ # No usable provider key → clean canonical message + exit (a daemon must not block
57
+ # on an interactive wizard). Loads env layers internally.
58
+ from cli.keys import preflight_or_onboard
59
+ if not preflight_or_onboard(interactive=False):
60
+ sys.exit(1)
61
+
62
+ # Resolve the bot token NOW — preflight_or_onboard already loaded the env layers
63
+ # (./.polyrob/.env etc.), so fail fast on a missing token (a clean ClickException)
64
+ # instead of paying a full container build first.
65
+ token = resolve_telegram_token(token_opt)
66
+
67
+ # A headless run (systemd: stderr is the journal, not a TTY) MUST log — a server
68
+ # with no logs is unoperable. An interactive run keeps the console clean. `-v` always
69
+ # gives DEBUG. So: verbose->DEBUG; headless->INFO to the journal; interactive->quiet.
70
+ headless = not sys.stderr.isatty()
71
+ if verbose:
72
+ log_level = "DEBUG"
73
+ elif headless:
74
+ log_level = "INFO"
75
+ else:
76
+ log_level = "ERROR"
77
+ quiet = (not verbose) and (not headless)
78
+ if quiet:
79
+ _logging.disable(_logging.CRITICAL)
80
+ os.environ["GRPC_VERBOSITY"] = "ERROR"
81
+ os.environ["GLOG_minloglevel"] = "3"
82
+
83
+ try:
84
+ container = await build_cli_container(log_level=log_level)
85
+ except Exception as e:
86
+ click.echo(click.style("[polyrob] ERROR: ", fg="red") + f"failed to start: {e}")
87
+ sys.exit(1)
88
+ if quiet:
89
+ _logging.disable(_logging.ERROR)
90
+ elif not verbose:
91
+ # Headless service: app logs at INFO in the journal, but silence the noisy
92
+ # transport libraries (long-poll getUpdates, aiogram dispatcher, asyncio).
93
+ for _noisy in ("httpx", "httpcore", "aiogram.event", "asyncio", "hpack"):
94
+ _logging.getLogger(_noisy).setLevel(_logging.WARNING)
95
+
96
+ task_agent = container.get_agent("task_agent")
97
+ if not task_agent:
98
+ click.echo(click.style("[polyrob] ERROR: ", fg="red") + "TaskAgent not available in container")
99
+ sys.exit(1)
100
+
101
+ # Install the outbound bus (idempotent; gated on SINGULAR_CHAT_ENABLED) so agent
102
+ # replies can route out to the Telegram surface.
103
+ from core.surfaces.bootstrap import install_surface_bus
104
+ install_surface_bus(container) # db_path defaults to container.config.data_dir
105
+
106
+ dispatcher = container.get_service("outbound_dispatcher")
107
+ if dispatcher is not None:
108
+ dispatcher.start()
109
+
110
+ from surfaces.telegram.harness import build_telegram_harness
111
+ # Pin harness state DBs (dedup / user directory) to the container's data_dir so
112
+ # per-instance isolation holds under POLYROB_DATA_DIR (else they land in ./data).
113
+ _data_dir = getattr(getattr(container, "config", None), "data_dir", "data") or "data"
114
+ harness = build_telegram_harness(container, task_agent, token=token, webhook_base=None,
115
+ data_dir=_data_dir)
116
+
117
+ # start() registers + subscribes the surface and clears any stale webhook.
118
+ await harness.start()
119
+
120
+ # Start the autonomy background loops under the local profile — the SAME shared
121
+ # runtime the REPL + API lifespan use. Crucially this is what runs the #6 surface-GC
122
+ # ticker that prunes stale chat<->session bindings; without it GC would be dead on
123
+ # the primary Telegram surface (the one that mints those bindings). Fail-open.
124
+ autonomy_handles = None
125
+ try:
126
+ from agents.task.constants import local_mode_enabled
127
+ if local_mode_enabled():
128
+ from core.autonomy_runtime import start_autonomy
129
+ _data_dir = getattr(getattr(container, "config", None), "data_dir", "data")
130
+ autonomy_handles = start_autonomy(task_agent=task_agent, data_dir=_data_dir)
131
+ except Exception:
132
+ autonomy_handles = None
133
+
134
+ # Greet: confirm which bot we're driving + the owner-lock state.
135
+ try:
136
+ me = await harness.bot.get_me()
137
+ username = getattr(me, "username", None)
138
+ except Exception:
139
+ username = None
140
+ allow = (os.environ.get("ALLOWED_TELEGRAM_USER_IDS") or "").strip()
141
+ click.echo(click.style("telegram bot online", fg="green")
142
+ + (f": @{username}" if username else ""))
143
+ if allow:
144
+ click.echo(click.style(f"owner-locked to user id(s): {allow}", dim=True))
145
+ else:
146
+ click.echo(click.style(
147
+ "no allowlist set — message the bot once and it will reply with your id, "
148
+ "then set ALLOWED_TELEGRAM_USER_IDS and restart.", fg="yellow"))
149
+ click.echo(click.style("polling… (Ctrl-C to stop)", dim=True))
150
+
151
+ # Clean shutdown on Ctrl-C: stop the loop, then close the bot session.
152
+ loop = asyncio.get_running_loop()
153
+ poll_task = asyncio.ensure_future(harness.run_polling())
154
+
155
+ def _stop(*_a):
156
+ harness._running = False
157
+ poll_task.cancel()
158
+
159
+ try:
160
+ loop.add_signal_handler(signal.SIGINT, _stop)
161
+ loop.add_signal_handler(signal.SIGTERM, _stop)
162
+ except (NotImplementedError, RuntimeError):
163
+ for _sig in (signal.SIGINT, signal.SIGTERM):
164
+ try:
165
+ signal.signal(_sig, lambda *_a: _stop())
166
+ except (ValueError, OSError, AttributeError):
167
+ pass # SIGTERM may be unavailable on some platforms/threads
168
+
169
+ try:
170
+ await poll_task
171
+ except asyncio.CancelledError:
172
+ pass
173
+ finally:
174
+ click.echo("\n" + click.style("stopping telegram bot…", dim=True))
175
+ if autonomy_handles is not None:
176
+ try:
177
+ await autonomy_handles.stop()
178
+ except Exception:
179
+ pass
180
+ if dispatcher is not None:
181
+ try:
182
+ await dispatcher.stop()
183
+ except Exception:
184
+ pass
185
+ await harness.stop()