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,397 @@
1
+ """Agent-creation + session-execution mixin (roadmap P9; code-motion from orchestrator.py).
2
+
3
+ create_agent (build + register an Agent) and execute_session (run the session loop),
4
+ extracted as whole methods so orchestrator.py drops under 700L. SessionOrchestrator
5
+ composes SessionExecutionMixin; callers (task_agent_lite, sub_agent_manager) use
6
+ orchestrator.create_agent / execute_session unchanged via MRO.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import json
12
+ import time
13
+ from typing import Any, Dict, List, Optional
14
+
15
+ # Module-level Agent import (no cycle: service does not import orchestrator, and
16
+ # service is loaded before this module during orchestrator import).
17
+ from agents.task.agent.service import Agent
18
+ from core.exceptions import LLMError, LLMPermanentError, LLMProviderExhaustedError
19
+
20
+
21
+ def _result_session_status(result) -> str:
22
+ """Map an agent.run() result (AgentHistoryList) to a session status.
23
+
24
+ Live-test F7b: a permanent LLM/billing error halts the loop *inside* the agent
25
+ (error_recovery sets a final ActionResult with .error and is_done=True), so
26
+ agent.run() returns a truthy history and the old `"completed" if result` logic
27
+ mislabeled the halt as a success. A terminal result carrying .error is an
28
+ 'error', not 'completed'. A genuine done() has error=None, so success is
29
+ unaffected. Falsy result → 'failed' (unchanged).
30
+ """
31
+ if not result:
32
+ return "failed"
33
+ try:
34
+ last = result.history[-1].result[-1]
35
+ if getattr(last, "error", None):
36
+ return "error"
37
+ except (AttributeError, IndexError, TypeError):
38
+ pass
39
+ return "completed"
40
+
41
+
42
+ class SessionExecutionMixin:
43
+ """Agent creation + session execution for SessionOrchestrator."""
44
+
45
+ async def create_agent(self,
46
+ task: str,
47
+ llm: Any = None,
48
+ agent_name: str = "agent",
49
+ profile_id: Optional[str] = None,
50
+ profile_overrides: Optional[Dict[str, Any]] = None,
51
+ share_controller: bool = True,
52
+ **kwargs) -> Any:
53
+ """Create an agent for this session.
54
+
55
+ Args:
56
+ task: Task description for the agent
57
+ llm: Language model to use
58
+ agent_name: Name for the agent
59
+ profile_id: Optional profile ID to use
60
+ profile_overrides: Optional overrides for the profile
61
+ share_controller: Whether to share the controller with other agents
62
+ **kwargs: Additional agent configuration
63
+
64
+ Returns:
65
+ Configured Agent instance
66
+ """
67
+ from agents.task.agent.service import Agent
68
+
69
+ self.logger.info(f"Creating agent '{agent_name}' for session {self.session_id}")
70
+
71
+ # CRITICAL FIX: If no LLM provided, get default from LLMManager
72
+ # This ensures sub-agents created via SubAgentManager get a valid LLM
73
+ if llm is None:
74
+ self.logger.info(f"No LLM provided for agent '{agent_name}', getting default from LLMManager")
75
+ try:
76
+ if self.container:
77
+ llm_manager = self.container.get_service('llm')
78
+ if llm_manager:
79
+ # Get default LLM using the primary provider
80
+ # Use fallback method to get any available provider
81
+ llm = await llm_manager.get_fallback_chat_model(
82
+ exclude_providers=[], # Don't exclude any providers
83
+ temperature=0.0
84
+ )
85
+ if llm:
86
+ self.logger.info(f"✅ Got default LLM for agent '{agent_name}': {getattr(llm, 'model_name', 'unknown')}")
87
+ else:
88
+ self.logger.error(f"❌ LLMManager returned no available LLM for agent '{agent_name}'")
89
+ raise RuntimeError(f"No LLM available for agent '{agent_name}'. Check your API keys.")
90
+ else:
91
+ self.logger.error(f"❌ LLMManager not found in container for agent '{agent_name}'")
92
+ raise RuntimeError(f"LLMManager not available in container for agent '{agent_name}'")
93
+ else:
94
+ self.logger.error(f"❌ No container available to get LLM for agent '{agent_name}'")
95
+ raise RuntimeError(f"No container available to get LLM for agent '{agent_name}'")
96
+ except Exception as e:
97
+ self.logger.error(f"Failed to get default LLM for agent '{agent_name}': {e}", exc_info=True)
98
+ raise RuntimeError(f"Cannot create agent '{agent_name}' without LLM: {e}")
99
+
100
+ # Generate unique agent ID (matches what Agent class will set)
101
+ agent_id = f"{agent_name}_{self.session_id}"
102
+
103
+ # Prepare agent configuration with all required parameters
104
+ # NOTE: Browser context obtained on-demand via orchestrator.get_browser_context()
105
+ agent_config = {
106
+ "agent_name": agent_name,
107
+ "task": task,
108
+ "llm": llm,
109
+ "orchestrator": self, # Orchestrator provides session_id and user_id (single source of truth)
110
+ **kwargs
111
+ }
112
+ # session_id and user_id removed - Agent gets them from orchestrator
113
+
114
+ # Note: Controller is accessed via self.orchestrator.controller in Agent.__init__
115
+ # No need to pass it explicitly as a parameter
116
+
117
+ # Add session_config if available
118
+ if hasattr(self, 'session_config') and self.session_config:
119
+ agent_config["session_config"] = self.session_config
120
+
121
+ # S1/S2 (chat consolidation): forward the chat-mode persona block set on
122
+ # the orchestrator (by TaskAgent.chat_once) into AgentConfig so the agent's
123
+ # SystemPrompt <identity> carries the character. None => byte-identical.
124
+ _persona = getattr(self, '_persona_block', None)
125
+ if _persona:
126
+ agent_config["persona_block"] = _persona
127
+
128
+ # Use profile if specified
129
+ if profile_id:
130
+ agent_config["profile_id"] = profile_id
131
+ if profile_overrides:
132
+ agent_config["profile_overrides"] = profile_overrides
133
+
134
+ # Create the agent
135
+ try:
136
+ agent = Agent.from_params(**agent_config)
137
+
138
+ # Validate requested tools are available
139
+ if hasattr(self, 'session_config') and self.session_config and isinstance(self.session_config, dict):
140
+ requested_tools = self.session_config.get('tools', [])
141
+ if requested_tools:
142
+ available_tools = self.controller.list_tools()
143
+ missing_tools = [t for t in requested_tools if t not in available_tools]
144
+ if missing_tools:
145
+ self.logger.warning(f"Agent '{agent_name}' missing requested tools: {missing_tools}")
146
+
147
+ # Track the agent
148
+ self.agents[agent_id] = agent
149
+ self.agent_types[agent_id] = profile_id or "executor"
150
+ self.agent_names[agent_id] = agent_name
151
+ import time
152
+ self.agent_creation_times[agent_id] = time.time()
153
+ # Store model name using agent property (after agent is created)
154
+ self.agent_models[agent_id] = agent.model_name
155
+
156
+ # Register agent with SessionManager (centralized registration)
157
+ # Skip for sub-agents - they're ephemeral and shouldn't pollute session registry
158
+ is_sub_agent = kwargs.get('is_sub_agent', False)
159
+ if self.session_manager and not is_sub_agent:
160
+ try:
161
+ self.session_manager.register_agent(
162
+ session_id=self.session_id,
163
+ agent_name=agent_name,
164
+ agent_id=agent_id,
165
+ agent_type=type(agent).__name__,
166
+ model_name=agent.model_name, # Use agent property (delegates to MessageManager)
167
+ user_id=self.user_id,
168
+ role="executor"
169
+ )
170
+ self.logger.debug(f"Registered agent '{agent_name}' with SessionManager")
171
+
172
+ # Emit telemetry for agent creation using TelemetryManager
173
+ try:
174
+ self.telemetry_manager.capture_agent_registration(
175
+ agent_id=agent_id,
176
+ agent_name=agent_name,
177
+ agent_type=type(agent).__name__,
178
+ model_name=agent.model_name, # Use agent property (delegates to MessageManager)
179
+ task=task
180
+ )
181
+ except Exception as te:
182
+ self.logger.debug(f"Failed to emit agent registration telemetry: {te}")
183
+ except Exception as e:
184
+ self.logger.warning(f"Failed to register agent with SessionManager: {e}")
185
+ elif is_sub_agent:
186
+ self.logger.debug(f"Skipped SessionManager registration for sub-agent '{agent_name}'")
187
+
188
+ # Register streaming callback if one was supplied (server layer
189
+ # provides the transport; core mode leaves _on_stream_chunk None).
190
+ if self._on_stream_chunk is not None:
191
+ await self._register_stream_callback(agent)
192
+
193
+ # CRITICAL FIX: Flush pending messages to newly created agent
194
+ # This ensures messages queued before agent existed are delivered
195
+ # SECURITY FIX: Use lock to prevent race with submit_user_message
196
+ async with self._pending_messages_lock:
197
+ if self._pending_messages and hasattr(agent, 'hitl_manager') and agent.hitl_manager:
198
+ pending_count = len(self._pending_messages)
199
+ image_count = sum(
200
+ 1 for _, _, meta in self._pending_messages
201
+ if meta and meta.get('image_attachments')
202
+ )
203
+ self.logger.info(
204
+ f"📦 Flushing {pending_count} pending message(s) to agent {agent_id} "
205
+ f"({image_count} with images)"
206
+ )
207
+ for text, kind, metadata in self._pending_messages:
208
+ await agent.hitl_manager.queue_user_message(text, kind, metadata)
209
+ has_images = bool(metadata and metadata.get('image_attachments'))
210
+ self.logger.debug(f" → Delivered pending message (has_images={has_images})")
211
+ self._pending_messages.clear()
212
+ self.logger.info(f"✅ All pending messages delivered to agent")
213
+
214
+ self.logger.info(f"✅ Created agent '{agent_name}' with ID {agent_id}")
215
+ return agent
216
+
217
+ except Exception as e:
218
+ self.logger.error(f"Failed to create agent '{agent_name}': {e}")
219
+ raise
220
+
221
+ async def execute_session(self,
222
+ agent_sequence: List[str],
223
+ max_steps_per_agent: Optional[Dict[str, int]] = None,
224
+ shared_context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
225
+ """Execute a session with the given agent sequence.
226
+
227
+ Args:
228
+ agent_sequence: List of agent IDs to execute in sequence
229
+ max_steps_per_agent: Maximum steps per agent
230
+ shared_context: Shared context data
231
+
232
+ Returns:
233
+ Results from the session execution
234
+ """
235
+ self.logger.info(f"Executing session with {len(agent_sequence)} agents: {agent_sequence}")
236
+
237
+ # C-N1: fire on_session_start once (fail-open; no-op unless hooks registered).
238
+ if not getattr(self, "_session_start_fired", False):
239
+ self._session_start_fired = True
240
+ run_hooks = getattr(self, "_run_session_start_hooks", None)
241
+ if run_hooks is not None:
242
+ await run_hooks(session_id=self.session_id, user_id=getattr(self, "user_id", None))
243
+
244
+ results = {}
245
+ max_steps_per_agent = max_steps_per_agent or {}
246
+
247
+ try:
248
+ for agent_id in agent_sequence:
249
+ if agent_id not in self.agents:
250
+ self.logger.error(f"Agent {agent_id} not found in session")
251
+ continue
252
+
253
+ agent = self.agents[agent_id]
254
+ max_steps = max_steps_per_agent.get(agent_id, 50)
255
+
256
+ self.logger.info(f"Running agent {agent_id} with max {max_steps} steps")
257
+
258
+ # Track agent execution
259
+ import time
260
+ start_time = time.time()
261
+
262
+ try:
263
+ # Run the agent
264
+ result = await agent.run(max_steps=max_steps)
265
+ end_time = time.time()
266
+
267
+ # Track execution - include agent_type parameter
268
+ agent_type = type(agent).__name__
269
+ self.track_agent_execution(agent_id, agent_type, start_time, end_time)
270
+
271
+ # Store result
272
+ results[agent_id] = {
273
+ "status": _result_session_status(result),
274
+ "result": result,
275
+ "execution_time": end_time - start_time
276
+ }
277
+
278
+ self.logger.info(f"Agent {agent_id} completed with status: {results[agent_id]['status']}")
279
+
280
+ except LLMProviderExhaustedError as e:
281
+ # All LLM providers have been tried and failed - don't retry
282
+ end_time = time.time()
283
+ providers = getattr(e, 'providers_tried', [])
284
+ self.logger.error(f"❌ Agent {agent_id} exhausted all LLM providers: {providers}")
285
+ results[agent_id] = {
286
+ "status": "error",
287
+ "error": f"All LLM providers exhausted: {providers}",
288
+ "execution_time": end_time - start_time,
289
+ "providers_tried": providers
290
+ }
291
+
292
+ except LLMPermanentError as e:
293
+ # Permanent error (auth, billing) - don't retry
294
+ end_time = time.time()
295
+ self.logger.error(f"❌ Agent {agent_id} hit permanent LLM error: {e}")
296
+ results[agent_id] = {
297
+ "status": "error",
298
+ "error": f"Permanent LLM error: {str(e)[:300]}",
299
+ "execution_time": end_time - start_time,
300
+ "is_permanent": True
301
+ }
302
+
303
+ except InterruptedError as e:
304
+ # CRITICAL FIX (Dec 2025): Handle pause/stop gracefully
305
+ # InterruptedError is raised when agent is stopped (not paused - pause now waits)
306
+ end_time = time.time()
307
+ error_str = str(e).lower()
308
+
309
+ if "paused" in error_str:
310
+ # Agent was paused - this is a graceful interruption, not an error
311
+ self.logger.info(f"⏸️ Agent {agent_id} paused by user")
312
+ results[agent_id] = {
313
+ "status": "paused",
314
+ "execution_time": end_time - start_time
315
+ }
316
+ else:
317
+ # Agent was stopped
318
+ self.logger.info(f"⏹️ Agent {agent_id} stopped: {e}")
319
+ results[agent_id] = {
320
+ "status": "stopped",
321
+ "execution_time": end_time - start_time
322
+ }
323
+
324
+ except Exception as e:
325
+ end_time = time.time()
326
+ error_str = str(e).lower()
327
+
328
+ # UPGRADE (Dec 2025): Check if this is an LLM error that might benefit from fallback
329
+ is_llm_error = (
330
+ isinstance(e, LLMError) or
331
+ any(x in error_str for x in [
332
+ 'llm', 'rate_limit', 'rate limit', '429', 'authentication',
333
+ 'connection', 'timeout', 'quota'
334
+ ])
335
+ )
336
+
337
+ if is_llm_error and hasattr(agent, 'state') and not agent.state.should_halt_for_llm_error():
338
+ self.logger.warning(f"Agent {agent_id} failed with LLM error, checking for fallback...")
339
+
340
+ # Try to get fallback LLM from container
341
+ try:
342
+ if self.container:
343
+ llm_manager = self.container.get_service('llm')
344
+ if llm_manager and hasattr(llm_manager, 'get_fallback_chat_model'):
345
+ fallback_llm = await llm_manager.get_fallback_chat_model(
346
+ exclude_providers=getattr(agent.state, 'llm_providers_failed', [])
347
+ )
348
+
349
+ if fallback_llm:
350
+ self.logger.info(f"🔄 Retrying agent {agent_id} with fallback LLM")
351
+ agent.llm = fallback_llm
352
+ agent.model_name = getattr(fallback_llm, 'model_name', 'fallback')
353
+
354
+ # Retry execution
355
+ retry_start = time.time()
356
+ result = await agent.run(max_steps=max_steps)
357
+ retry_end = time.time()
358
+
359
+ results[agent_id] = {
360
+ "status": _result_session_status(result),
361
+ "result": result,
362
+ "execution_time": retry_end - start_time,
363
+ "fallback_used": True
364
+ }
365
+ self.logger.info(f"✅ Agent {agent_id} succeeded with fallback LLM")
366
+ continue # Skip to next agent
367
+ except LLMProviderExhaustedError:
368
+ self.logger.error(f"Fallback retry exhausted all providers for {agent_id}")
369
+ except Exception as fallback_error:
370
+ self.logger.error(f"Fallback retry failed for {agent_id}: {fallback_error}")
371
+
372
+ self.logger.error(f"Agent {agent_id} failed: {e}")
373
+ results[agent_id] = {
374
+ "status": "error",
375
+ "error": str(e),
376
+ "execution_time": end_time - start_time
377
+ }
378
+ finally:
379
+ # Release browser context immediately after agent completes
380
+ if self.browser_manager:
381
+ try:
382
+ await self.browser_manager.release_context(agent_id, close=True)
383
+ self.logger.debug(f"Released browser context for completed agent {agent_id}")
384
+ except Exception as release_error:
385
+ self.logger.warning(f"Error releasing context for {agent_id}: {release_error}")
386
+
387
+ # NOTE: Status updates removed - task_agent_lite owns session status lifecycle
388
+ # Orchestrator only executes agents, it doesn't manage session status
389
+ self.logger.info(f"Session execution completed. Results: {list(results.keys())}")
390
+ return results
391
+
392
+ except Exception as e:
393
+ self.logger.error(f"Session execution failed: {e}")
394
+ # NOTE: Don't update status here - let caller (task_agent_lite) handle it
395
+ raise
396
+
397
+
@@ -0,0 +1,200 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import time
5
+ from datetime import datetime
6
+ from typing import Any, Awaitable, Callable, Optional
7
+
8
+ _logger = logging.getLogger(__name__)
9
+
10
+
11
+ def build_stream_publish(
12
+ router: Any, session_key: Optional[str]
13
+ ) -> Callable[..., Awaitable[None]]:
14
+ """Return an async ``(chunk, step) -> None`` that mirrors a streamed chunk into
15
+ the unified outbound seam (``MessageRouter.publish``) as a partial OutboundMessage.
16
+
17
+ P1a outbound-collapse: additive and gated on ``SINGULAR_CHAT_ENABLED``. When the
18
+ flag is OFF, or there is no router / session_key, the returned fn is a no-op so
19
+ the legacy stream callback path stays byte-identical. Fail-open — a publish error
20
+ never propagates into the run loop.
21
+ """
22
+ async def _publish(chunk: str, step: int = 0) -> None:
23
+ from agents.task.surface_config import SurfaceConfig
24
+
25
+ if not SurfaceConfig.singular_chat_enabled():
26
+ return
27
+ if router is None or not session_key:
28
+ return
29
+ try:
30
+ from core.surfaces.envelopes import OutboundMessage
31
+
32
+ await router.publish(OutboundMessage(
33
+ session_key=session_key,
34
+ text=chunk,
35
+ partial=True,
36
+ # Stable per-TURN stream_id (the session_key, NOT per-step): all of a
37
+ # turn's deltas edit ONE live message under incremental streaming, and the
38
+ # turn's discrete reply finalizes that same stream (see Surface
39
+ # _finalize_live_on_send). A per-step id would open a new bubble each step
40
+ # and never finalize. `step` is retained for callers but no longer keys.
41
+ stream_id=session_key,
42
+ ))
43
+ except Exception as e: # fail-open: streaming mirror is non-critical
44
+ _logger.debug("stream publish mirror failed: %s", e)
45
+
46
+ return _publish
47
+
48
+
49
+ class FeedMixin:
50
+ """Feed metadata processing and stream-callback wiring for SessionOrchestrator."""
51
+
52
+ def _process_feed_metadata(self, clean_id, feed_entries: list) -> None:
53
+ """Extract and store metadata from feed entries.
54
+
55
+ Args:
56
+ clean_id: Cleaned session ID
57
+ feed_entries: List of feed entry data objects
58
+ """
59
+ if not feed_entries:
60
+ return
61
+
62
+ try:
63
+ # Initialize metadata update
64
+ metadata_update = {
65
+ "feed": {
66
+ "processed_entries": len(feed_entries),
67
+ "last_processed": datetime.now().isoformat(),
68
+ "entries": []
69
+ }
70
+ }
71
+
72
+ # Process each entry and extract key information
73
+ for entry in feed_entries:
74
+ # Skip entries without proper type
75
+ if not isinstance(entry, dict) or "type" not in entry:
76
+ continue
77
+
78
+ entry_type = entry.get("type", "unknown")
79
+ timestamp = entry.get("timestamp", time.time())
80
+
81
+ # Create basic entry metadata
82
+ entry_meta = {
83
+ "type": entry_type,
84
+ "timestamp": timestamp
85
+ }
86
+
87
+ # Extract specific data based on entry type
88
+ if entry_type == "step" and "data" in entry:
89
+ # Step data - extract actions and progress
90
+ data = entry.get("data", {})
91
+ step_num = entry.get("step", 0)
92
+
93
+ # Add step-specific metadata
94
+ entry_meta.update({
95
+ "step": step_num,
96
+ "actions": [action.get("name", action.get("action_type", "unknown"))
97
+ for action in data.get("actions", [])],
98
+ "has_errors": bool(data.get("errors")),
99
+ "task_progress": data.get("task_progress", "")
100
+ })
101
+
102
+ elif entry_type == "planner" and "data" in entry:
103
+ # Planner data
104
+ data = entry.get("data", {})
105
+ step_num = entry.get("step", 0)
106
+
107
+ # Add planner-specific metadata
108
+ entry_meta.update({
109
+ "step": step_num,
110
+ "model": data.get("model_name", "unknown"),
111
+ "components": data.get("components", {})
112
+ })
113
+
114
+ elif entry_type == "evaluation" and "data" in entry:
115
+ # Evaluation data
116
+ data = entry.get("data", {})
117
+ step_num = entry.get("step", 0)
118
+
119
+ # Add evaluation-specific metadata
120
+ entry_meta.update({
121
+ "step": step_num,
122
+ "model": data.get("model_name", "unknown"),
123
+ "assessment": data.get("assessment", ""),
124
+ "has_strengths": bool(data.get("strengths")),
125
+ "has_weaknesses": bool(data.get("weaknesses")),
126
+ "has_suggestions": bool(data.get("suggestions"))
127
+ })
128
+
129
+ elif entry_type == "multi_agent_relationship" and "data" in entry:
130
+ # Relationship data
131
+ data = entry.get("data", {})
132
+
133
+ # Add relationship-specific metadata
134
+ entry_meta.update({
135
+ "agent_count": len(data.get("agent_ids", [])),
136
+ "agent_types": data.get("agent_types", {}),
137
+ "has_sequence": bool(data.get("agent_sequence"))
138
+ })
139
+
140
+ # Add the entry metadata to the list
141
+ metadata_update["feed"]["entries"].append(entry_meta)
142
+
143
+ # Update session metadata with the feed information
144
+ if self.session_manager:
145
+ self.session_manager.update_session_metadata(clean_id, metadata_update)
146
+ self.logger.debug(f"Updated session metadata with {len(feed_entries)} feed entries")
147
+
148
+ except Exception as e:
149
+ self.logger.warning(f"Error processing feed metadata: {e}", exc_info=True)
150
+
151
+ async def _register_stream_callback(self, agent: Any) -> None:
152
+ """Wire the injected stream callback into the agent's HITL manager.
153
+
154
+ The callback transport (httpx → webview, websocket, in-memory queue,
155
+ etc.) is supplied by whoever constructed the orchestrator. Core code
156
+ never knows the destination; it only forwards each LLM output chunk
157
+ with metadata.
158
+ """
159
+ callback = self._on_stream_chunk
160
+ # P1a outbound-collapse: mirror chunks into the unified MessageRouter seam when
161
+ # a router + chat session_key have been bound (later phases). No-op otherwise,
162
+ # so the legacy callback path stays byte-identical until then.
163
+ _mirror = build_stream_publish(
164
+ getattr(self, "_message_router", None),
165
+ getattr(self, "_chat_session_key", None),
166
+ )
167
+ if callback is None:
168
+ # Even with no legacy callback, the unified mirror may be active.
169
+ async def stream_wrapper(chunk: str) -> None:
170
+ await _mirror(chunk, getattr(agent, 'n_steps', 0))
171
+ try:
172
+ agent.hitl_manager.register_output_callback(stream_wrapper)
173
+ except Exception as e:
174
+ self.logger.warning(f"Failed to register stream callback: {e}")
175
+ return
176
+
177
+ async def stream_wrapper(chunk: str) -> None:
178
+ try:
179
+ await callback(
180
+ self.session_id,
181
+ agent.agent_id,
182
+ chunk,
183
+ getattr(agent, 'n_steps', 0),
184
+ )
185
+ except Exception as e:
186
+ # Streaming is non-critical; never fail the run for transport errors.
187
+ self.logger.debug(f"Stream callback error: {e}")
188
+ await _mirror(chunk, getattr(agent, 'n_steps', 0))
189
+
190
+ try:
191
+ agent.hitl_manager.register_output_callback(stream_wrapper)
192
+ self.logger.info(f"✅ Stream callback registered for {agent.agent_id}")
193
+ except Exception as e:
194
+ self.logger.warning(f"Failed to register stream callback: {e}")
195
+
196
+ async def add_to_feed(self, agent_id: str, entry_type: str, data: dict) -> None:
197
+ if self.session_manager:
198
+ await self.session_manager.add_to_feed(
199
+ self.session_id, agent_id, entry_type, data
200
+ )