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,275 @@
1
+ """Restart-durable autonomy state.
2
+
3
+ Two registries used to be volatile process memory, making autonomy turn-durable
4
+ but not restart-durable:
5
+
6
+ - ``AsyncDelegationRegistry`` (UP-12) — a background delegation dispatched and
7
+ then lost to a restart silently evaporated: the parent session was promised a
8
+ ``delegation_result`` that never arrived.
9
+ - ``ReentryBudget`` (W1) — a forged-wake storm mid-ping-pong got a free depth
10
+ reset by crashing.
11
+
12
+ ``AutonomyStateStore`` persists both on the standard WAL+jitter sidecar pattern
13
+ (``core/sqlite_util``, same as goals.db/cron.db) in ``autonomy_state.db`` under
14
+ the data root (registered in ``core/db_manifest.py`` so backup/rollback snapshot
15
+ it). Recovery is HONEST, never magic: a delegation still ``running`` at process
16
+ start was crash-interrupted — :func:`recover_interrupted_delegations` marks it
17
+ ``interrupted`` and surfaces that back to the originating session via the
18
+ self-wake rail (or leaves the durable row as the record when the wake is
19
+ dropped/disabled). The child coroutine itself is never resumed.
20
+
21
+ Everything here is fail-open: a store error degrades the registries to their
22
+ legacy in-memory behavior, never blocks a dispatch or a wake.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ import os
28
+ import threading
29
+ import time
30
+ from typing import Any, List, Optional
31
+
32
+ from core.sqlite_util import execute_retry
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+ _RESULT_TEXT_CAP = 4000
37
+ # Budget rows older than this are dropped on hydrate — a wake recorded days ago
38
+ # should not still be spacing/backing-off a fresh process.
39
+ STALE_BUDGET_SEC = 7 * 86400
40
+
41
+
42
+ def default_autonomy_state_db() -> str:
43
+ """Resolve autonomy_state.db NEXT TO its sibling autonomy DBs.
44
+
45
+ cron.db/goals.db/memory.db live under the container config's ``data_dir``
46
+ (server: ``{POLYROB_DATA_DIR}/data``; CLI: reconciled to the ``.polyrob``
47
+ root by build_cli_container) — prefer that, so ops tooling that assumes
48
+ co-location never misses this DB. Fall back to ``get_data_root()`` (the CLI
49
+ resolution) when no container config is available.
50
+ """
51
+ try:
52
+ from core.container import DependencyContainer
53
+ cfg = DependencyContainer.get_instance().get_service("config")
54
+ data_dir = getattr(cfg, "data_dir", None)
55
+ if data_dir:
56
+ return os.path.join(str(data_dir), "autonomy_state.db")
57
+ except Exception:
58
+ pass
59
+ from core.runtime_config import get_data_root
60
+ return os.path.join(get_data_root(), "autonomy_state.db")
61
+
62
+
63
+ class AutonomyStateStore:
64
+ """WAL-backed store for delegations + reentry budgets (one per data root)."""
65
+
66
+ def __init__(self, db_path: str):
67
+ self.db_path = db_path
68
+ os.makedirs(os.path.dirname(db_path) or ".", exist_ok=True)
69
+ execute_retry(
70
+ db_path,
71
+ """CREATE TABLE IF NOT EXISTS delegations (
72
+ session_id TEXT NOT NULL,
73
+ delegation_id TEXT NOT NULL,
74
+ user_id TEXT NOT NULL DEFAULT '',
75
+ goal TEXT,
76
+ profile TEXT,
77
+ parent_agent_id TEXT,
78
+ status TEXT NOT NULL DEFAULT 'running',
79
+ dispatched_at REAL,
80
+ completed_at REAL,
81
+ result_text TEXT,
82
+ PRIMARY KEY (session_id, delegation_id)
83
+ )""",
84
+ )
85
+ execute_retry(
86
+ db_path,
87
+ "CREATE INDEX IF NOT EXISTS idx_delegations_status ON delegations(status)",
88
+ )
89
+ execute_retry(
90
+ db_path,
91
+ """CREATE TABLE IF NOT EXISTS reentry_budget (
92
+ session_id TEXT PRIMARY KEY,
93
+ user_id TEXT NOT NULL DEFAULT '',
94
+ count INTEGER NOT NULL DEFAULT 0,
95
+ last_wake_at REAL NOT NULL DEFAULT 0
96
+ )""",
97
+ )
98
+
99
+ # -- delegations -----------------------------------------------------------
100
+
101
+ def record_dispatched(self, *, session_id: str, user_id: str, delegation_id: str,
102
+ goal: str, profile: str, parent_agent_id: Optional[str],
103
+ dispatched_at: float) -> None:
104
+ execute_retry(
105
+ self.db_path,
106
+ """INSERT OR REPLACE INTO delegations
107
+ (session_id, delegation_id, user_id, goal, profile, parent_agent_id,
108
+ status, dispatched_at)
109
+ VALUES (?,?,?,?,?,?,'running',?)""",
110
+ (session_id, delegation_id, user_id, goal, profile,
111
+ parent_agent_id, dispatched_at),
112
+ )
113
+
114
+ def record_terminal(self, session_id: str, delegation_id: str, *, status: str,
115
+ completed_at: float, result_text: str = "") -> None:
116
+ execute_retry(
117
+ self.db_path,
118
+ """UPDATE delegations SET status=?, completed_at=?, result_text=?
119
+ WHERE session_id=? AND delegation_id=?""",
120
+ (status, completed_at, (result_text or "")[:_RESULT_TEXT_CAP],
121
+ session_id, delegation_id),
122
+ )
123
+
124
+ def get(self, session_id: str, delegation_id: str) -> Optional[dict]:
125
+ row = execute_retry(
126
+ self.db_path,
127
+ "SELECT * FROM delegations WHERE session_id=? AND delegation_id=?",
128
+ (session_id, delegation_id), fetch="one",
129
+ )
130
+ return dict(row) if row else None
131
+
132
+ def list_running(self) -> List[dict]:
133
+ rows = execute_retry(
134
+ self.db_path, "SELECT * FROM delegations WHERE status='running'",
135
+ fetch="all",
136
+ )
137
+ return [dict(r) for r in (rows or [])]
138
+
139
+ def max_counter(self, session_id: str) -> int:
140
+ """Highest numeric suffix of this session's deleg_NNNN ids — seeds the
141
+ in-memory counter so a restarted session never reissues an id."""
142
+ rows = execute_retry(
143
+ self.db_path,
144
+ "SELECT delegation_id FROM delegations WHERE session_id=?",
145
+ (session_id,), fetch="all",
146
+ )
147
+ best = 0
148
+ for r in rows or []:
149
+ did = r["delegation_id"] or ""
150
+ if did.startswith("deleg_"):
151
+ try:
152
+ best = max(best, int(did.split("_", 1)[1]))
153
+ except ValueError:
154
+ continue
155
+ return best
156
+
157
+ # -- reentry budget ----------------------------------------------------------
158
+
159
+ def get_budget(self, session_id: str) -> Optional[dict]:
160
+ row = execute_retry(
161
+ self.db_path,
162
+ "SELECT count, last_wake_at FROM reentry_budget WHERE session_id=?",
163
+ (session_id,), fetch="one",
164
+ )
165
+ return dict(row) if row else None
166
+
167
+ def put_budget(self, session_id: str, user_id: str, *, count: int,
168
+ last_wake_at: float) -> None:
169
+ execute_retry(
170
+ self.db_path,
171
+ """INSERT INTO reentry_budget (session_id, user_id, count, last_wake_at)
172
+ VALUES (?,?,?,?)
173
+ ON CONFLICT(session_id) DO UPDATE SET
174
+ count=excluded.count, last_wake_at=excluded.last_wake_at""",
175
+ (session_id, user_id, count, last_wake_at),
176
+ )
177
+
178
+ def delete_budget(self, session_id: str) -> None:
179
+ execute_retry(
180
+ self.db_path, "DELETE FROM reentry_budget WHERE session_id=?",
181
+ (session_id,),
182
+ )
183
+
184
+
185
+ _STORE_LOCK = threading.Lock()
186
+ _STORE_CACHE: dict = {}
187
+
188
+
189
+ def get_autonomy_state_store() -> Optional[AutonomyStateStore]:
190
+ """The store at the default path, or None when durability is off or the
191
+ store cannot be opened (fail-open to legacy in-memory behavior).
192
+
193
+ Memoized per resolved path — orchestrators are constructed per session and
194
+ the store's schema init is blocking sqlite I/O; it must run once per
195
+ process, not once per session.
196
+ """
197
+ try:
198
+ from agents.task.constants import AutonomyConfig
199
+ if not AutonomyConfig.autonomy_state_durable():
200
+ return None
201
+ path = default_autonomy_state_db()
202
+ with _STORE_LOCK:
203
+ store = _STORE_CACHE.get(path)
204
+ if store is None:
205
+ store = AutonomyStateStore(path)
206
+ _STORE_CACHE[path] = store
207
+ return store
208
+ except Exception:
209
+ logger.warning("autonomy_state store unavailable — running in-memory only",
210
+ exc_info=True)
211
+ return None
212
+
213
+
214
+ def reset_autonomy_state_store_cache() -> None:
215
+ """Test seam: drop memoized stores so path/env changes take effect."""
216
+ with _STORE_LOCK:
217
+ _STORE_CACHE.clear()
218
+
219
+
220
+ async def recover_interrupted_delegations(task_agent: Any, db_path: str) -> int:
221
+ """Startup sweep: mark crash-interrupted delegations and surface them.
222
+
223
+ Returns the number of rows recovered. The self-wake delivery is best-effort
224
+ (SELF_WAKE_ENABLED off / non-resident session / budget exhausted → the wake
225
+ is dropped, but the durable ``interrupted`` row remains the honest record).
226
+ """
227
+ if not os.path.exists(db_path):
228
+ return 0
229
+ try:
230
+ store = AutonomyStateStore(db_path)
231
+ rows = store.list_running()
232
+ except Exception:
233
+ logger.warning("delegation recovery sweep failed to read store", exc_info=True)
234
+ return 0
235
+ recovered = 0
236
+ for row in rows:
237
+ session_id, delegation_id = row["session_id"], row["delegation_id"]
238
+ try:
239
+ store.record_terminal(
240
+ session_id, delegation_id, status="interrupted",
241
+ completed_at=time.time(),
242
+ result_text="Process restarted while this delegation was running.",
243
+ )
244
+ recovered += 1
245
+ except Exception:
246
+ logger.warning("could not mark delegation %s/%s interrupted",
247
+ session_id, delegation_id, exc_info=True)
248
+ continue
249
+ try:
250
+ from agents.task.telemetry.event_log import get_event_log, event_log_enabled
251
+ if event_log_enabled():
252
+ get_event_log().record(
253
+ "delegation_interrupted", user_id=row.get("user_id", ""),
254
+ session_id=session_id, source="autonomy_state",
255
+ attrs={"delegation_id": delegation_id,
256
+ "goal": (row.get("goal") or "")[:200]},
257
+ )
258
+ except Exception:
259
+ pass
260
+ try:
261
+ deliver = getattr(task_agent, "deliver_self_wake", None)
262
+ if deliver is not None:
263
+ await deliver(
264
+ session_id, row.get("user_id", ""),
265
+ (f"Background delegation {delegation_id} "
266
+ f"({(row.get('goal') or '')[:200]}) was interrupted by a process "
267
+ "restart and did NOT complete. Re-dispatch it if the work is "
268
+ "still needed."),
269
+ metadata={"delegation_id": delegation_id,
270
+ "recovery": "interrupted_delegation"},
271
+ )
272
+ except Exception:
273
+ logger.warning("interrupted-delegation wake for %s failed (row remains "
274
+ "the durable record)", session_id, exc_info=True)
275
+ return recovered
@@ -0,0 +1,75 @@
1
+ """First-class multi-turn conversation over the existing agent loop (R4).
2
+
3
+ A Conversation owns ordered Turns and drives the agent one turn at a time with a
4
+ small per-turn step budget, returning the answer synchronously — instead of the
5
+ HITL resume FSM (which re-enters a 100-step task loop per reply). Server task
6
+ sessions are unaffected; this is opt-in via Conversation.
7
+ """
8
+ from __future__ import annotations
9
+ from dataclasses import dataclass, field
10
+ from typing import Any, Iterable, List
11
+
12
+
13
+ def extract_answer(history: Any) -> str:
14
+ """The agent's reply for a turn = the last is_done result's extracted_content.
15
+
16
+ Accepts either a flat iterable of ActionResults or the AgentHistoryList that
17
+ Agent.run() actually returns. AgentHistoryList is a pydantic model: iterating
18
+ it yields (field, value) tuples, not results, so unwrap via action_results()
19
+ when available before scanning for the terminal is_done result.
20
+ """
21
+ results: Iterable[Any]
22
+ if hasattr(history, "action_results") and callable(history.action_results):
23
+ results = history.action_results()
24
+ else:
25
+ results = history
26
+ answer = ""
27
+ for result in results:
28
+ if getattr(result, "is_done", False) and getattr(result, "extracted_content", None):
29
+ answer = result.extracted_content
30
+ return answer
31
+
32
+
33
+ DEFAULT_TURN_BUDGET = 20 # interactive turns; raise for big asks, autonomous tasks use run()
34
+
35
+
36
+ def _reset_turn_state(agent: Any) -> None:
37
+ """Clear transient failure state so a NEW user turn gets a fresh attempt.
38
+
39
+ Agent.run() resets n_steps but not consecutive_failures / _cancelled. Without
40
+ this, once a turn hits max consecutive failures (e.g. an LLM quota error), the
41
+ agent stays wedged and every later respond() returns 'failed | -1 steps' — the
42
+ REPL is dead until restart. A fresh user message should start clean.
43
+ """
44
+ state = getattr(agent, "state", None)
45
+ if state is not None and hasattr(state, "consecutive_failures"):
46
+ state.consecutive_failures = 0
47
+ if getattr(agent, "_cancelled", False):
48
+ agent._cancelled = False
49
+
50
+
51
+ @dataclass
52
+ class Turn:
53
+ user: str
54
+ assistant: str = ""
55
+
56
+
57
+ @dataclass
58
+ class Conversation:
59
+ agent: Any
60
+ turns: List[Turn] = field(default_factory=list)
61
+
62
+ async def respond(self, text: str, max_steps: int = DEFAULT_TURN_BUDGET) -> str:
63
+ """Run exactly one conversational turn and return the agent's reply.
64
+
65
+ After the first turn we pass _continue_session=True so run() skips the
66
+ once-per-session preamble (session-start telemetry + H-MEM create/load),
67
+ which would otherwise re-fire on every REPL line.
68
+ """
69
+ _reset_turn_state(self.agent) # recover from a prior failed turn
70
+ self.agent.set_turn_input(text) # append-as-turn (Task 7)
71
+ history = await self.agent.run(
72
+ max_steps=max_steps, _continue_session=bool(self.turns))
73
+ answer = extract_answer(history)
74
+ self.turns.append(Turn(user=text, assistant=answer))
75
+ return answer
@@ -0,0 +1,6 @@
1
+ """Agent core mixins.
2
+
3
+ Pure code-motion extraction of internal method-groups from
4
+ ``agents/task/agent/service.py`` (class ``Agent``). The mixins here are composed
5
+ back into ``Agent`` via inheritance; method bodies are verbatim.
6
+ """
@@ -0,0 +1,61 @@
1
+ """Meter auxiliary LLM calls (compaction, judge, reflection) through the single
2
+ usage/deduction path. These calls were unbilled — only the main step
3
+ (next_action_internal.py:388) recorded usage — so long-context compaction on the
4
+ flagship model was free. Per operator decision aux calls DEDUCT like main calls, but
5
+ this helper FAILS OPEN on any error (including InsufficientCreditsError): aux calls are
6
+ internal maintenance and must never hard-halt a session. The deduction still happens
7
+ whenever the balance covers it.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from typing import Any, Optional
12
+ import logging
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def _llm_identity(llm: Any, response: Any = None) -> tuple[str, str]:
18
+ """Derive (model, provider) from the llm object, falling back to detecting
19
+ the provider from the model name via ``detect_llm_provider`` (which takes a
20
+ RESPONSE + optional model name — the response param is unused by that
21
+ function but kept for signature compatibility; the model name is what it
22
+ actually looks up in the model registry).
23
+ """
24
+ model = (getattr(llm, "model_name", None) or getattr(llm, "model", None)
25
+ or getattr(llm, "model_type", None) or "unknown")
26
+ provider = (getattr(llm, "llm_provider", None) or getattr(llm, "provider_name", None))
27
+ if not provider:
28
+ try:
29
+ from agents.task.utils import detect_llm_provider
30
+ provider = detect_llm_provider(response, model) or "unknown"
31
+ except Exception:
32
+ provider = "unknown"
33
+ return str(model), str(provider)
34
+
35
+
36
+ async def meter_aux_llm(*, usage_tracker, user_id: Optional[str], session_id: str,
37
+ agent_id: str, llm: Any, response: Any, duration_seconds: float,
38
+ component: str, purpose: str) -> None:
39
+ """Record usage for an auxiliary (non-main-step) LLM call.
40
+
41
+ Fail-open: no-ops silently when there's no tracker/user_id (e.g. anonymous
42
+ local sessions), and swallows ANY exception from token extraction or
43
+ ``record_llm_usage`` itself (including InsufficientCreditsError) — an aux
44
+ call is internal maintenance and must never abort the caller.
45
+ """
46
+ if not (usage_tracker and user_id):
47
+ return
48
+ try:
49
+ from agents.task.utils import extract_token_usage
50
+ model, provider = _llm_identity(llm, response)
51
+ usage = extract_token_usage(response, provider) or {}
52
+ await usage_tracker.record_llm_usage(
53
+ user_id=user_id, session_id=session_id, agent_id=agent_id,
54
+ model=model, provider=provider,
55
+ input_tokens=usage.get("prompt_tokens") or 0,
56
+ output_tokens=usage.get("completion_tokens") or 0,
57
+ cached_tokens=usage.get("cached_tokens") or 0,
58
+ duration_seconds=duration_seconds, component=component, purpose=purpose, success=True,
59
+ )
60
+ except Exception as e: # fail-open (incl. InsufficientCreditsError): never break the aux op
61
+ logger.warning(f"aux metering skipped ({component}/{purpose}): {type(e).__name__}: {e}")
@@ -0,0 +1,157 @@
1
+ """Post-turn background-review fork (W2-C, Reference-parity self-improvement loop).
2
+
3
+ After a run of productive turns, the agent forks a cheap, fire-and-forget sub-agent
4
+ that reviews what just happened and — when it finds a durable, reusable procedure —
5
+ authors a skill (via the W2 ``skill_manage`` tool, which quarantines it for review).
6
+ This is the "learning" loop: experience → distilled skill → available next session.
7
+
8
+ Design:
9
+ - **Non-blocking** — ``asyncio.create_task`` so the run loop returns immediately; a
10
+ review never delays the user-facing turn.
11
+ - **Cheap** — runs on the aux/judge model (``_provision_aux_llm("judge")``) and
12
+ inherits the cached system prefix → prefix-cache hit (Reference's ~26% saving).
13
+ - **Least-privilege** — spawned through ``SubAgentManager.run_subtask`` as a depth-1
14
+ LEAF (UP-05 child controller: no code-exec/cron, cannot re-delegate).
15
+ - **Bounded** — at most ``BG_REVIEW_MAX_STEPS`` steps; exempt for sub-agents (a
16
+ reviewer never forks its own reviewer).
17
+ - **Fail-open** — any error is swallowed; the loop must never break on a review.
18
+
19
+ Gated ``BACKGROUND_REVIEW_ENABLED`` (default OFF). Authored skills are quarantined
20
+ (``created_by="background_review"`` → always ``.pending/``), so even a compromised
21
+ review cannot self-activate a skill.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import asyncio
26
+ import logging
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ _REVIEW_PROMPT = (
31
+ "You are a background reviewer for an AI agent. The agent just finished a run of "
32
+ "work in this session. Review the recent conversation/work and decide whether a "
33
+ "DURABLE, REUSABLE procedure emerged that would help in future sessions.\n\n"
34
+ "If yes: call skill_manage(action='create', skill_id=<short-kebab-id>, "
35
+ "content=<a concise SKILL.md: a '# Title' heading then when-to-use + steps>). "
36
+ "Keep it general (no secrets, no session-specific data). Author at most ONE skill.\n"
37
+ "If nothing is worth saving: call done() with a one-line reason. Do not do any "
38
+ "other work, browse, or message the user."
39
+ )
40
+
41
+ # polyrob Phase E: the "learns over time" engine. The reviewer already runs as a
42
+ # LEAF sub-agent, so a self_context_manage write resolves to background provenance →
43
+ # ALWAYS .pending, NEVER auto-active and NEVER promotable by this forged turn. So we
44
+ # can safely invite it to ALSO propose a consolidated SELF-context refinement; the
45
+ # owner promotes it later. Only offered when SELF_CONTEXT_WRITABLE is on.
46
+ _SELF_CONTEXT_REVIEW_ADDENDUM = (
47
+ "\n\nSEPARATELY: if you learned something durable about how to work with THIS user "
48
+ "(a stable preference, convention, or fact worth remembering), you may also call "
49
+ "self_context_manage(action='update', content=<a consolidated self.md, ≤2200 "
50
+ "chars — MERGE with what's already there via action='read' first; do not sprawl>). "
51
+ "This proposes a refinement to your evolving SELF context; it is QUARANTINED for "
52
+ "owner review and applies next session. Keep it about working-style/preferences, "
53
+ "never your core identity or boundaries (those are operator-owned)."
54
+ )
55
+
56
+
57
+ def build_review_prompt() -> str:
58
+ """The reviewer task. Adds the SELF-context-refinement invitation only when the
59
+ SELF_CONTEXT_WRITABLE tool is actually available (else the legacy skills-only
60
+ prompt, byte-identical)."""
61
+ try:
62
+ from agents.task.constants import AutonomyConfig
63
+ if AutonomyConfig.self_context_writable():
64
+ return _REVIEW_PROMPT + _SELF_CONTEXT_REVIEW_ADDENDUM
65
+ except Exception:
66
+ pass
67
+ return _REVIEW_PROMPT
68
+
69
+
70
+ class BackgroundReviewMixin:
71
+ """Fire a post-turn self-improvement reviewer. Composed into Agent."""
72
+
73
+ def _bg_review_should_fire(self, turn_was_productive: bool) -> bool:
74
+ """Pure decision: increment the productive-turn counter and report whether the
75
+ interval has elapsed. Resets the counter when it fires."""
76
+ from agents.task.constants import AutonomyConfig
77
+ if not AutonomyConfig.background_review_enabled():
78
+ return False
79
+ if not AutonomyConfig.skills_writable():
80
+ # SK-F8: the reviewer's whole output rail is skill_manage(create) —
81
+ # firing it while skills are read-only just burns an aux-model run
82
+ # that is guaranteed to fail at the tool layer.
83
+ return False
84
+ if getattr(self, "_is_sub_agent", False):
85
+ return False # a reviewer never forks a reviewer
86
+ if not turn_was_productive:
87
+ return False
88
+ interval = max(1, AutonomyConfig.bg_review_interval())
89
+ n = getattr(self, "_bg_review_productive_turns", 0) + 1
90
+ if n >= interval:
91
+ self._bg_review_productive_turns = 0
92
+ return True
93
+ self._bg_review_productive_turns = n
94
+ return False
95
+
96
+ def _maybe_spawn_background_review(self, *, turn_was_productive: bool) -> None:
97
+ """Fire-and-forget the reviewer if the cadence says so. Never blocks/raises."""
98
+ try:
99
+ if not self._bg_review_should_fire(turn_was_productive):
100
+ return
101
+ task = asyncio.create_task(self._run_background_review())
102
+ # M5: keep a strong reference so CPython can't GC the task mid-run (a task no
103
+ # one references may be collected before it completes), and so it's cancelled
104
+ # at session teardown. Mirrors _stall_check_task; self-removes when done.
105
+ _tasks = getattr(self.orchestrator, "_execution_tasks", None)
106
+ if isinstance(_tasks, list):
107
+ _tasks.append(task)
108
+ task.add_done_callback(lambda t, _l=_tasks: (_l.remove(t) if t in _l else None))
109
+ logger.info("🪞 spawned background-review fork (session %s)",
110
+ getattr(self.orchestrator, "session_id", "?"))
111
+ except Exception as e:
112
+ logger.debug("background-review spawn skipped: %s", e)
113
+
114
+ async def _run_background_review(self) -> None:
115
+ """Run the reviewer sub-agent on the aux model. Fail-open."""
116
+ try:
117
+ from agents.task.constants import AutonomyConfig
118
+ manager = self.orchestrator.get_sub_agent_manager() if self.orchestrator else None
119
+ if manager is None:
120
+ return
121
+ # P2-8: reuse the agent's cached judge client instead of building a fresh
122
+ # isolated client (with its own httpx pool) on every fire — the old code
123
+ # leaked one pool per background review. Cached on self._judge_llm (the same
124
+ # slot _validate_output uses) and closed once at session cleanup (M2).
125
+ aux = getattr(self, "_judge_llm", None)
126
+ if aux is None:
127
+ try:
128
+ # P2-9: async provisioning — don't block the loop building the client.
129
+ aux = await self._provision_aux_llm_async("judge")
130
+ if aux is not None:
131
+ self._judge_llm = aux
132
+ except Exception:
133
+ aux = None
134
+ # P2-10: make the reviewer's model cost VISIBLE. The cheap-aux map has no
135
+ # openrouter/nvidia key, so on the default OpenRouter deploy `aux` is None and
136
+ # this PROACTIVE loop (fires every BG_REVIEW_INTERVAL turns without any user
137
+ # ask) runs up to bg_review_max_steps on the FLAGSHIP main model. Log at INFO
138
+ # which model it will use so the cost isn't silent (add an openrouter/nvidia
139
+ # cheap-map entry or set AUX_MODEL_JUDGE to route it cheap).
140
+ _review_llm = aux or self.llm
141
+ if aux is None:
142
+ logger.info(
143
+ "background review running on the MAIN model %r (no cheap aux "
144
+ "resolved) — set AUX_MODEL_JUDGE or add a provider cheap-map entry "
145
+ "to route it to a cheaper model",
146
+ getattr(self.llm, "model_name", getattr(self.llm, "model_type", "?")),
147
+ )
148
+ await manager.run_subtask(
149
+ task=build_review_prompt(),
150
+ parent_agent_id=self.agent_id,
151
+ profile_id="executor",
152
+ max_steps=AutonomyConfig.bg_review_max_steps(),
153
+ parent_llm=_review_llm,
154
+ skip_complexity_check=True,
155
+ )
156
+ except Exception as e:
157
+ logger.debug("background-review run failed (ignored): %s", e)