dulus 3.14.0__tar.gz → 3.14.2__tar.gz

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 (388) hide show
  1. {dulus-3.14.0/dulus.egg-info → dulus-3.14.2}/PKG-INFO +7 -1
  2. {dulus-3.14.0 → dulus-3.14.2}/docs/news.md +3 -1
  3. {dulus-3.14.0 → dulus-3.14.2/dulus.egg-info}/PKG-INFO +7 -1
  4. {dulus-3.14.0 → dulus-3.14.2}/dulus.egg-info/requires.txt +6 -0
  5. {dulus-3.14.0 → dulus-3.14.2}/dulus.py +44 -37
  6. {dulus-3.14.0 → dulus-3.14.2}/providers.py +249 -0
  7. {dulus-3.14.0 → dulus-3.14.2}/pyproject.toml +21 -1
  8. {dulus-3.14.0 → dulus-3.14.2}/LICENSE +0 -0
  9. {dulus-3.14.0 → dulus-3.14.2}/MANIFEST.in +0 -0
  10. {dulus-3.14.0 → dulus-3.14.2}/README.md +0 -0
  11. {dulus-3.14.0 → dulus-3.14.2}/agent.py +0 -0
  12. {dulus-3.14.0 → dulus-3.14.2}/algolia_search.py +0 -0
  13. {dulus-3.14.0 → dulus-3.14.2}/analytics.py +0 -0
  14. {dulus-3.14.0 → dulus-3.14.2}/backend/__init__.py +0 -0
  15. {dulus-3.14.0 → dulus-3.14.2}/backend/agents_bridge.py +0 -0
  16. {dulus-3.14.0 → dulus-3.14.2}/backend/compressor.py +0 -0
  17. {dulus-3.14.0 → dulus-3.14.2}/backend/context.py +0 -0
  18. {dulus-3.14.0 → dulus-3.14.2}/backend/githook.py +0 -0
  19. {dulus-3.14.0 → dulus-3.14.2}/backend/marketplace.py +0 -0
  20. {dulus-3.14.0 → dulus-3.14.2}/backend/mempalace_bridge.py +0 -0
  21. {dulus-3.14.0 → dulus-3.14.2}/backend/paths.py +0 -0
  22. {dulus-3.14.0 → dulus-3.14.2}/backend/personas.py +0 -0
  23. {dulus-3.14.0 → dulus-3.14.2}/backend/plugins.py +0 -0
  24. {dulus-3.14.0 → dulus-3.14.2}/backend/server.py +0 -0
  25. {dulus-3.14.0 → dulus-3.14.2}/backend/tasks.py +0 -0
  26. {dulus-3.14.0 → dulus-3.14.2}/banners/__init__.py +0 -0
  27. {dulus-3.14.0 → dulus-3.14.2}/banners/banner_rotator.py +0 -0
  28. {dulus-3.14.0 → dulus-3.14.2}/banners/banners_dulus_ai.txt +0 -0
  29. {dulus-3.14.0 → dulus-3.14.2}/batch_api.py +0 -0
  30. {dulus-3.14.0 → dulus-3.14.2}/checkpoint/__init__.py +0 -0
  31. {dulus-3.14.0 → dulus-3.14.2}/checkpoint/hooks.py +0 -0
  32. {dulus-3.14.0 → dulus-3.14.2}/checkpoint/store.py +0 -0
  33. {dulus-3.14.0 → dulus-3.14.2}/checkpoint/types.py +0 -0
  34. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/__init__.py +0 -0
  35. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/__main__.py +0 -0
  36. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/ansi.py +0 -0
  37. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/assets/ascii/dulus_logo.txt +0 -0
  38. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/assets/ascii/eagle.txt +0 -0
  39. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/assets/ascii/terminal_art.txt +0 -0
  40. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/banners.py +0 -0
  41. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/demo.py +0 -0
  42. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/effects.py +0 -0
  43. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/effort_slider.py +0 -0
  44. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/progress.py +0 -0
  45. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/py.typed +0 -0
  46. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/spinners.py +0 -0
  47. {dulus-3.14.0 → dulus-3.14.2}/cli_animations/status.py +0 -0
  48. {dulus-3.14.0 → dulus-3.14.2}/clipboard_utils.py +0 -0
  49. {dulus-3.14.0 → dulus-3.14.2}/cloudsave.py +0 -0
  50. {dulus-3.14.0 → dulus-3.14.2}/common.py +0 -0
  51. {dulus-3.14.0 → dulus-3.14.2}/compaction.py +0 -0
  52. {dulus-3.14.0 → dulus-3.14.2}/config.py +0 -0
  53. {dulus-3.14.0 → dulus-3.14.2}/context.py +0 -0
  54. {dulus-3.14.0 → dulus-3.14.2}/data/__init__.py +0 -0
  55. {dulus-3.14.0 → dulus-3.14.2}/data/active_persona.json +0 -0
  56. {dulus-3.14.0 → dulus-3.14.2}/data/marketplace.json +0 -0
  57. {dulus-3.14.0 → dulus-3.14.2}/data/personas.json +0 -0
  58. {dulus-3.14.0 → dulus-3.14.2}/data/plugins/__init__.py +0 -0
  59. {dulus-3.14.0 → dulus-3.14.2}/data/plugins/composio/__init__.py +0 -0
  60. {dulus-3.14.0 → dulus-3.14.2}/data/plugins/composio/composio_plugin/__init__.py +0 -0
  61. {dulus-3.14.0 → dulus-3.14.2}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
  62. {dulus-3.14.0 → dulus-3.14.2}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
  63. {dulus-3.14.0 → dulus-3.14.2}/data/plugins/composio/plugin.json +0 -0
  64. {dulus-3.14.0 → dulus-3.14.2}/data/plugins/composio/plugin_tool.py +0 -0
  65. {dulus-3.14.0 → dulus-3.14.2}/data/tasks.json +0 -0
  66. {dulus-3.14.0 → dulus-3.14.2}/display_blocks.py +0 -0
  67. {dulus-3.14.0 → dulus-3.14.2}/docs/API.md +0 -0
  68. {dulus-3.14.0 → dulus-3.14.2}/docs/BRAND_GUIDE.md +0 -0
  69. {dulus-3.14.0 → dulus-3.14.2}/docs/CHANGELOG.md +0 -0
  70. {dulus-3.14.0 → dulus-3.14.2}/docs/CONTRIBUTING.md +0 -0
  71. {dulus-3.14.0 → dulus-3.14.2}/docs/DEPLOYMENT.md +0 -0
  72. {dulus-3.14.0 → dulus-3.14.2}/docs/FAQ.md +0 -0
  73. {dulus-3.14.0 → dulus-3.14.2}/docs/GETTING_STARTED.md +0 -0
  74. {dulus-3.14.0 → dulus-3.14.2}/docs/README.md +0 -0
  75. {dulus-3.14.0 → dulus-3.14.2}/docs/README_AR.md +0 -0
  76. {dulus-3.14.0 → dulus-3.14.2}/docs/README_EN.md +0 -0
  77. {dulus-3.14.0 → dulus-3.14.2}/docs/README_ES.md +0 -0
  78. {dulus-3.14.0 → dulus-3.14.2}/docs/README_FR.md +0 -0
  79. {dulus-3.14.0 → dulus-3.14.2}/docs/README_JA.md +0 -0
  80. {dulus-3.14.0 → dulus-3.14.2}/docs/README_KO.md +0 -0
  81. {dulus-3.14.0 → dulus-3.14.2}/docs/README_PT.md +0 -0
  82. {dulus-3.14.0 → dulus-3.14.2}/docs/README_RU.md +0 -0
  83. {dulus-3.14.0 → dulus-3.14.2}/docs/README_ZH.md +0 -0
  84. {dulus-3.14.0 → dulus-3.14.2}/docs/SECURITY.md +0 -0
  85. {dulus-3.14.0 → dulus-3.14.2}/docs/__init__.py +0 -0
  86. {dulus-3.14.0 → dulus-3.14.2}/docs/api.html +0 -0
  87. {dulus-3.14.0 → dulus-3.14.2}/docs/architecture.md +0 -0
  88. {dulus-3.14.0 → dulus-3.14.2}/docs/azure-speech-template.json +0 -0
  89. {dulus-3.14.0 → dulus-3.14.2}/docs/dashboard/index.html +0 -0
  90. {dulus-3.14.0 → dulus-3.14.2}/docs/divider.svg +0 -0
  91. {dulus-3.14.0 → dulus-3.14.2}/docs/dulus-bird.png +0 -0
  92. {dulus-3.14.0 → dulus-3.14.2}/docs/generate.py +0 -0
  93. {dulus-3.14.0 → dulus-3.14.2}/docs/index.html +0 -0
  94. {dulus-3.14.0 → dulus-3.14.2}/docs/nvidia-models.svg +0 -0
  95. {dulus-3.14.0 → dulus-3.14.2}/docs/particle-playground.html +0 -0
  96. {dulus-3.14.0 → dulus-3.14.2}/docs/personas/index.html +0 -0
  97. {dulus-3.14.0 → dulus-3.14.2}/docs/poetry-banner.png +0 -0
  98. {dulus-3.14.0 → dulus-3.14.2}/docs/preview.html +0 -0
  99. {dulus-3.14.0 → dulus-3.14.2}/docs/python-console.md +0 -0
  100. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/auto-adapter.png +0 -0
  101. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/dulus-os.png +0 -0
  102. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/hero.png +0 -0
  103. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/lookback_two_windows.png +0 -0
  104. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/pyc_heap.svg +0 -0
  105. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/pyc_receipts.svg +0 -0
  106. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/pyc_trap.svg +0 -0
  107. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/roundtable.png +0 -0
  108. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/task-board.png +0 -0
  109. {dulus-3.14.0 → dulus-3.14.2}/docs/readme/webchat.png +0 -0
  110. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-agents.svg +0 -0
  111. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-brainstorm.svg +0 -0
  112. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-bridges.svg +0 -0
  113. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-features.svg +0 -0
  114. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-freetier.svg +0 -0
  115. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-memory.svg +0 -0
  116. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-models.svg +0 -0
  117. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-perms.svg +0 -0
  118. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-plugins.svg +0 -0
  119. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-quickstart.svg +0 -0
  120. {dulus-3.14.0 → dulus-3.14.2}/docs/sec-ssj.svg +0 -0
  121. {dulus-3.14.0 → dulus-3.14.2}/docs/spinners.svg +0 -0
  122. {dulus-3.14.0 → dulus-3.14.2}/docs/split-pane.svg +0 -0
  123. {dulus-3.14.0 → dulus-3.14.2}/docs/terminal-boot.svg +0 -0
  124. {dulus-3.14.0 → dulus-3.14.2}/docs/termux-edge.md +0 -0
  125. {dulus-3.14.0 → dulus-3.14.2}/dulus.egg-info/SOURCES.txt +0 -0
  126. {dulus-3.14.0 → dulus-3.14.2}/dulus.egg-info/dependency_links.txt +0 -0
  127. {dulus-3.14.0 → dulus-3.14.2}/dulus.egg-info/entry_points.txt +0 -0
  128. {dulus-3.14.0 → dulus-3.14.2}/dulus.egg-info/top_level.txt +0 -0
  129. {dulus-3.14.0 → dulus-3.14.2}/dulus_account.py +0 -0
  130. {dulus-3.14.0 → dulus-3.14.2}/dulus_bar_client.py +0 -0
  131. {dulus-3.14.0 → dulus-3.14.2}/dulus_gui.py +0 -0
  132. {dulus-3.14.0 → dulus-3.14.2}/dulus_mcp/__init__.py +0 -0
  133. {dulus-3.14.0 → dulus-3.14.2}/dulus_mcp/client.py +0 -0
  134. {dulus-3.14.0 → dulus-3.14.2}/dulus_mcp/commands.py +0 -0
  135. {dulus-3.14.0 → dulus-3.14.2}/dulus_mcp/config.py +0 -0
  136. {dulus-3.14.0 → dulus-3.14.2}/dulus_mcp/hub.py +0 -0
  137. {dulus-3.14.0 → dulus-3.14.2}/dulus_mcp/tools.py +0 -0
  138. {dulus-3.14.0 → dulus-3.14.2}/dulus_mcp/types.py +0 -0
  139. {dulus-3.14.0 → dulus-3.14.2}/dulus_quick_menu.py +0 -0
  140. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/__init__.py +0 -0
  141. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/add_dir_manager.py +0 -0
  142. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/afk_mode.py +0 -0
  143. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/approval_runtime.py +0 -0
  144. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/background_tasks.py +0 -0
  145. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/clipboard_utils.py +0 -0
  146. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/diff_visualizer.py +0 -0
  147. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/export_import.py +0 -0
  148. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/hook_engine.py +0 -0
  149. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/isolate.py +0 -0
  150. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/notification_manager.py +0 -0
  151. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/session_fork.py +0 -0
  152. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/shell_mode.py +0 -0
  153. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/todo_visualizer.py +0 -0
  154. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/tree_ls.py +0 -0
  155. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/wire_events.py +0 -0
  156. {dulus-3.14.0 → dulus-3.14.2}/dulus_tools/yolo_mode.py +0 -0
  157. {dulus-3.14.0 → dulus-3.14.2}/file_filter.py +0 -0
  158. {dulus-3.14.0 → dulus-3.14.2}/fs_compass.py +0 -0
  159. {dulus-3.14.0 → dulus-3.14.2}/governance.py +0 -0
  160. {dulus-3.14.0 → dulus-3.14.2}/gui/__init__.py +0 -0
  161. {dulus-3.14.0 → dulus-3.14.2}/gui/agent_bridge.py +0 -0
  162. {dulus-3.14.0 → dulus-3.14.2}/gui/chat_widget.py +0 -0
  163. {dulus-3.14.0 → dulus-3.14.2}/gui/main_window.py +0 -0
  164. {dulus-3.14.0 → dulus-3.14.2}/gui/personas.py +0 -0
  165. {dulus-3.14.0 → dulus-3.14.2}/gui/sandbox_server.py +0 -0
  166. {dulus-3.14.0 → dulus-3.14.2}/gui/session_utils.py +0 -0
  167. {dulus-3.14.0 → dulus-3.14.2}/gui/settings_dialog.py +0 -0
  168. {dulus-3.14.0 → dulus-3.14.2}/gui/sidebar.py +0 -0
  169. {dulus-3.14.0 → dulus-3.14.2}/gui/tasks_view.py +0 -0
  170. {dulus-3.14.0 → dulus-3.14.2}/gui/themes.py +0 -0
  171. {dulus-3.14.0 → dulus-3.14.2}/gui/tool_panel.py +0 -0
  172. {dulus-3.14.0 → dulus-3.14.2}/input.py +0 -0
  173. {dulus-3.14.0 → dulus-3.14.2}/license_manager.py +0 -0
  174. {dulus-3.14.0 → dulus-3.14.2}/lookback.py +0 -0
  175. {dulus-3.14.0 → dulus-3.14.2}/memory/__init__.py +0 -0
  176. {dulus-3.14.0 → dulus-3.14.2}/memory/audit.py +0 -0
  177. {dulus-3.14.0 → dulus-3.14.2}/memory/consolidator.py +0 -0
  178. {dulus-3.14.0 → dulus-3.14.2}/memory/context.py +0 -0
  179. {dulus-3.14.0 → dulus-3.14.2}/memory/mempalace_bridge.py +0 -0
  180. {dulus-3.14.0 → dulus-3.14.2}/memory/offload.py +0 -0
  181. {dulus-3.14.0 → dulus-3.14.2}/memory/palace.py +0 -0
  182. {dulus-3.14.0 → dulus-3.14.2}/memory/scan.py +0 -0
  183. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/adaptation_guides.md +0 -0
  184. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/askuserquestion_after_numbered_list.md +0 -0
  185. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/background_jobs_no_extra_timers.md +0 -0
  186. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/cleverness_over_resources.md +0 -0
  187. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/corporate_tls_breaks_curl_cffi.md +0 -0
  188. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/hardened_rules.md +0 -0
  189. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/investigator_workflow_heuristic.md +0 -0
  190. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/native_code_over_sketchy_plugins.md +0 -0
  191. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/no_personal_info_in_code.md +0 -0
  192. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/searches_include_modified_date.md +0 -0
  193. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/tmux_send_keys_power_patterns.md +0 -0
  194. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/token_optimization_technique.md +0 -0
  195. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/tool_chaining_capability.md +0 -0
  196. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/insights/webbridge_evaluate_first_pattern.md +0 -0
  197. {dulus-3.14.0 → dulus-3.14.2}/memory/seeds/short_memory.md +0 -0
  198. {dulus-3.14.0 → dulus-3.14.2}/memory/sessions.py +0 -0
  199. {dulus-3.14.0 → dulus-3.14.2}/memory/store.py +0 -0
  200. {dulus-3.14.0 → dulus-3.14.2}/memory/tools.py +0 -0
  201. {dulus-3.14.0 → dulus-3.14.2}/memory/types.py +0 -0
  202. {dulus-3.14.0 → dulus-3.14.2}/memory/vector_search.py +0 -0
  203. {dulus-3.14.0 → dulus-3.14.2}/model_params.py +0 -0
  204. {dulus-3.14.0 → dulus-3.14.2}/multi_agent/__init__.py +0 -0
  205. {dulus-3.14.0 → dulus-3.14.2}/multi_agent/subagent.py +0 -0
  206. {dulus-3.14.0 → dulus-3.14.2}/multi_agent/tools.py +0 -0
  207. {dulus-3.14.0 → dulus-3.14.2}/paste_placeholders.py +0 -0
  208. {dulus-3.14.0 → dulus-3.14.2}/personality_config.py +0 -0
  209. {dulus-3.14.0 → dulus-3.14.2}/plugin/__init__.py +0 -0
  210. {dulus-3.14.0 → dulus-3.14.2}/plugin/autoadapter.py +0 -0
  211. {dulus-3.14.0 → dulus-3.14.2}/plugin/loader.py +0 -0
  212. {dulus-3.14.0 → dulus-3.14.2}/plugin/recommend.py +0 -0
  213. {dulus-3.14.0 → dulus-3.14.2}/plugin/store.py +0 -0
  214. {dulus-3.14.0 → dulus-3.14.2}/plugin/types.py +0 -0
  215. {dulus-3.14.0 → dulus-3.14.2}/profiles.py +0 -0
  216. {dulus-3.14.0 → dulus-3.14.2}/pyconsole.py +0 -0
  217. {dulus-3.14.0 → dulus-3.14.2}/sandbox/README.md +0 -0
  218. {dulus-3.14.0 → dulus-3.14.2}/sandbox/__init__.py +0 -0
  219. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
  220. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
  221. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/index.html +0 -0
  222. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/wallpaper-default.jpg +0 -0
  223. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/wallpapers/default.jpeg +0 -0
  224. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/wallpapers/light.jpeg +0 -0
  225. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/wallpapers/nature.jpeg +0 -0
  226. {dulus-3.14.0 → dulus-3.14.2}/sandbox/dist/wallpapers/tech.jpeg +0 -0
  227. {dulus-3.14.0 → dulus-3.14.2}/sandbox/info.md +0 -0
  228. {dulus-3.14.0 → dulus-3.14.2}/sandbox_bootstrap.py +0 -0
  229. {dulus-3.14.0 → dulus-3.14.2}/setup.cfg +0 -0
  230. {dulus-3.14.0 → dulus-3.14.2}/skill/__init__.py +0 -0
  231. {dulus-3.14.0 → dulus-3.14.2}/skill/builtin.py +0 -0
  232. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/LICENSE-obsidian-skills +0 -0
  233. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/README.md +0 -0
  234. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/defuddle/SKILL.md +0 -0
  235. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/json-canvas/SKILL.md +0 -0
  236. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/json-canvas/references/EXAMPLES.md +0 -0
  237. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/obsidian-bases/SKILL.md +0 -0
  238. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +0 -0
  239. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/obsidian-cli/SKILL.md +0 -0
  240. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/obsidian-markdown/SKILL.md +0 -0
  241. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/obsidian-markdown/references/CALLOUTS.md +0 -0
  242. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/obsidian-markdown/references/EMBEDS.md +0 -0
  243. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/obsidian-markdown/references/PROPERTIES.md +0 -0
  244. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/README.md +0 -0
  245. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/SKILL.md +0 -0
  246. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/addresses.json +0 -0
  247. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/advancedbackgroundchecks.json +0 -0
  248. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/beenverified.json +0 -0
  249. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/clustal.json +0 -0
  250. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/clustrmaps.json +0 -0
  251. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/cyberbackgroundchecks.json +0 -0
  252. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/familytreenow.json +0 -0
  253. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/fastpeoplesearch.json +0 -0
  254. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/intelius.json +0 -0
  255. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/mylife.json +0 -0
  256. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/nuwber.json +0 -0
  257. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/peekyou.json +0 -0
  258. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/peoplefinders.json +0 -0
  259. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/radaris.json +0 -0
  260. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/rehold.json +0 -0
  261. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/searchpeoplefree.json +0 -0
  262. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/socialcatfish.json +0 -0
  263. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/spokeo.json +0 -0
  264. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/thatsthem.json +0 -0
  265. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/truepeoplesearch.json +0 -0
  266. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/usphonebook.json +0 -0
  267. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/brokers/whitepages.json +0 -0
  268. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/legal/ccpa.md +0 -0
  269. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/legal/drop.md +0 -0
  270. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/legal/gdpr.md +0 -0
  271. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/methods.md +0 -0
  272. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/site-playbooks.md +0 -0
  273. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/references/state-machine.md +0 -0
  274. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/autopilot.py +0 -0
  275. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/badbool.py +0 -0
  276. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/brokers.py +0 -0
  277. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/cdp.py +0 -0
  278. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/config.py +0 -0
  279. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/crypto.py +0 -0
  280. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/dossier.py +0 -0
  281. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/email_modes.py +0 -0
  282. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/emailer.py +0 -0
  283. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/ledger.py +0 -0
  284. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/legal.py +0 -0
  285. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/paths.py +0 -0
  286. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/pdd.py +0 -0
  287. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/registry.py +0 -0
  288. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/report.py +0 -0
  289. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/scan.py +0 -0
  290. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/storage.py +0 -0
  291. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/tiers.py +0 -0
  292. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/scripts/vectors.py +0 -0
  293. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/templates/consent/authorization.md +0 -0
  294. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/templates/emails/ccpa-authorized-agent.txt +0 -0
  295. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/templates/emails/ccpa-deletion.txt +0 -0
  296. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/templates/emails/ccpa-indirect-deletion.txt +0 -0
  297. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/templates/emails/gdpr-erasure.txt +0 -0
  298. {dulus-3.14.0 → dulus-3.14.2}/skill/bundled/unbroker/templates/emails/generic-optout.txt +0 -0
  299. {dulus-3.14.0 → dulus-3.14.2}/skill/clawhub.py +0 -0
  300. {dulus-3.14.0 → dulus-3.14.2}/skill/executor.py +0 -0
  301. {dulus-3.14.0 → dulus-3.14.2}/skill/loader.py +0 -0
  302. {dulus-3.14.0 → dulus-3.14.2}/skill/tools.py +0 -0
  303. {dulus-3.14.0 → dulus-3.14.2}/skills.py +0 -0
  304. {dulus-3.14.0 → dulus-3.14.2}/soul.py +0 -0
  305. {dulus-3.14.0 → dulus-3.14.2}/spinner.py +0 -0
  306. {dulus-3.14.0 → dulus-3.14.2}/steer_input.py +0 -0
  307. {dulus-3.14.0 → dulus-3.14.2}/subagent.py +0 -0
  308. {dulus-3.14.0 → dulus-3.14.2}/task/__init__.py +0 -0
  309. {dulus-3.14.0 → dulus-3.14.2}/task/store.py +0 -0
  310. {dulus-3.14.0 → dulus-3.14.2}/task/tools.py +0 -0
  311. {dulus-3.14.0 → dulus-3.14.2}/task/types.py +0 -0
  312. {dulus-3.14.0 → dulus-3.14.2}/tests/test_afk_yolo.py +0 -0
  313. {dulus-3.14.0 → dulus-3.14.2}/tests/test_approval_runtime.py +0 -0
  314. {dulus-3.14.0 → dulus-3.14.2}/tests/test_background_task_tools.py +0 -0
  315. {dulus-3.14.0 → dulus-3.14.2}/tests/test_background_tasks.py +0 -0
  316. {dulus-3.14.0 → dulus-3.14.2}/tests/test_chatgpt_oauth.py +0 -0
  317. {dulus-3.14.0 → dulus-3.14.2}/tests/test_checkpoint.py +0 -0
  318. {dulus-3.14.0 → dulus-3.14.2}/tests/test_cli_animations_integration.py +0 -0
  319. {dulus-3.14.0 → dulus-3.14.2}/tests/test_clipboard_utils.py +0 -0
  320. {dulus-3.14.0 → dulus-3.14.2}/tests/test_compaction.py +0 -0
  321. {dulus-3.14.0 → dulus-3.14.2}/tests/test_composio_tool_generator.py +0 -0
  322. {dulus-3.14.0 → dulus-3.14.2}/tests/test_diff_view.py +0 -0
  323. {dulus-3.14.0 → dulus-3.14.2}/tests/test_diff_visualization.py +0 -0
  324. {dulus-3.14.0 → dulus-3.14.2}/tests/test_display_blocks.py +0 -0
  325. {dulus-3.14.0 → dulus-3.14.2}/tests/test_dulus_account.py +0 -0
  326. {dulus-3.14.0 → dulus-3.14.2}/tests/test_export_import.py +0 -0
  327. {dulus-3.14.0 → dulus-3.14.2}/tests/test_gold_memory_preload.py +0 -0
  328. {dulus-3.14.0 → dulus-3.14.2}/tests/test_grok_usage.py +0 -0
  329. {dulus-3.14.0 → dulus-3.14.2}/tests/test_hook_engine.py +0 -0
  330. {dulus-3.14.0 → dulus-3.14.2}/tests/test_injection_fix.py +0 -0
  331. {dulus-3.14.0 → dulus-3.14.2}/tests/test_json_protocol.py +0 -0
  332. {dulus-3.14.0 → dulus-3.14.2}/tests/test_license.py +0 -0
  333. {dulus-3.14.0 → dulus-3.14.2}/tests/test_lookback_cache.py +0 -0
  334. {dulus-3.14.0 → dulus-3.14.2}/tests/test_lookback_compact.py +0 -0
  335. {dulus-3.14.0 → dulus-3.14.2}/tests/test_mcp.py +0 -0
  336. {dulus-3.14.0 → dulus-3.14.2}/tests/test_mcp_algolia.py +0 -0
  337. {dulus-3.14.0 → dulus-3.14.2}/tests/test_memory.py +0 -0
  338. {dulus-3.14.0 → dulus-3.14.2}/tests/test_memory_home_resolution.py +0 -0
  339. {dulus-3.14.0 → dulus-3.14.2}/tests/test_notification_manager.py +0 -0
  340. {dulus-3.14.0 → dulus-3.14.2}/tests/test_plugin.py +0 -0
  341. {dulus-3.14.0 → dulus-3.14.2}/tests/test_profile_isolation.py +0 -0
  342. {dulus-3.14.0 → dulus-3.14.2}/tests/test_profile_plugin_scope.py +0 -0
  343. {dulus-3.14.0 → dulus-3.14.2}/tests/test_provider_errors.py +0 -0
  344. {dulus-3.14.0 → dulus-3.14.2}/tests/test_session_fork.py +0 -0
  345. {dulus-3.14.0 → dulus-3.14.2}/tests/test_shell_detection.py +0 -0
  346. {dulus-3.14.0 → dulus-3.14.2}/tests/test_shell_mode.py +0 -0
  347. {dulus-3.14.0 → dulus-3.14.2}/tests/test_skills.py +0 -0
  348. {dulus-3.14.0 → dulus-3.14.2}/tests/test_skillssh_algolia.py +0 -0
  349. {dulus-3.14.0 → dulus-3.14.2}/tests/test_steer_input.py +0 -0
  350. {dulus-3.14.0 → dulus-3.14.2}/tests/test_subagent.py +0 -0
  351. {dulus-3.14.0 → dulus-3.14.2}/tests/test_task.py +0 -0
  352. {dulus-3.14.0 → dulus-3.14.2}/tests/test_telegram_buffer.py +0 -0
  353. {dulus-3.14.0 → dulus-3.14.2}/tests/test_think_tool.py +0 -0
  354. {dulus-3.14.0 → dulus-3.14.2}/tests/test_todo_tool.py +0 -0
  355. {dulus-3.14.0 → dulus-3.14.2}/tests/test_todo_visualization.py +0 -0
  356. {dulus-3.14.0 → dulus-3.14.2}/tests/test_tool_registry.py +0 -0
  357. {dulus-3.14.0 → dulus-3.14.2}/tests/test_tree_ls.py +0 -0
  358. {dulus-3.14.0 → dulus-3.14.2}/tests/test_voice.py +0 -0
  359. {dulus-3.14.0 → dulus-3.14.2}/tests/test_webbridge_tabs.py +0 -0
  360. {dulus-3.14.0 → dulus-3.14.2}/tests/test_wire_events.py +0 -0
  361. {dulus-3.14.0 → dulus-3.14.2}/tmux_tools.py +0 -0
  362. {dulus-3.14.0 → dulus-3.14.2}/token_budget.py +0 -0
  363. {dulus-3.14.0 → dulus-3.14.2}/tool_registry.py +0 -0
  364. {dulus-3.14.0 → dulus-3.14.2}/tools.py +0 -0
  365. {dulus-3.14.0 → dulus-3.14.2}/tools_background.py +0 -0
  366. {dulus-3.14.0 → dulus-3.14.2}/tools_think.py +0 -0
  367. {dulus-3.14.0 → dulus-3.14.2}/tools_todo.py +0 -0
  368. {dulus-3.14.0 → dulus-3.14.2}/ui/__init__.py +0 -0
  369. {dulus-3.14.0 → dulus-3.14.2}/ui/input.py +0 -0
  370. {dulus-3.14.0 → dulus-3.14.2}/ui/render.py +0 -0
  371. {dulus-3.14.0 → dulus-3.14.2}/updater.py +0 -0
  372. {dulus-3.14.0 → dulus-3.14.2}/voice/__init__.py +0 -0
  373. {dulus-3.14.0 → dulus-3.14.2}/voice/audio_utils.py +0 -0
  374. {dulus-3.14.0 → dulus-3.14.2}/voice/keyterms.py +0 -0
  375. {dulus-3.14.0 → dulus-3.14.2}/voice/recorder.py +0 -0
  376. {dulus-3.14.0 → dulus-3.14.2}/voice/stt.py +0 -0
  377. {dulus-3.14.0 → dulus-3.14.2}/voice/tts.py +0 -0
  378. {dulus-3.14.0 → dulus-3.14.2}/voice/wake_word.py +0 -0
  379. {dulus-3.14.0 → dulus-3.14.2}/webbridge/__init__.py +0 -0
  380. {dulus-3.14.0 → dulus-3.14.2}/webbridge/core.py +0 -0
  381. {dulus-3.14.0 → dulus-3.14.2}/webbridge/tools.py +0 -0
  382. {dulus-3.14.0 → dulus-3.14.2}/webchat.py +0 -0
  383. {dulus-3.14.0 → dulus-3.14.2}/webchat_server.py +0 -0
  384. {dulus-3.14.0 → dulus-3.14.2}/webchat_ui/__init__.py +0 -0
  385. {dulus-3.14.0 → dulus-3.14.2}/webchat_ui/app.js +0 -0
  386. {dulus-3.14.0 → dulus-3.14.2}/webchat_ui/index.html +0 -0
  387. {dulus-3.14.0 → dulus-3.14.2}/webchat_ui/style.css +0 -0
  388. {dulus-3.14.0 → dulus-3.14.2}/welcome.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dulus
3
- Version: 3.14.0
3
+ Version: 3.14.2
4
4
  Summary: Provider-independent agent runtime for the terminal — cloud and local models, real tools, MCP, plugins, memory, sub-agents, and Dulus OS.
5
5
  Author-email: KevRojo <dev@dulus.ai>
6
6
  Maintainer-email: KevRojo <dev@dulus.ai>
@@ -65,6 +65,12 @@ Requires-Dist: mcp>=1.0.0
65
65
  Requires-Dist: uv>=0.4.0
66
66
  Requires-Dist: sse-starlette>=2.0.0
67
67
  Requires-Dist: starlette>=0.38.0
68
+ Requires-Dist: posthog>=3.0.0
69
+ Requires-Dist: mempalace>=3.3.4
70
+ Requires-Dist: sounddevice
71
+ Requires-Dist: elevenlabs>=1.0.0
72
+ Requires-Dist: playwright>=1.40.0
73
+ Requires-Dist: litellm>=1.50.0
68
74
  Provides-Extra: dev
69
75
  Requires-Dist: build>=1.2; extra == "dev"
70
76
  Requires-Dist: pyright>=1.1.400; extra == "dev"
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## 🔥🔥🔥 News (Pacific Time)
4
4
 
5
- - September 2, 2026 (**v6.0.5**): **🚀 Dulus 6.0.5 is live.** The newest build just landed — same free open runtime, sharper and faster. No API key, no credit card, your machine. Grab it: `pip install -U dulus`. 🦅🇩🇴
5
+ - September 2, 2026 (**v3.14.1**): **🌐 Web-harvest browser fix.** `/harvest-gemini` (and claude/kimi/qwen/deepseek) no longer launch without a browser after the first run. The installer used to key off the *pip package*, so once the wizard installed it, later harvests skipped the *browser* download entirely — leaving you with a package but no Chromium. Now a single idempotent guard ensures both the package and the browser are present every time. `pip install -U dulus`. 🦅
6
+
7
+ - September 3, 2026 (**v3.14.2**): **📦 One-shot install — everything's in the box now.** No more `dulus[full]` archaeology: `pip install dulus` now pulls the whole runtime by default — memory palace, voice, browser automation, LiteLLM routing and analytics all come along. Plus a new **Meta AI (Muse Spark)** provider (`meta/muse-spark-1.3`). Same free open runtime, your machine. `pip install -U dulus`. 🦅🇩🇴
6
8
 
7
9
  - September 1, 2026 (**v3.13.9**): **🔒 `/isolate` — workspace bubble, on demand.** Flip `/isolate on` and Write / Edit / NotebookEdit + Bash mutations stay locked to the frozen workspace root (plus anything you added with `/add-dir`). Read-only tools keep flying free. No `/temp` backdoor — pure bubble. Blocked? Dulus tells you straight: write `/isolate off` if you want help outside. `pip install -U dulus`. 🦅
8
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dulus
3
- Version: 3.14.0
3
+ Version: 3.14.2
4
4
  Summary: Provider-independent agent runtime for the terminal — cloud and local models, real tools, MCP, plugins, memory, sub-agents, and Dulus OS.
5
5
  Author-email: KevRojo <dev@dulus.ai>
6
6
  Maintainer-email: KevRojo <dev@dulus.ai>
@@ -65,6 +65,12 @@ Requires-Dist: mcp>=1.0.0
65
65
  Requires-Dist: uv>=0.4.0
66
66
  Requires-Dist: sse-starlette>=2.0.0
67
67
  Requires-Dist: starlette>=0.38.0
68
+ Requires-Dist: posthog>=3.0.0
69
+ Requires-Dist: mempalace>=3.3.4
70
+ Requires-Dist: sounddevice
71
+ Requires-Dist: elevenlabs>=1.0.0
72
+ Requires-Dist: playwright>=1.40.0
73
+ Requires-Dist: litellm>=1.50.0
68
74
  Provides-Extra: dev
69
75
  Requires-Dist: build>=1.2; extra == "dev"
70
76
  Requires-Dist: pyright>=1.1.400; extra == "dev"
@@ -21,6 +21,12 @@ mcp>=1.0.0
21
21
  uv>=0.4.0
22
22
  sse-starlette>=2.0.0
23
23
  starlette>=0.38.0
24
+ posthog>=3.0.0
25
+ mempalace>=3.3.4
26
+ sounddevice
27
+ elevenlabs>=1.0.0
28
+ playwright>=1.40.0
29
+ litellm>=1.50.0
24
30
 
25
31
  [:python_version < "3.11"]
26
32
  tomli>=2.0
@@ -396,7 +396,7 @@ try:
396
396
  from importlib.metadata import version as _pkg_version
397
397
  VERSION = _pkg_version("dulus")
398
398
  except Exception:
399
- VERSION = "3.14.0" # dev fallback — keep in sync with pyproject.toml
399
+ VERSION = "3.14.2" # dev fallback — keep in sync with pyproject.toml
400
400
 
401
401
  # ── Machine-readable protocol output (`--output json`) ─────────────────────
402
402
  # Dulus is increasingly run as a child process by another agent runtime (an
@@ -3667,14 +3667,8 @@ def cmd_harvest(_args: str, _state, config) -> bool:
3667
3667
  out_path = pathlib.Path.home() / ".dulus" / "claude_cookies.json"
3668
3668
  ok(f"Starting Playwright harvest → {out_path}")
3669
3669
 
3670
- try:
3671
- from playwright.sync_api import sync_playwright
3672
- except ImportError:
3673
- import os
3674
- info("Installing playwright...")
3675
- __import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
3676
- os.system("playwright install chromium")
3677
- from playwright.sync_api import sync_playwright
3670
+ _ensure_playwright_browser()
3671
+ from playwright.sync_api import sync_playwright
3678
3672
 
3679
3673
  import os, time
3680
3674
  from datetime import datetime
@@ -3813,13 +3807,8 @@ def cmd_harvest_kimi(_args: str, _state, config) -> bool:
3813
3807
  out_path = pathlib.Path.home() / ".dulus" / "kimi_consumer.json"
3814
3808
  ok(f"Starting Kimi Harvester → {out_path}")
3815
3809
 
3816
- try:
3817
- from playwright.sync_api import sync_playwright
3818
- except ImportError:
3819
- info("Installing playwright...")
3820
- __import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
3821
- os.system("playwright install chromium")
3822
- from playwright.sync_api import sync_playwright
3810
+ _ensure_playwright_browser()
3811
+ from playwright.sync_api import sync_playwright
3823
3812
 
3824
3813
  pw_profile = os.path.join(os.path.expanduser("~"), ".dulus", "playwright", "kimi-consumer")
3825
3814
  os.makedirs(pw_profile, exist_ok=True)
@@ -3910,6 +3899,39 @@ def cmd_harvest_kimi(_args: str, _state, config) -> bool:
3910
3899
  return True
3911
3900
 
3912
3901
 
3902
+ def _ensure_playwright_browser():
3903
+ """Guarantee BOTH the playwright package AND a browser binary are present.
3904
+
3905
+ The old per-harvest snippet only ran the installer inside `except
3906
+ ImportError`, i.e. it keyed off the *pip package* being importable. But
3907
+ the package and the *browser binary* are two separate installs: after the
3908
+ first-run wizard installs the package, a later `/harvest-*` sees the import
3909
+ succeed, skips the whole block, and never (re)installs the browser — so if
3910
+ the Chromium/Chrome download was missing or half-done, the harvest launches
3911
+ with no browser and fails. The welcome only "worked" because it was the
3912
+ first run, where the import genuinely failed and the full install fired.
3913
+
3914
+ Fix: check the import, install the package if needed, and ALWAYS run
3915
+ `playwright install chromium` — it's idempotent (a no-op when the browser
3916
+ is already there, a real download when it isn't), so it's cheap to call
3917
+ every time and closes the package-vs-browser gap for good.
3918
+ """
3919
+ import os, subprocess, sys as _sys
3920
+ try:
3921
+ from playwright.sync_api import sync_playwright # noqa: F401
3922
+ except ImportError:
3923
+ info("Installing playwright...")
3924
+ subprocess.run(__import__("common").pip_install_cmd("playwright"))
3925
+ # Idempotent: installs the browser only if it's missing. Runs every time so
3926
+ # a present package with an absent/broken browser still gets fixed.
3927
+ try:
3928
+ subprocess.run([_sys.executable, "-m", "playwright", "install", "chromium"],
3929
+ check=False, timeout=600)
3930
+ except Exception:
3931
+ # Fallback to the plain CLI if `-m playwright` isn't on this interpreter.
3932
+ os.system("playwright install chromium")
3933
+
3934
+
3913
3935
  def _launch_harvest_browser(p, pw_profile, headless):
3914
3936
  """Launch a persistent browser for harvesting, resilient to a bare box.
3915
3937
 
@@ -3985,13 +4007,8 @@ def cmd_harvest_gemini(_args: str, _state, config) -> "bool | None":
3985
4007
  out_path = pathlib.Path.home() / ".dulus" / "gemini_web.json"
3986
4008
  ok(f"Starting Gemini Harvester → {out_path}")
3987
4009
 
3988
- try:
3989
- from playwright.sync_api import sync_playwright
3990
- except ImportError:
3991
- info("Installing playwright...")
3992
- __import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
3993
- os.system("playwright install chromium")
3994
- from playwright.sync_api import sync_playwright
4010
+ _ensure_playwright_browser()
4011
+ from playwright.sync_api import sync_playwright
3995
4012
 
3996
4013
  # Reutiliza el perfil de Gemini para no loguear cada vez
3997
4014
  pw_profile = os.path.join(os.path.expanduser("~"), ".dulus", "playwright", "gemini-interceptor")
@@ -4191,13 +4208,8 @@ def cmd_harvest_deepseek(_args: str, _state, config) -> bool:
4191
4208
  # Optional: navigate directly to a specific chat session from arg
4192
4209
  start_url = _args.strip() if _args.strip().startswith("http") else "https://chat.deepseek.com/"
4193
4210
 
4194
- try:
4195
- from playwright.sync_api import sync_playwright
4196
- except ImportError:
4197
- info("Installing playwright...")
4198
- __import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
4199
- os.system("playwright install chromium")
4200
- from playwright.sync_api import sync_playwright
4211
+ _ensure_playwright_browser()
4212
+ from playwright.sync_api import sync_playwright
4201
4213
 
4202
4214
  pw_profile = os.path.join(os.path.expanduser("~"), ".dulus", "playwright", "deepseek-interceptor")
4203
4215
  os.makedirs(pw_profile, exist_ok=True)
@@ -4340,13 +4352,8 @@ def cmd_harvest_qwen(_args: str, _state, config) -> bool:
4340
4352
 
4341
4353
  start_url = _args.strip() if _args.strip().startswith("http") else "https://chat.qwen.ai/"
4342
4354
 
4343
- try:
4344
- from playwright.sync_api import sync_playwright
4345
- except ImportError:
4346
- info("Installing playwright...")
4347
- __import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
4348
- os.system("playwright install chromium")
4349
- from playwright.sync_api import sync_playwright
4355
+ _ensure_playwright_browser()
4356
+ from playwright.sync_api import sync_playwright
4350
4357
 
4351
4358
  pw_profile = os.path.join(os.path.expanduser("~"), ".dulus", "playwright", "qwen-interceptor")
4352
4359
  os.makedirs(pw_profile, exist_ok=True)
@@ -875,6 +875,19 @@ PROVIDERS: dict[str, dict] = {
875
875
  "fugu-mini", "fugu-ultra",
876
876
  ],
877
877
  },
878
+ # Meta AI (Muse Spark) — Responses-API-style SSE endpoint at api.meta.ai.
879
+ # Key via /config meta_api_key=... or export META_API_KEY.
880
+ # Pick models as 'meta/muse-spark-1.3' (or bare 'muse-spark-1.3').
881
+ "meta": {
882
+ "type": "meta",
883
+ "api_key_env": "META_API_KEY",
884
+ "base_url": "https://api.meta.ai/v1",
885
+ "context_limit": 128000,
886
+ "max_completion_tokens": 32000,
887
+ "models": [
888
+ "muse-spark-1.3", "muse-spark-1.3-contributor",
889
+ ],
890
+ },
878
891
  }
879
892
 
880
893
  # Cost per million tokens (approximate, fallback to 0 for unknown)
@@ -934,6 +947,9 @@ _PREFIXES = [
934
947
  ("gpt-5-codex", "chatgpt-oauth"),
935
948
  ("codex-mini", "chatgpt-oauth"),
936
949
  ("codex-auto", "chatgpt-oauth"),
950
+ # Meta AI (Muse Spark) — api.meta.ai Responses-style SSE, key via META_API_KEY.
951
+ ("meta/", "meta"),
952
+ ("muse-", "meta"),
937
953
  ("claude-", "anthropic"),
938
954
  ("gpt-", "openai"),
939
955
  ("o1", "openai"),
@@ -2842,6 +2858,236 @@ def stream_chatgpt_oauth(
2842
2858
  cache_read_tokens=cached_tok)
2843
2859
 
2844
2860
 
2861
+ # ── Meta AI (Muse Spark) — API key, Responses-API-style SSE ────────────────
2862
+ META_API_BASE_URL = "https://api.meta.ai/v1"
2863
+
2864
+
2865
+ def stream_meta(
2866
+ api_key: str,
2867
+ model: str,
2868
+ system: str,
2869
+ messages: list,
2870
+ tool_schemas: list,
2871
+ config: dict,
2872
+ ) -> Generator:
2873
+ """Meta AI Muse Spark via api.meta.ai/v1/responses (SSE, OpenAI Responses
2874
+ wire format). Key: /config meta_api_key=... or META_API_KEY.
2875
+ Yields TextChunk / ThinkingChunk / AssistantTurn."""
2876
+ import requests as _req
2877
+
2878
+ if not api_key:
2879
+ yield TextChunk(
2880
+ "[meta] No API key. Set one with `/config meta_api_key=...` "
2881
+ "or export META_API_KEY."
2882
+ )
2883
+ yield AssistantTurn(
2884
+ "[meta] No API key. Set META_API_KEY or /config meta_api_key=...",
2885
+ [], 0, 0, error=True,
2886
+ )
2887
+ return
2888
+
2889
+ instructions, input_items = _chatgpt_messages_to_responses_input(system, messages)
2890
+
2891
+ body: dict[str, Any] = {
2892
+ "model": model,
2893
+ "input": input_items,
2894
+ "stream": True,
2895
+ "temperature": 1,
2896
+ "max_output_tokens": 32000,
2897
+ "top_p": 1,
2898
+ }
2899
+ if instructions:
2900
+ body["instructions"] = instructions
2901
+
2902
+ tools = _chatgpt_tools_to_responses(tool_schemas)
2903
+ if tools:
2904
+ body["tools"] = tools
2905
+ if not (config or {}).get("disable_tool_choice"):
2906
+ body["tool_choice"] = "auto"
2907
+
2908
+ base = (PROVIDERS.get("meta", {}).get("base_url") or META_API_BASE_URL).rstrip("/")
2909
+ url = base + "/responses"
2910
+ headers = {
2911
+ "Authorization": f"Bearer {api_key}",
2912
+ "Content-Type": "application/json",
2913
+ "Accept": "text/event-stream",
2914
+ }
2915
+
2916
+ text = ""
2917
+ thinking = ""
2918
+ in_tok = 0
2919
+ out_tok = 0
2920
+ cached_tok = 0
2921
+ tool_buf: dict[str, dict] = {}
2922
+ tool_order: list[str] = []
2923
+ current_call_id: str | None = None
2924
+ stream_interrupted = False
2925
+ resp = None
2926
+
2927
+ def _ensure_tool(call_id: str | None, name: str | None = None) -> str:
2928
+ nonlocal current_call_id
2929
+ cid = call_id or current_call_id or f"call_{len(tool_order)}"
2930
+ if cid not in tool_buf:
2931
+ tool_buf[cid] = {"id": cid, "name": name or "", "args": ""}
2932
+ tool_order.append(cid)
2933
+ elif name and not tool_buf[cid].get("name"):
2934
+ tool_buf[cid]["name"] = name
2935
+ current_call_id = cid
2936
+ return cid
2937
+
2938
+ try:
2939
+ resp = _req.post(url, headers=headers, json=body, stream=True, timeout=300)
2940
+
2941
+ if resp.status_code in (401, 403):
2942
+ body_preview = (resp.text or "")[:300]
2943
+ yield TextChunk(
2944
+ f"[meta] Auth error {resp.status_code}. Check your key: "
2945
+ f"/config meta_api_key=... Detail: {body_preview}"
2946
+ )
2947
+ yield AssistantTurn(
2948
+ f"[meta] Auth error {resp.status_code}. Check meta_api_key.",
2949
+ [], 0, 0, error=True,
2950
+ )
2951
+ return
2952
+ if resp.status_code >= 400:
2953
+ body_preview = (resp.text or "")[:400]
2954
+ yield TextChunk(f"[meta] HTTP {resp.status_code}: {body_preview}")
2955
+ yield AssistantTurn(
2956
+ f"[meta] HTTP {resp.status_code}: {body_preview}",
2957
+ [], 0, 0, error=True,
2958
+ )
2959
+ return
2960
+
2961
+ event_type = ""
2962
+ # NOTE: iterate RAW BYTES (no decode_unicode). requests defaults to
2963
+ # ISO-8859-1 for text/event-stream when the server omits charset, which
2964
+ # mangled UTF-8 accents (á→á, ñ→ñ) in Spanish output. Decoding each
2965
+ # line explicitly as UTF-8 fixes the mojibake.
2966
+ for raw_line in resp.iter_lines():
2967
+ if raw_line is None:
2968
+ continue
2969
+ line = raw_line.decode("utf-8", "replace").strip() if isinstance(raw_line, (bytes, bytearray)) else raw_line.strip()
2970
+ if not line:
2971
+ continue
2972
+ if line.startswith(":"):
2973
+ continue
2974
+ if line.startswith("event:"):
2975
+ event_type = line[6:].strip()
2976
+ continue
2977
+ if not line.startswith("data:"):
2978
+ continue
2979
+ data = line[5:].strip()
2980
+ if data == "[DONE]":
2981
+ break
2982
+ try:
2983
+ event = json.loads(data)
2984
+ except Exception:
2985
+ continue
2986
+ if not isinstance(event, dict):
2987
+ continue
2988
+ et = event.get("type") or event_type or ""
2989
+
2990
+ if et in ("response.output_text.delta", "response.refusal.delta"):
2991
+ delta = event.get("delta") or ""
2992
+ if delta:
2993
+ text += delta
2994
+ yield TextChunk(delta)
2995
+ continue
2996
+
2997
+ if et in (
2998
+ "response.reasoning_summary_text.delta",
2999
+ "response.reasoning_text.delta",
3000
+ ):
3001
+ delta = event.get("delta") or ""
3002
+ if delta:
3003
+ thinking += delta
3004
+ yield ThinkingChunk(delta)
3005
+ continue
3006
+
3007
+ if et == "response.output_item.added":
3008
+ item = event.get("item") or {}
3009
+ if isinstance(item, dict) and item.get("type") == "function_call":
3010
+ _ensure_tool(item.get("call_id") or item.get("id"), item.get("name"))
3011
+ continue
3012
+
3013
+ if et == "response.function_call_arguments.delta":
3014
+ cid = event.get("call_id") or current_call_id
3015
+ key = _ensure_tool(cid)
3016
+ delta_args = event.get("delta") or ""
3017
+ if delta_args:
3018
+ tool_buf[key]["args"] += delta_args
3019
+ continue
3020
+
3021
+ if et == "response.function_call_arguments.done":
3022
+ cid = event.get("call_id") or current_call_id
3023
+ key = _ensure_tool(cid)
3024
+ final_args = event.get("arguments")
3025
+ if final_args is not None:
3026
+ tool_buf[key]["args"] = final_args
3027
+ continue
3028
+
3029
+ if et == "response.output_item.done":
3030
+ item = event.get("item") or {}
3031
+ if isinstance(item, dict) and item.get("type") == "function_call":
3032
+ key = _ensure_tool(item.get("call_id") or item.get("id"), item.get("name"))
3033
+ if item.get("name"):
3034
+ tool_buf[key]["name"] = item["name"]
3035
+ if item.get("arguments") is not None:
3036
+ tool_buf[key]["args"] = item["arguments"]
3037
+ if item.get("call_id"):
3038
+ tool_buf[key]["id"] = item["call_id"]
3039
+ continue
3040
+
3041
+ if et in ("response.completed", "response.incomplete"):
3042
+ resp_obj = event.get("response") or {}
3043
+ usage = resp_obj.get("usage") or event.get("usage") or {}
3044
+ if isinstance(usage, dict):
3045
+ in_tok = int(usage.get("input_tokens") or usage.get("prompt_tokens") or in_tok or 0)
3046
+ out_tok = int(usage.get("output_tokens") or usage.get("completion_tokens") or out_tok or 0)
3047
+ _det = usage.get("input_tokens_details") or usage.get("prompt_tokens_details") or {}
3048
+ if isinstance(_det, dict):
3049
+ cached_tok = int(_det.get("cached_tokens") or cached_tok or 0)
3050
+ continue
3051
+
3052
+ if et in ("response.failed", "error"):
3053
+ err = event.get("error") or event.get("response", {}).get("error") or event
3054
+ if isinstance(err, dict):
3055
+ msg = err.get("message") or err.get("code") or json.dumps(err)[:300]
3056
+ else:
3057
+ msg = str(err)[:300]
3058
+ note = f"\n\n_[meta error: {msg}]_"
3059
+ text += note
3060
+ yield TextChunk(note)
3061
+ continue
3062
+
3063
+ except _req.exceptions.ChunkedEncodingError:
3064
+ stream_interrupted = True
3065
+ except _req.exceptions.ConnectionError:
3066
+ stream_interrupted = True
3067
+ except Exception as e:
3068
+ msg = f"[meta] Error: {e}"
3069
+ yield TextChunk(msg)
3070
+ yield AssistantTurn(msg, [], 0, 0, error=True)
3071
+ return
3072
+ finally:
3073
+ if resp is not None:
3074
+ try:
3075
+ resp.close()
3076
+ except Exception:
3077
+ pass
3078
+
3079
+ if stream_interrupted and not tool_buf:
3080
+ note = "\n\n_[meta: el streaming se cortó a media respuesta — esto es parcial, reintenta]_"
3081
+ text += note
3082
+ yield TextChunk(note)
3083
+
3084
+ ordered_buf = {i: tool_buf[k] for i, k in enumerate(tool_order)} if tool_order else tool_buf
3085
+ final_tool_calls = _finalize_tool_calls(ordered_buf)
3086
+
3087
+ yield AssistantTurn(text, final_tool_calls, in_tok, out_tok, thinking=thinking,
3088
+ cache_read_tokens=cached_tok)
3089
+
3090
+
2845
3091
  def stream_xai_oauth(
2846
3092
  model: str,
2847
3093
  system: str,
@@ -7512,6 +7758,9 @@ def stream(
7512
7758
  # ChatGPT/Codex subscription OAuth (`/login chatgpt` or ~/.codex/auth.json)
7513
7759
  # → chatgpt.com/backend-api/codex/responses (Responses API, not chat/completions)
7514
7760
  yield from stream_chatgpt_oauth(model_name, system, messages, tool_schemas, config)
7761
+ elif prov["type"] == "meta":
7762
+ # Meta AI (Muse Spark) via api.meta.ai/v1/responses — API key (META_API_KEY).
7763
+ yield from stream_meta(api_key, model_name, system, messages, tool_schemas, config)
7515
7764
  elif prov["type"] == "gcloud":
7516
7765
  yield from stream_gcloud(model_name, system, messages, tool_schemas, config)
7517
7766
  elif prov["type"] == "litellm":
@@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta"
10
10
 
11
11
  [project]
12
12
  name = "dulus"
13
- version = "3.14.0"
13
+ version = "3.14.2"
14
14
  description = "Provider-independent agent runtime for the terminal — cloud and local models, real tools, MCP, plugins, memory, sub-agents, and Dulus OS."
15
15
  readme = "README.md"
16
16
  requires-python = ">=3.10"
@@ -112,6 +112,26 @@ dependencies = [
112
112
  "uv>=0.4.0",
113
113
  "sse-starlette>=2.0.0",
114
114
  "starlette>=0.38.0",
115
+ # Product analytics — usage/telemetry (respects opt-out). Nobody runs
116
+ # `pip install dulus[full]`, so shipping it here is the only way metrics
117
+ # actually reach us from real installs.
118
+ "posthog>=3.0.0",
119
+ # Memory Palace — per-turn semantic memory injection. Pulls chromadb; on
120
+ # exotic targets (termux/aarch64) wheels may be missing, but the vast
121
+ # majority of installs want this ON by default rather than hunting the
122
+ # `[memory]` extra nobody types.
123
+ "mempalace>=3.3.4",
124
+ # Voice I/O — mic capture + cloud TTS. On by default so `/voice` works out
125
+ # of the box; silent no-op on headless boxes with no audio device.
126
+ "sounddevice",
127
+ "elevenlabs>=1.0.0",
128
+ # Browser automation — /harvest-*, WebBridge, JS-rendered scraping. The pip
129
+ # package ships here; the Chromium binary is fetched on first harvest.
130
+ "playwright>=1.40.0",
131
+ # LiteLLM unified gateway — one provider entry, 100+ backends. Bundled by
132
+ # default so the welcome wizard's litellm option works without a second
133
+ # install step.
134
+ "litellm>=1.50.0",
115
135
  ]
116
136
 
117
137
  # mempalace pulls chromadb (26 transitive deps incl. numpy + onnxruntime),
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes