polyrob 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (745) hide show
  1. agents/README.md +453 -0
  2. agents/__init__.py +164 -0
  3. agents/base_agent.py +743 -0
  4. agents/personality/character.py +119 -0
  5. agents/personality/character_manager.py +309 -0
  6. agents/personality/characters/rob.character.json +56 -0
  7. agents/personality/characters/trump.character.json +63 -0
  8. agents/personality/persona_render.py +78 -0
  9. agents/personality/persona_resolver.py +115 -0
  10. agents/prompt/__init__.py +58 -0
  11. agents/prompt/base_prompt.py +92 -0
  12. agents/prompt/system.py +417 -0
  13. agents/task/README_PATH_MANAGEMENT.md +141 -0
  14. agents/task/__init__.py +162 -0
  15. agents/task/agent/__init__.py +70 -0
  16. agents/task/agent/agent_state.py +408 -0
  17. agents/task/agent/async_delegation.py +274 -0
  18. agents/task/agent/autonomy_state.py +275 -0
  19. agents/task/agent/conversation.py +75 -0
  20. agents/task/agent/core/__init__.py +6 -0
  21. agents/task/agent/core/aux_metering.py +61 -0
  22. agents/task/agent/core/background_review.py +157 -0
  23. agents/task/agent/core/construction.py +1179 -0
  24. agents/task/agent/core/conversational_exit.py +49 -0
  25. agents/task/agent/core/correspondent_gate.py +227 -0
  26. agents/task/agent/core/curator.py +246 -0
  27. agents/task/agent/core/episodic_digest.py +103 -0
  28. agents/task/agent/core/error_recovery.py +480 -0
  29. agents/task/agent/core/history_io.py +257 -0
  30. agents/task/agent/core/llm_provisioning.py +287 -0
  31. agents/task/agent/core/llm_runner.py +547 -0
  32. agents/task/agent/core/logging_io.py +276 -0
  33. agents/task/agent/core/loop_detection.py +262 -0
  34. agents/task/agent/core/memory_prefetch.py +325 -0
  35. agents/task/agent/core/memory_writer.py +667 -0
  36. agents/task/agent/core/model_introspection.py +82 -0
  37. agents/task/agent/core/model_swap.py +177 -0
  38. agents/task/agent/core/next_action_internal.py +1331 -0
  39. agents/task/agent/core/output_validation.py +303 -0
  40. agents/task/agent/core/project_context.py +272 -0
  41. agents/task/agent/core/resources.py +93 -0
  42. agents/task/agent/core/result_processing.py +389 -0
  43. agents/task/agent/core/run_loop.py +661 -0
  44. agents/task/agent/core/safety_lifecycle.py +228 -0
  45. agents/task/agent/core/secret_guard.py +293 -0
  46. agents/task/agent/core/self_wake.py +280 -0
  47. agents/task/agent/core/session_metadata.py +104 -0
  48. agents/task/agent/core/step.py +781 -0
  49. agents/task/agent/core/step_execution.py +398 -0
  50. agents/task/agent/core/step_telemetry.py +222 -0
  51. agents/task/agent/core/test_aux_metering.py +50 -0
  52. agents/task/agent/core/test_billing_failover.py +68 -0
  53. agents/task/agent/core/test_fatal_error_classifier.py +38 -0
  54. agents/task/agent/core/test_judge_validation.py +275 -0
  55. agents/task/agent/core/test_model_swap.py +190 -0
  56. agents/task/agent/core/test_reflection_offload.py +54 -0
  57. agents/task/agent/core/turn_input.py +8 -0
  58. agents/task/agent/core/untrusted_wrap.py +103 -0
  59. agents/task/agent/core/user_ingress.py +228 -0
  60. agents/task/agent/hitl_manager.py +396 -0
  61. agents/task/agent/log_sanitize.py +36 -0
  62. agents/task/agent/message_manager/config.py +97 -0
  63. agents/task/agent/message_manager/service.py +758 -0
  64. agents/task/agent/message_manager/tests.py +305 -0
  65. agents/task/agent/message_manager/tool_call_builder.py +539 -0
  66. agents/task/agent/message_manager/tool_message_repair.py +364 -0
  67. agents/task/agent/message_manager/views.py +143 -0
  68. agents/task/agent/messages/__init__.py +8 -0
  69. agents/task/agent/messages/builders.py +309 -0
  70. agents/task/agent/messages/compactor.py +611 -0
  71. agents/task/agent/messages/context_references.py +271 -0
  72. agents/task/agent/messages/filters.py +333 -0
  73. agents/task/agent/messages/guidance.py +328 -0
  74. agents/task/agent/messages/persistence.py +448 -0
  75. agents/task/agent/messages/retrieval.py +506 -0
  76. agents/task/agent/messages/sqlite_persistence.py +85 -0
  77. agents/task/agent/messages/test_compaction_upgrade.py +349 -0
  78. agents/task/agent/messages/test_persistence_subdir_fix.py +28 -0
  79. agents/task/agent/messages/test_sqlite_persistence.py +54 -0
  80. agents/task/agent/messages/token_counter.py +532 -0
  81. agents/task/agent/orchestrator.py +776 -0
  82. agents/task/agent/profile_manager.py +105 -0
  83. agents/task/agent/profile_registry.py +173 -0
  84. agents/task/agent/prompts.py +1090 -0
  85. agents/task/agent/scenario_registry.py +226 -0
  86. agents/task/agent/service.py +509 -0
  87. agents/task/agent/session.py +984 -0
  88. agents/task/agent/skill_discovery.py +110 -0
  89. agents/task/agent/skill_frontmatter.py +94 -0
  90. agents/task/agent/skill_manager.py +1201 -0
  91. agents/task/agent/skill_store.py +627 -0
  92. agents/task/agent/skill_validation.py +56 -0
  93. agents/task/agent/skill_writer.py +492 -0
  94. agents/task/agent/sub_agent_manager.py +1137 -0
  95. agents/task/agent/test_session_disk_recovery.py +34 -0
  96. agents/task/agent/tests.py +513 -0
  97. agents/task/agent/tool_call_tracker.py +511 -0
  98. agents/task/agent/views.py +469 -0
  99. agents/task/config.py +432 -0
  100. agents/task/constants.py +1384 -0
  101. agents/task/flag_defaults.py +63 -0
  102. agents/task/goals/__init__.py +1 -0
  103. agents/task/goals/autonomy_marker.py +28 -0
  104. agents/task/goals/board.py +690 -0
  105. agents/task/goals/completion_judge.py +247 -0
  106. agents/task/goals/context.py +62 -0
  107. agents/task/goals/dispatcher.py +848 -0
  108. agents/task/goals/escalation.py +102 -0
  109. agents/task/goals/planner.py +114 -0
  110. agents/task/logging_config.py +155 -0
  111. agents/task/path.py +991 -0
  112. agents/task/robust_parse_config.py +400 -0
  113. agents/task/runtime/__init__.py +0 -0
  114. agents/task/runtime/run_as_session.py +130 -0
  115. agents/task/runtime_safety.py +308 -0
  116. agents/task/session/__init__.py +5 -0
  117. agents/task/session/browser_pool.py +130 -0
  118. agents/task/session/cleanup.py +612 -0
  119. agents/task/session/execution.py +397 -0
  120. agents/task/session/feed.py +200 -0
  121. agents/task/session/hitl_ingress.py +201 -0
  122. agents/task/session/hooks.py +83 -0
  123. agents/task/session/multi_agent.py +243 -0
  124. agents/task/session/workspace.py +91 -0
  125. agents/task/session_registry.py +91 -0
  126. agents/task/session_route.py +35 -0
  127. agents/task/sqlite_session_registry.py +261 -0
  128. agents/task/surface_config.py +185 -0
  129. agents/task/telemetry/__init__.py +58 -0
  130. agents/task/telemetry/event_log.py +180 -0
  131. agents/task/telemetry/formatters.py +642 -0
  132. agents/task/telemetry/manager.py +571 -0
  133. agents/task/telemetry/memory_events.py +66 -0
  134. agents/task/telemetry/self_events.py +53 -0
  135. agents/task/telemetry/sequence.py +160 -0
  136. agents/task/telemetry/service.py +1265 -0
  137. agents/task/telemetry/views.py +882 -0
  138. agents/task/templates.py +98 -0
  139. agents/task/test_aux_chain.py +151 -0
  140. agents/task/test_aux_router.py +41 -0
  141. agents/task/tests/test_parse_robustness.py +170 -0
  142. agents/task/tool_defaults.py +110 -0
  143. agents/task/utils.py +762 -0
  144. agents/task/utils_json.py +768 -0
  145. agents/task/utils_webview.py +86 -0
  146. agents/task/workspace_context.py +229 -0
  147. agents/task_agent_lite.py +2398 -0
  148. api/README.md +683 -0
  149. api/TASK_API_DOCS.md +151 -0
  150. api/__init__.py +0 -0
  151. api/a2a/__init__.py +37 -0
  152. api/a2a/agent_card.py +337 -0
  153. api/a2a/client.py +657 -0
  154. api/a2a/endpoints.py +375 -0
  155. api/a2a/models.py +268 -0
  156. api/a2a/streaming.py +419 -0
  157. api/a2a/task_handler.py +929 -0
  158. api/admin_endpoints.py +1584 -0
  159. api/app.py +923 -0
  160. api/auth_constants.py +28 -0
  161. api/auth_endpoints.py +402 -0
  162. api/auth_state.py +36 -0
  163. api/chat_via_task.py +42 -0
  164. api/conversation_manager.py +231 -0
  165. api/dependencies.py +186 -0
  166. api/eip8004_endpoints.py +452 -0
  167. api/hyperliquid_models.py +174 -0
  168. api/hyperliquid_routes.py +450 -0
  169. api/interfaces.py +207 -0
  170. api/jwt_middleware.py +83 -0
  171. api/kb/__init__.py +1 -0
  172. api/kb/endpoints.py +314 -0
  173. api/mcp_models.py +279 -0
  174. api/mcp_routes.py +438 -0
  175. api/middleware.py +526 -0
  176. api/models.py +176 -0
  177. api/openai_compat/__init__.py +0 -0
  178. api/openai_compat/model_map.py +25 -0
  179. api/openai_compat/models.py +59 -0
  180. api/openai_compat/router.py +140 -0
  181. api/payment_endpoints.py +444 -0
  182. api/payment_verification.py +138 -0
  183. api/polymarket_models.py +155 -0
  184. api/polymarket_routes.py +501 -0
  185. api/pricing_endpoints.py +89 -0
  186. api/session_routing.py +54 -0
  187. api/skill_endpoints.py +453 -0
  188. api/task_http_api.py +1856 -0
  189. api/webhooks.py +52 -0
  190. api/x402_endpoints.py +280 -0
  191. avatar/README.md +44 -0
  192. avatar/__init__.py +1 -0
  193. avatar/config/rob.json +7 -0
  194. avatar/mindprint.js +395 -0
  195. avatar/renders/rob.meta.json +20 -0
  196. avatar/renders/rob.png +0 -0
  197. avatar/studio.html +373 -0
  198. avatar/webview/avatar-live.js +44 -0
  199. cli/README.md +89 -0
  200. cli/__init__.py +0 -0
  201. cli/cli_surface.py +67 -0
  202. cli/commands/__init__.py +0 -0
  203. cli/commands/_bootstrap.py +49 -0
  204. cli/commands/_errors.py +53 -0
  205. cli/commands/chat.py +973 -0
  206. cli/commands/config.py +100 -0
  207. cli/commands/dashboard.py +103 -0
  208. cli/commands/doctor.py +222 -0
  209. cli/commands/email.py +174 -0
  210. cli/commands/gateway.py +346 -0
  211. cli/commands/goals.py +509 -0
  212. cli/commands/init.py +276 -0
  213. cli/commands/journey.py +30 -0
  214. cli/commands/kb.py +280 -0
  215. cli/commands/model.py +107 -0
  216. cli/commands/owner.py +431 -0
  217. cli/commands/pfp.py +232 -0
  218. cli/commands/run.py +414 -0
  219. cli/commands/serve.py +41 -0
  220. cli/commands/session.py +684 -0
  221. cli/commands/skill_install.py +786 -0
  222. cli/commands/skills.py +128 -0
  223. cli/commands/subagents.py +100 -0
  224. cli/commands/surface.py +81 -0
  225. cli/commands/telegram.py +185 -0
  226. cli/commands/todos.py +209 -0
  227. cli/commands/tools.py +102 -0
  228. cli/commands/update.py +316 -0
  229. cli/commands/whatsapp.py +175 -0
  230. cli/config_store.py +276 -0
  231. cli/gitignore.py +43 -0
  232. cli/inventory.py +46 -0
  233. cli/keys.py +101 -0
  234. cli/persona.py +17 -0
  235. cli/polyrob.py +137 -0
  236. cli/ui/__init__.py +64 -0
  237. cli/ui/activity.py +141 -0
  238. cli/ui/app.py +538 -0
  239. cli/ui/banner.py +233 -0
  240. cli/ui/blocks.py +534 -0
  241. cli/ui/bootstrap_notice.py +47 -0
  242. cli/ui/commands/__init__.py +41 -0
  243. cli/ui/commands/h_cron.py +64 -0
  244. cli/ui/commands/h_journey.py +164 -0
  245. cli/ui/commands/h_kb.py +121 -0
  246. cli/ui/commands/h_learn.py +100 -0
  247. cli/ui/commands/h_mcp.py +215 -0
  248. cli/ui/commands/h_self.py +113 -0
  249. cli/ui/commands/h_skills.py +314 -0
  250. cli/ui/commands/handlers.py +1672 -0
  251. cli/ui/commands/registry.py +426 -0
  252. cli/ui/dialog.py +425 -0
  253. cli/ui/event_registry.py +110 -0
  254. cli/ui/event_specs.py +45 -0
  255. cli/ui/events.py +433 -0
  256. cli/ui/identity.py +15 -0
  257. cli/ui/lifecycle.py +159 -0
  258. cli/ui/live_hooks.py +43 -0
  259. cli/ui/model_selector.py +532 -0
  260. cli/ui/persistent_loop.py +181 -0
  261. cli/ui/pick.py +113 -0
  262. cli/ui/plain_renderer.py +351 -0
  263. cli/ui/renderer.py +384 -0
  264. cli/ui/rich_renderer.py +533 -0
  265. cli/ui/secrets.py +115 -0
  266. cli/ui/state.py +376 -0
  267. cli/ui/statusbar.py +231 -0
  268. cli/ui/streaming.py +254 -0
  269. cli/ui/terminal_render.py +105 -0
  270. cli/ui/theme.py +132 -0
  271. cli/update/__init__.py +6 -0
  272. cli/update/context.py +121 -0
  273. cli/update/detect.py +176 -0
  274. cli/update/engine.py +95 -0
  275. cli/update/migrate_guarded.py +55 -0
  276. cli/update/process_guard.py +215 -0
  277. cli/update/runners.py +78 -0
  278. cli/update/snapshot.py +270 -0
  279. cli/update/versions.py +205 -0
  280. core/README.md +377 -0
  281. core/__init__.py +174 -0
  282. core/assets.py +87 -0
  283. core/async_bridge.py +76 -0
  284. core/autonomy_runtime.py +259 -0
  285. core/base_component.py +302 -0
  286. core/bootstrap.py +681 -0
  287. core/bot.py +310 -0
  288. core/config.py +833 -0
  289. core/constants.py +126 -0
  290. core/container.py +462 -0
  291. core/db_manifest.py +101 -0
  292. core/embedding.py +57 -0
  293. core/env.py +39 -0
  294. core/exceptions.py +287 -0
  295. core/flags.py +144 -0
  296. core/flags_catalog.py +345 -0
  297. core/home_migration.py +62 -0
  298. core/identity.py +118 -0
  299. core/initialization.py +1004 -0
  300. core/instance.py +537 -0
  301. core/interactive_gate.py +96 -0
  302. core/logging.py +436 -0
  303. core/owner_doc_writer.py +176 -0
  304. core/pairing.py +193 -0
  305. core/path_safety.py +20 -0
  306. core/paths.py +27 -0
  307. core/payment_config.py +21 -0
  308. core/permissions.py +322 -0
  309. core/runtime_config.py +96 -0
  310. core/runtime_paths.py +135 -0
  311. core/seams.py +35 -0
  312. core/secret_scan.py +25 -0
  313. core/secret_scrub.py +71 -0
  314. core/secrets.py +35 -0
  315. core/security_logging_filter.py +148 -0
  316. core/self_context_writer.py +333 -0
  317. core/self_evolution.py +294 -0
  318. core/session_context.py +47 -0
  319. core/sqlite_util.py +59 -0
  320. core/surfaces/__init__.py +16 -0
  321. core/surfaces/access.py +118 -0
  322. core/surfaces/binding.py +88 -0
  323. core/surfaces/bootstrap.py +78 -0
  324. core/surfaces/circuit.py +168 -0
  325. core/surfaces/continuity.py +77 -0
  326. core/surfaces/correspondents.py +228 -0
  327. core/surfaces/dispatcher.py +216 -0
  328. core/surfaces/envelopes.py +75 -0
  329. core/surfaces/gc.py +30 -0
  330. core/surfaces/idempotency.py +57 -0
  331. core/surfaces/inbound_webhook.py +124 -0
  332. core/surfaces/media.py +32 -0
  333. core/surfaces/message_router.py +91 -0
  334. core/surfaces/outbound_allowlist.py +80 -0
  335. core/surfaces/outbound_dispatcher.py +110 -0
  336. core/surfaces/outbound_mirror.py +37 -0
  337. core/surfaces/outbound_queue.py +107 -0
  338. core/surfaces/outbound_target.py +12 -0
  339. core/surfaces/owner_admin.py +29 -0
  340. core/surfaces/proactive.py +78 -0
  341. core/surfaces/progress.py +108 -0
  342. core/surfaces/rate_bucket.py +21 -0
  343. core/surfaces/registry.py +51 -0
  344. core/surfaces/rendering.py +53 -0
  345. core/surfaces/send_policy.py +14 -0
  346. core/surfaces/serialize.py +24 -0
  347. core/surfaces/session_chat_registry.py +112 -0
  348. core/surfaces/session_policy.py +60 -0
  349. core/surfaces/surface.py +288 -0
  350. core/surfaces/transcription.py +88 -0
  351. core/surfaces/voice_echo.py +26 -0
  352. core/surfaces/voice_guard.py +23 -0
  353. core/tickers.py +174 -0
  354. core/tool_catalog.py +192 -0
  355. core/version.py +75 -0
  356. core/wallet/__init__.py +5 -0
  357. core/wallet/agent_wallet.py +59 -0
  358. core/wallet/audit_sink.py +61 -0
  359. core/wallet/config.py +70 -0
  360. core/wallet/factory.py +87 -0
  361. core/wallet/policy.py +104 -0
  362. core/wallet/signer.py +51 -0
  363. cron/README.md +65 -0
  364. cron/__init__.py +0 -0
  365. cron/delivery.py +277 -0
  366. cron/digest.py +124 -0
  367. cron/jobs.py +207 -0
  368. cron/runner.py +297 -0
  369. cron/schedule.py +194 -0
  370. cron/scheduler.py +201 -0
  371. cron/service.py +57 -0
  372. cron/wake_gate.py +223 -0
  373. data/__init__.py +1 -0
  374. data/prompts/autov2_prompts.json +87 -0
  375. data/prompts/skills/browser-automation/SKILL.md +43 -0
  376. data/prompts/skills/coding-workflow/SKILL.md +36 -0
  377. data/prompts/skills/crypto-trading-safety/SKILL.md +53 -0
  378. data/prompts/skills/document-writing/SKILL.md +33 -0
  379. data/prompts/skills/email-comms/SKILL.md +33 -0
  380. data/prompts/skills/file-data-ops/SKILL.md +35 -0
  381. data/prompts/skills/hyperliquid-account-review/SKILL.md +48 -0
  382. data/prompts/skills/hyperliquid-market-data/SKILL.md +52 -0
  383. data/prompts/skills/hyperliquid-trading/SKILL.md +59 -0
  384. data/prompts/skills/lead-research/SKILL.md +86 -0
  385. data/prompts/skills/market-research-brief/SKILL.md +104 -0
  386. data/prompts/skills/person-analyzer/SKILL.md +124 -0
  387. data/prompts/skills/polymarket-market-research/SKILL.md +54 -0
  388. data/prompts/skills/polymarket-portfolio-review/SKILL.md +50 -0
  389. data/prompts/skills/polymarket-trading/SKILL.md +55 -0
  390. data/prompts/skills/presentation-creator/SKILL.md +36 -0
  391. data/prompts/skills/project-analyzer/SKILL.md +169 -0
  392. data/prompts/skills/rules.json +268 -0
  393. data/prompts/skills/secret-handling/SKILL.md +20 -0
  394. data/prompts/skills/skill-authoring/SKILL.md +31 -0
  395. data/prompts/skills/skill-security-review/SKILL.md +27 -0
  396. data/prompts/skills/social-discovery/SKILL.md +57 -0
  397. data/prompts/skills/task-planning/SKILL.md +33 -0
  398. data/prompts/skills/web-research/SKILL.md +39 -0
  399. data/prompts/skills/web-scraping/SKILL.md +73 -0
  400. data/prompts/skills/x-engagement/SKILL.md +69 -0
  401. data/prompts/system_prompts.json +4 -0
  402. data/subscription_channels.json +5 -0
  403. modules/README.md +503 -0
  404. modules/__init__.py +124 -0
  405. modules/auth/__init__.py +13 -0
  406. modules/auth/api_key_manager.py +157 -0
  407. modules/auth/identity_mapper.py +380 -0
  408. modules/auth/siwe_auth.py +273 -0
  409. modules/auth/tier_manager.py +173 -0
  410. modules/base_module.py +190 -0
  411. modules/credits/__init__.py +29 -0
  412. modules/credits/balance_manager.py +237 -0
  413. modules/credits/cost_utils.py +129 -0
  414. modules/credits/pricing.py +139 -0
  415. modules/credits/unified_ledger.py +148 -0
  416. modules/credits/usage_meter.py +310 -0
  417. modules/credits/usage_tracker.py +674 -0
  418. modules/database/__init__.py +33 -0
  419. modules/database/audit_log.py +322 -0
  420. modules/database/auth_tables.py +276 -0
  421. modules/database/connection.py +644 -0
  422. modules/database/connection_pool.py +305 -0
  423. modules/database/conversation_contexts.py +446 -0
  424. modules/database/database_manager.py +237 -0
  425. modules/database/hyperliquid.py +375 -0
  426. modules/database/polymarket.py +574 -0
  427. modules/database/user_mcp_servers.py +728 -0
  428. modules/database/user_profiles.py +562 -0
  429. modules/database/utils.py +107 -0
  430. modules/database/x402_tables.py +92 -0
  431. modules/eip8004/README.md +1069 -0
  432. modules/eip8004/__init__.py +65 -0
  433. modules/eip8004/contracts.py +498 -0
  434. modules/eip8004/models.py +271 -0
  435. modules/eip8004/registration.py +148 -0
  436. modules/eip8004/reputation.py +367 -0
  437. modules/eip8004/validation.py +277 -0
  438. modules/llm/__init__.py +97 -0
  439. modules/llm/adapters.py +963 -0
  440. modules/llm/anthropic_client.py +831 -0
  441. modules/llm/available_models.py +106 -0
  442. modules/llm/brain_scrubber.py +137 -0
  443. modules/llm/cache_hints.py +162 -0
  444. modules/llm/deepseek_client.py +562 -0
  445. modules/llm/gemini_client.py +1367 -0
  446. modules/llm/llm_client.py +1139 -0
  447. modules/llm/llm_client_registry.py +188 -0
  448. modules/llm/llm_factory.py +237 -0
  449. modules/llm/llm_manager.py +1036 -0
  450. modules/llm/messages.py +374 -0
  451. modules/llm/model_registry.py +1935 -0
  452. modules/llm/nvidia_client.py +125 -0
  453. modules/llm/openai_client.py +800 -0
  454. modules/llm/openrouter_client.py +969 -0
  455. modules/llm/profiles.py +178 -0
  456. modules/llm/test_anthropic_cache_capture.py +35 -0
  457. modules/llm/test_cached_pricing_derivation.py +24 -0
  458. modules/llm/think_scrubber.py +444 -0
  459. modules/llm/token_counter.py +268 -0
  460. modules/memory/__init__.py +35 -0
  461. modules/memory/backend_factory.py +83 -0
  462. modules/memory/cache_manager.py +186 -0
  463. modules/memory/episodic.py +108 -0
  464. modules/memory/local_vector_memory_provider.py +552 -0
  465. modules/memory/memory_manager.py +225 -0
  466. modules/memory/models.py +312 -0
  467. modules/memory/provider.py +210 -0
  468. modules/memory/registry.py +205 -0
  469. modules/memory/sqlite_memory_provider.py +767 -0
  470. modules/memory/task/__init__.py +52 -0
  471. modules/memory/task/compaction_manager.py +157 -0
  472. modules/memory/task/context_retriever.py +809 -0
  473. modules/memory/task/hierarchical_memory.py +1002 -0
  474. modules/memory/task/lexical_retriever.py +71 -0
  475. modules/memory/task/null_context_manager.py +213 -0
  476. modules/memory/task/phase_manager.py +625 -0
  477. modules/memory/task/reflection_service.py +157 -0
  478. modules/memory/task/semantic_retriever.py +218 -0
  479. modules/memory/task/task_context_manager.py +1165 -0
  480. modules/memory/task/test_forgetting_engages.py +24 -0
  481. modules/memory/task/test_reflection_llm.py +39 -0
  482. modules/memory/task/test_threat_scan.py +53 -0
  483. modules/memory/task/threat_scan.py +90 -0
  484. modules/memory/test_sqlite_memory_provider.py +106 -0
  485. modules/memory/user_profile_manager.py +527 -0
  486. modules/payments/__init__.py +11 -0
  487. modules/payments/deposit_monitor.py +478 -0
  488. modules/payments/price_oracle.py +70 -0
  489. modules/payments/treasury_sweeper.py +401 -0
  490. modules/payments/wallet_generator.py +145 -0
  491. modules/pfp/__init__.py +1 -0
  492. modules/pfp/config.py +92 -0
  493. modules/pfp/mesh.py +565 -0
  494. modules/pfp/push.py +69 -0
  495. modules/pfp/renderer.py +85 -0
  496. modules/pfp/store.py +82 -0
  497. modules/skills/__init__.py +1 -0
  498. modules/skills/skill_usage.py +248 -0
  499. modules/transcription/__init__.py +24 -0
  500. modules/transcription/base.py +20 -0
  501. modules/transcription/faster_whisper_transcriber.py +55 -0
  502. modules/x402/README.md +249 -0
  503. modules/x402/__init__.py +22 -0
  504. modules/x402/invoicing.py +416 -0
  505. modules/x402/middleware.py +364 -0
  506. modules/x402/settlement_watcher.py +145 -0
  507. modules/x402/x402_integration.py +344 -0
  508. polyrob-0.5.0.dist-info/METADATA +482 -0
  509. polyrob-0.5.0.dist-info/RECORD +745 -0
  510. polyrob-0.5.0.dist-info/WHEEL +5 -0
  511. polyrob-0.5.0.dist-info/entry_points.txt +2 -0
  512. polyrob-0.5.0.dist-info/licenses/LICENSE +21 -0
  513. polyrob-0.5.0.dist-info/top_level.txt +12 -0
  514. surfaces/README.md +70 -0
  515. surfaces/__init__.py +0 -0
  516. surfaces/email/__init__.py +0 -0
  517. surfaces/email/dedup.py +44 -0
  518. surfaces/email/harness.py +187 -0
  519. surfaces/email/inbound.py +159 -0
  520. surfaces/email/seed.py +60 -0
  521. surfaces/email/surface.py +105 -0
  522. surfaces/telegram/__init__.py +0 -0
  523. surfaces/telegram/dedup.py +83 -0
  524. surfaces/telegram/harness.py +886 -0
  525. surfaces/telegram/inbound.py +145 -0
  526. surfaces/telegram/interactive_tools.py +49 -0
  527. surfaces/telegram/markdown.py +347 -0
  528. surfaces/telegram/rate_limit.py +167 -0
  529. surfaces/telegram/surface.py +187 -0
  530. surfaces/telegram/voice.py +52 -0
  531. surfaces/whatsapp/__init__.py +0 -0
  532. surfaces/whatsapp/client.py +51 -0
  533. surfaces/whatsapp/harness.py +62 -0
  534. surfaces/whatsapp/inbound.py +132 -0
  535. surfaces/whatsapp/surface.py +64 -0
  536. surfaces/whatsapp/window.py +34 -0
  537. tools/README.md +578 -0
  538. tools/__init__.py +424 -0
  539. tools/alchemy/__init__.py +5 -0
  540. tools/alchemy/alchemy_tool.py +389 -0
  541. tools/anysite/__init__.py +17 -0
  542. tools/anysite/client.py +88 -0
  543. tools/anysite/tool.py +89 -0
  544. tools/base_tool.py +468 -0
  545. tools/browser/__init__.py +22 -0
  546. tools/browser/actions.py +118 -0
  547. tools/browser/browser.py +1457 -0
  548. tools/browser/browser_manager.py +690 -0
  549. tools/browser/context.py +1748 -0
  550. tools/browser/playwright_utils.py +137 -0
  551. tools/browser/views.py +56 -0
  552. tools/code_exec/SANDBOX_SECURITY.md +144 -0
  553. tools/code_exec/__init__.py +178 -0
  554. tools/code_exec/backend.py +73 -0
  555. tools/code_exec/backends/__init__.py +1 -0
  556. tools/code_exec/backends/docker.py +779 -0
  557. tools/code_exec/backends/local_subprocess.py +154 -0
  558. tools/code_exec/env_policy.py +45 -0
  559. tools/code_exec/result.py +42 -0
  560. tools/code_exec/sandbox_guard.py +59 -0
  561. tools/code_exec/tool.py +227 -0
  562. tools/coding/__init__.py +50 -0
  563. tools/coding/edit.py +107 -0
  564. tools/coding/search.py +113 -0
  565. tools/coding/tool.py +353 -0
  566. tools/collabland/__init__.py +5 -0
  567. tools/collabland/collabland_tool.py +471 -0
  568. tools/controller/__init__.py +0 -0
  569. tools/controller/_helpers.py +165 -0
  570. tools/controller/action_registration.py +1765 -0
  571. tools/controller/approval.py +244 -0
  572. tools/controller/approval_interactive.py +90 -0
  573. tools/controller/delegation.py +195 -0
  574. tools/controller/execution.py +797 -0
  575. tools/controller/execution_context.py +141 -0
  576. tools/controller/hooks.py +135 -0
  577. tools/controller/introspection.py +344 -0
  578. tools/controller/mcp_registrar.py +190 -0
  579. tools/controller/message_send.py +31 -0
  580. tools/controller/registry/__init__.py +4 -0
  581. tools/controller/registry/schema_generators.py +395 -0
  582. tools/controller/registry/schema_sanitizer.py +406 -0
  583. tools/controller/registry/service.py +1266 -0
  584. tools/controller/registry/views.py +151 -0
  585. tools/controller/service.py +409 -0
  586. tools/controller/tool_management.py +334 -0
  587. tools/controller/types.py +28 -0
  588. tools/controller/views.py +450 -0
  589. tools/cronjob_tools.py +166 -0
  590. tools/crypto_trade_gate.py +59 -0
  591. tools/descriptors.py +452 -0
  592. tools/dom/__init__.py +0 -0
  593. tools/dom/history_tree_processor/service.py +244 -0
  594. tools/dom/history_tree_processor/view.py +99 -0
  595. tools/dom/service.py +471 -0
  596. tools/dom/views.py +346 -0
  597. tools/email_tool.py +403 -0
  598. tools/exceptions.py +169 -0
  599. tools/filesystem.py +1038 -0
  600. tools/filesystem_docproc.py +331 -0
  601. tools/filesystem_pdf.py +755 -0
  602. tools/git/__init__.py +42 -0
  603. tools/git/tool.py +258 -0
  604. tools/github/__init__.py +33 -0
  605. tools/github/client.py +94 -0
  606. tools/github/tool.py +237 -0
  607. tools/goal_tools.py +309 -0
  608. tools/hyperliquid/__init__.py +56 -0
  609. tools/hyperliquid/models.py +233 -0
  610. tools/hyperliquid/service.py +1680 -0
  611. tools/knowledge_ingest.py +950 -0
  612. tools/mcp/README.md +535 -0
  613. tools/mcp/__init__.py +52 -0
  614. tools/mcp/catalog.py +115 -0
  615. tools/mcp/config.py +337 -0
  616. tools/mcp/mcp_tool.py +1415 -0
  617. tools/mcp/param_coercion.py +289 -0
  618. tools/mcp/protocol.py +1520 -0
  619. tools/mcp/rate_limit.py +50 -0
  620. tools/mcp/security.py +424 -0
  621. tools/mcp/self_install.py +111 -0
  622. tools/mcp/server_manager.py +829 -0
  623. tools/mcp/subscriptions.py +72 -0
  624. tools/mcp/user_mcp_service.py +899 -0
  625. tools/mcp/validation_tracker.py +89 -0
  626. tools/mcp/views.py +346 -0
  627. tools/oauth/__init__.py +17 -0
  628. tools/oauth/manager.py +75 -0
  629. tools/oauth/provider.py +69 -0
  630. tools/oauth/providers/__init__.py +1 -0
  631. tools/oauth/providers/generic_oauth2.py +96 -0
  632. tools/perplexity_tool.py +280 -0
  633. tools/polymarket/__init__.py +41 -0
  634. tools/polymarket/clob_adapter.py +76 -0
  635. tools/polymarket/models.py +182 -0
  636. tools/polymarket/service.py +2082 -0
  637. tools/self_env/__init__.py +61 -0
  638. tools/self_env/tool.py +274 -0
  639. tools/shell/__init__.py +80 -0
  640. tools/shell/backend_pool.py +118 -0
  641. tools/shell/discipline.py +62 -0
  642. tools/shell/executor.py +157 -0
  643. tools/shell/loopback_allow.py +87 -0
  644. tools/shell/process_registry.py +113 -0
  645. tools/shell/process_tool.py +131 -0
  646. tools/shell/state.py +129 -0
  647. tools/shell/tool.py +151 -0
  648. tools/task_tool.py +637 -0
  649. tools/twitter_tool.py +2086 -0
  650. tools/user_directory.py +179 -0
  651. tools/web_fetch/__init__.py +3 -0
  652. tools/web_fetch/fetcher.py +144 -0
  653. tools/web_fetch/render.py +44 -0
  654. tools/web_fetch/tool.py +60 -0
  655. tools/x402/__init__.py +60 -0
  656. tools/x402/client.py +47 -0
  657. tools/x402/invoice_tool.py +143 -0
  658. tools/x402/real_client.py +250 -0
  659. tools/x402/service.py +131 -0
  660. utils/README.md +498 -0
  661. utils/__init__.py +46 -0
  662. utils/auth_utils.py +114 -0
  663. utils/bounded_collections.py +162 -0
  664. utils/circuit_breaker.py +389 -0
  665. utils/gif_utils.py +435 -0
  666. utils/markdown_utils.py +291 -0
  667. utils/message_utils.py +147 -0
  668. utils/metrics.py +36 -0
  669. utils/path_validator.py +227 -0
  670. utils/rate_limit_manager.py +399 -0
  671. utils/result_size.py +296 -0
  672. utils/time_utils.py +206 -0
  673. utils/user_utils.py +188 -0
  674. webview/README.md +111 -0
  675. webview/RENAME_AND_ALIGN_HANDOFF.md +200 -0
  676. webview/__init__.py +8 -0
  677. webview/activity.py +636 -0
  678. webview/owner_auth.py +108 -0
  679. webview/pages.py +448 -0
  680. webview/repair_sessions.py +399 -0
  681. webview/server.py +4323 -0
  682. webview/server_launcher.py +112 -0
  683. webview/static/css/activity.css +89 -0
  684. webview/static/css/chat.css +1772 -0
  685. webview/static/css/components.css +1190 -0
  686. webview/static/css/config-panel.css +651 -0
  687. webview/static/css/pages/admin.css +766 -0
  688. webview/static/css/pages/profile.css +231 -0
  689. webview/static/css/pages/settings.css +297 -0
  690. webview/static/css/pages/signin.css +316 -0
  691. webview/static/css/style.css +4250 -0
  692. webview/static/css/variables.css +147 -0
  693. webview/static/css/workspace-fullwidth-fix.css +109 -0
  694. webview/static/img/favicon.ico +0 -0
  695. webview/static/js/activity.js +394 -0
  696. webview/static/js/admin/activity.js +186 -0
  697. webview/static/js/admin/dashboard.js +163 -0
  698. webview/static/js/admin/user_detail.js +400 -0
  699. webview/static/js/admin/users.js +174 -0
  700. webview/static/js/admin/utils.js +214 -0
  701. webview/static/js/chat.js +3178 -0
  702. webview/static/js/config-panel.js +522 -0
  703. webview/static/js/constants.js +20 -0
  704. webview/static/js/error-handler.js +226 -0
  705. webview/static/js/ethers.min.js +1 -0
  706. webview/static/js/event-filter.js +137 -0
  707. webview/static/js/event-store.js +351 -0
  708. webview/static/js/file-attachments.js +374 -0
  709. webview/static/js/file-loader.js +163 -0
  710. webview/static/js/index.js +103 -0
  711. webview/static/js/performance-utils.js +196 -0
  712. webview/static/js/profile.js +396 -0
  713. webview/static/js/screenshot.js +386 -0
  714. webview/static/js/session.js +1527 -0
  715. webview/static/js/settings.js +1288 -0
  716. webview/static/js/sidebar-data.js +766 -0
  717. webview/static/js/sidebar-toggle.js +296 -0
  718. webview/static/js/socket.io.min.js +7 -0
  719. webview/static/js/stats.js +355 -0
  720. webview/static/js/ui-utils.js +420 -0
  721. webview/static/js/workspace.js +966 -0
  722. webview/stats_service.py +613 -0
  723. webview/templates/__init__.py +1 -0
  724. webview/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  725. webview/templates/activity.html +36 -0
  726. webview/templates/admin/activity.html +122 -0
  727. webview/templates/admin/dashboard.html +112 -0
  728. webview/templates/admin/user_detail.html +228 -0
  729. webview/templates/admin/users.html +105 -0
  730. webview/templates/autonomy.html +60 -0
  731. webview/templates/error.html +133 -0
  732. webview/templates/finance.html +90 -0
  733. webview/templates/identity.html +73 -0
  734. webview/templates/index.html +42 -0
  735. webview/templates/layout.html +125 -0
  736. webview/templates/memory.html +59 -0
  737. webview/templates/owner_login.html +15 -0
  738. webview/templates/profile.html +121 -0
  739. webview/templates/session.html +312 -0
  740. webview/templates/settings.html +534 -0
  741. webview/templates/sidebar.html +112 -0
  742. webview/templates/signin.html +213 -0
  743. webview/templates/status.html +14 -0
  744. webview/templates/system.html +45 -0
  745. webview/webgate.py +196 -0
@@ -0,0 +1,642 @@
1
+ """
2
+ Telemetry Feed Formatters
3
+
4
+ Extracts feed formatting logic from ProductTelemetry into specialized formatter classes.
5
+ Each formatter handles one event type, converting telemetry events into feed-friendly formats.
6
+
7
+ Architecture:
8
+ - BaseFeedFormatter: Abstract base for all formatters
9
+ - Concrete formatters: One per event type (AgentStepFormatter, LLMRequestFormatter, etc.)
10
+ - FeedFormatterRegistry: Maps event names to formatters
11
+ """
12
+
13
+ import logging
14
+ import time
15
+ from datetime import datetime
16
+ from typing import Dict, Any, Optional, List
17
+ from abc import ABC, abstractmethod
18
+
19
+ from agents.task.telemetry.views import BaseTelemetryEvent
20
+
21
+ # Import logger
22
+ from agents.task.logging_config import get_task_logger
23
+ logger = get_task_logger('telemetry.formatters')
24
+
25
+
26
+ class BaseFeedFormatter(ABC):
27
+ """Base class for feed formatters.
28
+
29
+ Each formatter converts a telemetry event into a feed-friendly format
30
+ for consumption by web UI, dashboards, etc.
31
+ """
32
+
33
+ @abstractmethod
34
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
35
+ """Format event into feed structure.
36
+
37
+ Args:
38
+ event: The telemetry event to format
39
+
40
+ Returns:
41
+ Dictionary with feed-friendly structure
42
+ """
43
+ pass
44
+
45
+ # Known tool/service names that namespace their actions
46
+ KNOWN_TOOLS = {
47
+ 'polymarket', 'mcp', 'twitter', 'email', 'perplexity',
48
+ 'filesystem', 'browser', 'collabland', 'alchemy', 'task'
49
+ }
50
+
51
+ @staticmethod
52
+ def _detect_service_for_action(action_name: str) -> str:
53
+ """Detect which service an action belongs to based on its name.
54
+
55
+ Uses a multi-tier detection strategy:
56
+ 1. Check for known tool prefix (e.g., 'polymarket_get_markets' → 'polymarket')
57
+ 2. Fall back to keyword-based detection for legacy actions
58
+ 3. Return 'default' if no match found
59
+
60
+ Args:
61
+ action_name: The name of the action
62
+
63
+ Returns:
64
+ The service name or 'default' if not detected
65
+ """
66
+ action_name_lower = action_name.lower()
67
+
68
+ # Strategy 1: Check for known tool prefix pattern (tool_action)
69
+ for tool in BaseFeedFormatter.KNOWN_TOOLS:
70
+ if action_name_lower.startswith(f"{tool}_"):
71
+ return tool
72
+
73
+ # Strategy 2: Keyword-based detection for actions without prefix
74
+ if 'perplexity' in action_name_lower or 'search_web' in action_name_lower:
75
+ return 'perplexity'
76
+ elif any(kw in action_name_lower for kw in ['document', 'doc_', 'extract_text', 'process_',
77
+ 'file', 'write_file', 'read_file', 'append_file',
78
+ 'delete_file', 'create_directory', 'list_directory']):
79
+ return 'filesystem'
80
+ elif any(kw in action_name_lower for kw in ['click', 'scroll', 'input', 'navigate', 'go_to',
81
+ 'browse_to', 'back', 'forward', 'reload', 'screenshot']):
82
+ return 'browser'
83
+
84
+ # Strategy 3: Try to extract prefix from first underscore
85
+ # e.g., 'custom_tool_action' would detect 'custom_tool' if underscore found
86
+ if '_' in action_name_lower:
87
+ potential_tool = action_name_lower.split('_')[0]
88
+ # Only use if it looks like a tool name (3+ chars, not a common verb)
89
+ if len(potential_tool) >= 3 and potential_tool not in {'get', 'set', 'add', 'del', 'run'}:
90
+ return potential_tool
91
+
92
+ return 'default'
93
+
94
+
95
+ class AgentStepFormatter(BaseFeedFormatter):
96
+ """Formatter for agent step events."""
97
+
98
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
99
+ """Format agent step event for feed."""
100
+ # Process actions for feed
101
+ processed_actions = []
102
+ try:
103
+ raw_actions = event.properties.get('actions', [])
104
+ if raw_actions:
105
+ for action in raw_actions:
106
+ try:
107
+ if isinstance(action, dict) and len(action) > 0:
108
+ action_keys = list(action.keys())
109
+ if not action_keys:
110
+ continue
111
+
112
+ action_name = action_keys[0]
113
+ service = self._detect_service_for_action(action_name)
114
+
115
+ # Strip service prefix from display name if present
116
+ # e.g., 'polymarket_get_markets' → 'get_markets' when service='polymarket'
117
+ display_name = action_name
118
+ if service != 'default':
119
+ prefix = f"{service}_"
120
+ if action_name.lower().startswith(prefix):
121
+ display_name = action_name[len(prefix):]
122
+
123
+ # Extract parameters safely
124
+ parameters = {}
125
+ if action_name in action and action[action_name] is not None:
126
+ parameters = action[action_name]
127
+
128
+ processed_action = {
129
+ 'action_type': action_name, # Keep full name for execution
130
+ 'name': display_name, # Clean name for display
131
+ 'service': service,
132
+ 'params': parameters
133
+ }
134
+ processed_actions.append(processed_action)
135
+ except Exception as e:
136
+ logger.debug(f"Error processing action: {e}")
137
+ continue
138
+ except Exception as e:
139
+ logger.debug(f"Error processing actions: {e}")
140
+
141
+ # Get context data if available
142
+ context_data = event.properties.get('context_data', {})
143
+
144
+ # Extract reasoning from multiple sources
145
+ reasoning = ""
146
+ if event.properties.get('reasoning'):
147
+ reasoning = event.properties.get('reasoning')
148
+ elif context_data and isinstance(context_data, dict):
149
+ # Check outputs for reasoning
150
+ if 'outputs' in context_data and isinstance(context_data['outputs'], dict):
151
+ reasoning = context_data['outputs'].get('reasoning', '')
152
+
153
+ # Check if there's any browser URL information
154
+ browser_url = None
155
+ if context_data and isinstance(context_data, dict):
156
+ # Check inputs and outputs for URL info
157
+ for section in ['inputs', 'outputs']:
158
+ if section in context_data and isinstance(context_data[section], dict):
159
+ section_data = context_data[section]
160
+ for key in ['url', 'page_url', 'current_url']:
161
+ if key in section_data and section_data[key]:
162
+ browser_url = section_data[key]
163
+ break
164
+ if browser_url:
165
+ break
166
+
167
+ # Check actions for URL information if we still don't have it
168
+ if not browser_url and processed_actions:
169
+ for action in processed_actions:
170
+ if action['service'] == 'browser' and action['name'] in ['navigate_to', 'open_tab']:
171
+ if 'params' in action and 'url' in action['params']:
172
+ browser_url = action['params']['url']
173
+ break
174
+
175
+ # Create step update data
176
+ update_data = {
177
+ 'type': 'step',
178
+ 'step': event.properties.get('step', 0),
179
+ 'timestamp': time.time(),
180
+ 'datetime': datetime.now().isoformat(),
181
+ 'data': {
182
+ 'actions': processed_actions,
183
+ 'errors': event.properties.get('step_error', []),
184
+ 'consecutive_failures': event.properties.get('consecutive_failures', 0),
185
+ 'agent_name': event.properties.get('agent_name', 'Unknown'),
186
+ 'agent_type': event.properties.get('agent_type', 'Unknown'),
187
+ 'task_progress': event.properties.get('task_progress', ''),
188
+ 'current_task': event.properties.get('current_task', ''),
189
+ 'reasoning': reasoning,
190
+ 'context': context_data,
191
+ # New iteration fields for enhanced UI rendering
192
+ 'iteration': event.properties.get('iteration') or event.properties.get('step', 0),
193
+ 'iteration_type': event.properties.get('iteration_type', 'mixed'),
194
+ 'iteration_status': event.properties.get('iteration_status', 'active'),
195
+ 'files_created': event.properties.get('files_created', []),
196
+ 'files_modified': event.properties.get('files_modified', []),
197
+ 'files_read': event.properties.get('files_read', []),
198
+ 'files_deleted': event.properties.get('files_deleted', []),
199
+ 'error_message': event.properties.get('error_message'),
200
+ 'loop_warning': event.properties.get('loop_warning'),
201
+ 'is_done': event.properties.get('is_done', False),
202
+ 'reasoning_synthesized': event.properties.get('reasoning_synthesized', False)
203
+ }
204
+ }
205
+
206
+ # Ensure agent name is extracted from agent_id if not explicitly provided
207
+ if update_data['data']['agent_name'] == 'Unknown' and event.properties.get('agent_id'):
208
+ agent_id = event.properties.get('agent_id')
209
+ if '_' in agent_id:
210
+ prefix = agent_id.split('_')[0]
211
+ update_data['data']['agent_name'] = prefix.capitalize()
212
+
213
+ # Add agent name at root level for backwards compatibility
214
+ update_data['agent_name'] = update_data['data']['agent_name']
215
+
216
+ # Add browser URL information if available
217
+ if browser_url:
218
+ update_data['data']['page_url'] = browser_url
219
+ update_data['data']['current_url'] = browser_url
220
+
221
+ return update_data
222
+
223
+
224
+ class LLMRequestFormatter(BaseFeedFormatter):
225
+ """Formatter for LLM request events."""
226
+
227
+ @staticmethod
228
+ def _detect_provider(model_name: str) -> str:
229
+ """Detect the provider based on model name."""
230
+ from agents.task.utils import detect_llm_provider
231
+ provider = detect_llm_provider(None, model_name)
232
+ return 'unknown' if provider == 'generic' else provider
233
+
234
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
235
+ """Format LLM request event for feed."""
236
+ return {
237
+ 'type': 'llm_request',
238
+ 'timestamp': time.time(),
239
+ 'datetime': datetime.now().isoformat(),
240
+ 'data': {
241
+ 'component': event.properties.get('component', 'Unknown'),
242
+ 'purpose': event.properties.get('purpose', 'Unknown'),
243
+ 'model_name': event.properties.get('model_name', 'Unknown'),
244
+ 'provider': self._detect_provider(event.properties.get('model_name', '')),
245
+ 'duration_seconds': event.properties.get('duration_seconds', 0),
246
+ 'success': event.properties.get('success', False),
247
+ 'token_count': event.properties.get('token_count', None),
248
+ 'prompt_tokens': event.properties.get('prompt_tokens', None),
249
+ 'completion_tokens': event.properties.get('completion_tokens', None),
250
+ 'cost_estimate': event.properties.get('cost_estimate', None),
251
+ 'agent_id': event.properties.get('agent_id', None),
252
+ 'request_id': event.properties.get('request_id', None)
253
+ }
254
+ }
255
+
256
+
257
+ class SessionStartFormatter(BaseFeedFormatter):
258
+ """Formatter for session start events."""
259
+
260
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
261
+ """Format session start event for feed."""
262
+ task = event.properties.get('task', '')
263
+
264
+ # If task is empty, try to get it from event directly
265
+ if not task and hasattr(event, 'task'):
266
+ task = event.task
267
+
268
+ session_data = {
269
+ 'session_id': event.properties.get('session_id', 'unknown'),
270
+ 'task': task,
271
+ 'model_name': event.properties.get('model_name', 'Unknown'),
272
+ 'agent_id': event.properties.get('agent_id', ''),
273
+ 'agent_type': event.properties.get('agent_type', ''),
274
+ 'use_vision': event.properties.get('use_vision', False),
275
+ 'internal_planning': event.properties.get('internal_planning', True)
276
+ }
277
+
278
+ return {
279
+ 'type': 'session_start',
280
+ 'timestamp': time.time(),
281
+ 'datetime': datetime.now().isoformat(),
282
+ 'data': session_data
283
+ }
284
+
285
+
286
+ class MultiAgentRelationshipFormatter(BaseFeedFormatter):
287
+ """Formatter for multi-agent relationship events."""
288
+
289
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
290
+ """Format multi-agent relationship event for feed."""
291
+ relationship_data = event.properties
292
+
293
+ # Build enhanced agent data
294
+ agent_details = []
295
+ agent_types = relationship_data.get('agent_types', {})
296
+ agent_sequence = relationship_data.get('execution_sequence',
297
+ relationship_data.get('agent_sequence', []))
298
+
299
+ for agent_id in relationship_data.get('agent_ids', []):
300
+ agent_type = agent_types.get(agent_id, "Unknown")
301
+
302
+ agent_name = "Unknown"
303
+ if '_' in agent_id:
304
+ prefix = agent_id.split('_')[0]
305
+ agent_name = prefix.capitalize()
306
+
307
+ sequence_position = -1
308
+ if agent_id in agent_sequence:
309
+ sequence_position = agent_sequence.index(agent_id)
310
+
311
+ agent_details.append({
312
+ 'id': agent_id,
313
+ 'agent_id': agent_id,
314
+ 'name': agent_name,
315
+ 'agent_name': agent_name,
316
+ 'type': agent_type,
317
+ 'agent_type': agent_type,
318
+ 'model': relationship_data.get('agent_models', {}).get(agent_id, 'Unknown'),
319
+ 'sequence_position': sequence_position
320
+ })
321
+
322
+ # Sort by sequence position
323
+ agent_details.sort(key=lambda x: x['sequence_position'] if x['sequence_position'] >= 0 else 999)
324
+
325
+ return {
326
+ 'type': 'multi_agent_relationship',
327
+ 'timestamp': time.time(),
328
+ 'datetime': datetime.now().isoformat(),
329
+ 'data': {
330
+ 'agent_ids': relationship_data.get('agent_ids', []),
331
+ 'agent_types': relationship_data.get('agent_types', {}),
332
+ 'agent_sequence': relationship_data.get('agent_sequence', []),
333
+ 'agent_models': relationship_data.get('agent_models', {}),
334
+ 'orchestrator_type': relationship_data.get('orchestrator_type', 'default'),
335
+ 'agent_details': agent_details
336
+ }
337
+ }
338
+
339
+
340
+ class ControllerRegisteredFunctionsFormatter(BaseFeedFormatter):
341
+ """Formatter for controller registered functions events.
342
+
343
+ Transforms internal controller_registered_functions events into
344
+ user-friendly available_actions format for the webview services panel.
345
+ """
346
+
347
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
348
+ """Format controller registered functions as available actions."""
349
+ properties = event.properties
350
+ registered_functions = properties.get('registered_functions', [])
351
+
352
+ # Group functions by service
353
+ by_service = {}
354
+ for func in registered_functions:
355
+ if isinstance(func, dict):
356
+ service = func.get('service', 'default')
357
+ action_name = func.get('name', 'unknown')
358
+ else:
359
+ # Handle RegisteredFunction dataclass
360
+ service = getattr(func, 'service', 'default')
361
+ action_name = getattr(func, 'name', 'unknown')
362
+
363
+ if service not in by_service:
364
+ by_service[service] = []
365
+ by_service[service].append(action_name)
366
+
367
+ return {
368
+ 'type': 'available_actions',
369
+ 'timestamp': time.time(),
370
+ 'datetime': datetime.now().isoformat(),
371
+ 'data': {
372
+ 'by_service': by_service,
373
+ 'total_actions': len(registered_functions)
374
+ }
375
+ }
376
+
377
+
378
+ class ToolExecutionFormatter(BaseFeedFormatter):
379
+ """Formatter for tool execution events."""
380
+
381
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
382
+ """Format tool execution event for feed."""
383
+ return {
384
+ 'type': 'tool_execution',
385
+ 'timestamp': time.time(),
386
+ 'datetime': datetime.now().isoformat(),
387
+ 'step': event.properties.get('step', 0),
388
+ 'data': {
389
+ 'tool_name': event.properties.get('tool_name', 'Unknown'),
390
+ 'action_name': event.properties.get('action_name', 'Unknown'),
391
+ 'success': event.properties.get('success', False),
392
+ 'duration_seconds': event.properties.get('duration_seconds', 0),
393
+ 'error': event.properties.get('error'),
394
+ 'result_size': event.properties.get('result_size'),
395
+ 'result_truncated': event.properties.get('result_truncated', False),
396
+ 'result_preview': event.properties.get('result_preview'),
397
+ 'parameters': event.properties.get('parameters', {})
398
+ }
399
+ }
400
+
401
+
402
+ class ErrorFormatter(BaseFeedFormatter):
403
+ """Formatter for error events."""
404
+
405
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
406
+ """Format error event for feed."""
407
+ return {
408
+ 'type': 'error',
409
+ 'timestamp': time.time(),
410
+ 'datetime': datetime.now().isoformat(),
411
+ 'step': event.properties.get('step', 0),
412
+ 'data': {
413
+ 'error_type': event.properties.get('error_type', 'Unknown'),
414
+ 'error_message': event.properties.get('error_message', ''),
415
+ 'error_stack': event.properties.get('error_stack'),
416
+ 'recoverable': event.properties.get('recoverable', True),
417
+ 'context': event.properties.get('context', {})
418
+ }
419
+ }
420
+
421
+
422
+ class SessionCompletionFormatter(BaseFeedFormatter):
423
+ """Formatter for session completion events."""
424
+
425
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
426
+ """Format session completion event for feed."""
427
+ return {
428
+ 'type': 'session_completion',
429
+ 'timestamp': time.time(),
430
+ 'datetime': datetime.now().isoformat(),
431
+ 'data': {
432
+ 'success': event.properties.get('success', False),
433
+ 'total_steps': event.properties.get('total_steps', 0),
434
+ 'duration_seconds': event.properties.get('duration_seconds', 0),
435
+ 'error_message': event.properties.get('error_message'),
436
+ 'metrics': event.properties.get('metrics', {})
437
+ }
438
+ }
439
+
440
+
441
+ class GenericEventFormatter(BaseFeedFormatter):
442
+ """Formatter for unrecognized/generic events.
443
+
444
+ Provides a simple envelope with raw properties so UI can display unknown events.
445
+ """
446
+
447
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
448
+ """Format generic event for feed."""
449
+ return {
450
+ 'type': event.name,
451
+ 'timestamp': time.time(),
452
+ 'datetime': datetime.now().isoformat(),
453
+ 'data': dict(event.properties) if hasattr(event, 'properties') else {}
454
+ }
455
+
456
+
457
+ class UserMessageFormatter(BaseFeedFormatter):
458
+ """Formatter for user message feed events.
459
+
460
+ Formats user messages for display in the chat UI.
461
+ CRITICAL: Output type MUST be 'user_message' to match chat.js handleUserMessage().
462
+ """
463
+
464
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
465
+ """Format user message event for chat display."""
466
+ return {
467
+ 'type': 'user_message',
468
+ 'timestamp': time.time(),
469
+ 'datetime': datetime.now().isoformat(),
470
+ 'data': {
471
+ 'text': event.properties.get('text', ''),
472
+ 'message': event.properties.get('text', ''), # Alias for compatibility
473
+ 'kind': event.properties.get('kind', 'comment'),
474
+ 'state': event.properties.get('state', 'queued'),
475
+ 'metadata': event.properties.get('metadata', {})
476
+ }
477
+ }
478
+
479
+
480
+ class QueueStatusFormatter(BaseFeedFormatter):
481
+ """Formatter for queue status events.
482
+
483
+ Formats queue status updates for the chat UI queue indicator.
484
+ """
485
+
486
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
487
+ """Format queue status event for UI updates."""
488
+ return {
489
+ 'type': 'queue_status',
490
+ 'timestamp': time.time(),
491
+ 'datetime': datetime.now().isoformat(),
492
+ 'data': {
493
+ 'queued_count': event.properties.get('queued_count', 0),
494
+ 'processing': event.properties.get('processing', False),
495
+ 'oldest_message_age_seconds': event.properties.get('oldest_message_age_seconds')
496
+ }
497
+ }
498
+
499
+
500
+ class SessionPausedFormatter(BaseFeedFormatter):
501
+ """Formatter for session paused events."""
502
+
503
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
504
+ """Format session paused event for feed."""
505
+ return {
506
+ 'type': 'session_paused',
507
+ 'timestamp': time.time(),
508
+ 'datetime': datetime.now().isoformat(),
509
+ 'data': {
510
+ 'session_id': event.properties.get('session_id', ''),
511
+ 'reason': event.properties.get('reason', 'user_interrupt'),
512
+ 'paused_by': event.properties.get('paused_by', 'user'),
513
+ 'status': 'paused'
514
+ }
515
+ }
516
+
517
+
518
+ class SessionResumedFormatter(BaseFeedFormatter):
519
+ """Formatter for session resumed events."""
520
+
521
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
522
+ """Format session resumed event for feed."""
523
+ return {
524
+ 'type': 'session_resumed',
525
+ 'timestamp': time.time(),
526
+ 'datetime': datetime.now().isoformat(),
527
+ 'data': {
528
+ 'session_id': event.properties.get('session_id', ''),
529
+ 'resumed_by': event.properties.get('resumed_by', 'user'),
530
+ 'status': 'running'
531
+ }
532
+ }
533
+
534
+
535
+ class SessionStatusFormatter(BaseFeedFormatter):
536
+ """Formatter for generic session status events."""
537
+
538
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
539
+ """Format session status event for feed."""
540
+ return {
541
+ 'type': 'status',
542
+ 'timestamp': time.time(),
543
+ 'datetime': datetime.now().isoformat(),
544
+ 'data': {
545
+ 'session_id': event.properties.get('session_id', ''),
546
+ 'status': event.properties.get('status', 'unknown'),
547
+ 'previous_status': event.properties.get('previous_status'),
548
+ 'reason': event.properties.get('reason')
549
+ }
550
+ }
551
+
552
+
553
+ class IterationCompleteFormatter(BaseFeedFormatter):
554
+ """Formatter for iteration complete events.
555
+
556
+ Provides a clean boundary marker for the UI to render iteration summaries
557
+ with file chips and status indicators.
558
+ """
559
+
560
+ def format(self, event: BaseTelemetryEvent) -> Dict[str, Any]:
561
+ """Format iteration complete event for feed."""
562
+ props = event.properties
563
+
564
+ return {
565
+ 'type': 'iteration_complete',
566
+ 'timestamp': time.time(),
567
+ 'datetime': datetime.now().isoformat(),
568
+ 'data': {
569
+ 'iteration': props.get('iteration', 0),
570
+ 'step': props.get('step', 0),
571
+ 'iteration_type': props.get('iteration_type', 'mixed'),
572
+ 'iteration_status': props.get('iteration_status', 'completed'),
573
+ 'reasoning_summary': props.get('reasoning_summary', ''),
574
+ 'actions': props.get('actions_executed', []),
575
+ 'action_results': props.get('action_results', []),
576
+ 'files_created': props.get('files_created', []),
577
+ 'files_modified': props.get('files_modified', []),
578
+ 'files_read': props.get('files_read', []),
579
+ 'files_deleted': props.get('files_deleted', []),
580
+ 'success': props.get('success', True),
581
+ 'error': props.get('error'),
582
+ 'is_done': props.get('is_done', False),
583
+ 'duration_seconds': props.get('duration_seconds', 0.0)
584
+ }
585
+ }
586
+
587
+
588
+ class FeedFormatterRegistry:
589
+ """Registry mapping event names to formatters."""
590
+
591
+ def __init__(self):
592
+ self._formatters: Dict[str, BaseFeedFormatter] = {
593
+ 'agent_step': AgentStepFormatter(),
594
+ 'llm_request': LLMRequestFormatter(),
595
+ 'session_start': SessionStartFormatter(),
596
+ 'session_completion': SessionCompletionFormatter(),
597
+ 'multi_agent_relationship': MultiAgentRelationshipFormatter(),
598
+ 'multi_agent_relationship_detailed': MultiAgentRelationshipFormatter(),
599
+ 'session_relationship': MultiAgentRelationshipFormatter(),
600
+ 'controller_registered_functions': ControllerRegisteredFunctionsFormatter(),
601
+ 'tool_execution': ToolExecutionFormatter(),
602
+ 'error': ErrorFormatter(),
603
+ # User message and queue status formatters for chat UI
604
+ 'user_message': UserMessageFormatter(),
605
+ 'queue_status': QueueStatusFormatter(),
606
+ # Session state change formatters
607
+ 'session_paused': SessionPausedFormatter(),
608
+ 'session_resumed': SessionResumedFormatter(),
609
+ 'status': SessionStatusFormatter(),
610
+ # Iteration complete formatter for enhanced UI
611
+ 'iteration_complete': IterationCompleteFormatter(),
612
+ }
613
+ self._generic_formatter = GenericEventFormatter()
614
+
615
+ def get_formatter(self, event_name: str) -> BaseFeedFormatter:
616
+ """Get formatter for event name.
617
+
618
+ Args:
619
+ event_name: The name of the event type
620
+
621
+ Returns:
622
+ Formatter for the event, or generic formatter if not registered
623
+ """
624
+ return self._formatters.get(event_name, self._generic_formatter)
625
+
626
+ def register_formatter(self, event_name: str, formatter: BaseFeedFormatter) -> None:
627
+ """Register a custom formatter.
628
+
629
+ Args:
630
+ event_name: The event name to register formatter for
631
+ formatter: The formatter instance
632
+ """
633
+ self._formatters[event_name] = formatter
634
+
635
+
636
+ # Global registry instance
637
+ _registry = FeedFormatterRegistry()
638
+
639
+
640
+ def get_formatter_registry() -> FeedFormatterRegistry:
641
+ """Get the global formatter registry."""
642
+ return _registry