dulus 3.11.2__tar.gz → 3.11.4__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 (393) hide show
  1. {dulus-3.11.2/dulus.egg-info → dulus-3.11.4}/PKG-INFO +1 -1
  2. {dulus-3.11.2 → dulus-3.11.4}/docs/CHANGELOG.md +28 -0
  3. {dulus-3.11.2 → dulus-3.11.4}/docs/news.md +2 -0
  4. {dulus-3.11.2 → dulus-3.11.4/dulus.egg-info}/PKG-INFO +1 -1
  5. {dulus-3.11.2 → dulus-3.11.4}/dulus.egg-info/SOURCES.txt +1 -0
  6. {dulus-3.11.2 → dulus-3.11.4}/dulus.py +48 -7
  7. {dulus-3.11.2 → dulus-3.11.4}/providers.py +29 -26
  8. {dulus-3.11.2 → dulus-3.11.4}/pyproject.toml +247 -247
  9. dulus-3.11.4/tests/test_dulus_account.py +183 -0
  10. {dulus-3.11.2 → dulus-3.11.4}/tests/test_provider_errors.py +0 -52
  11. {dulus-3.11.2 → dulus-3.11.4}/webchat_server.py +13 -0
  12. {dulus-3.11.2 → dulus-3.11.4}/webchat_ui/app.js +47 -8
  13. {dulus-3.11.2 → dulus-3.11.4}/LICENSE +0 -0
  14. {dulus-3.11.2 → dulus-3.11.4}/MANIFEST.in +0 -0
  15. {dulus-3.11.2 → dulus-3.11.4}/README.md +0 -0
  16. {dulus-3.11.2 → dulus-3.11.4}/agent.py +0 -0
  17. {dulus-3.11.2 → dulus-3.11.4}/algolia_search.py +0 -0
  18. {dulus-3.11.2 → dulus-3.11.4}/analytics.py +0 -0
  19. {dulus-3.11.2 → dulus-3.11.4}/backend/__init__.py +0 -0
  20. {dulus-3.11.2 → dulus-3.11.4}/backend/agents_bridge.py +0 -0
  21. {dulus-3.11.2 → dulus-3.11.4}/backend/compressor.py +0 -0
  22. {dulus-3.11.2 → dulus-3.11.4}/backend/context.py +0 -0
  23. {dulus-3.11.2 → dulus-3.11.4}/backend/githook.py +0 -0
  24. {dulus-3.11.2 → dulus-3.11.4}/backend/marketplace.py +0 -0
  25. {dulus-3.11.2 → dulus-3.11.4}/backend/mempalace_bridge.py +0 -0
  26. {dulus-3.11.2 → dulus-3.11.4}/backend/paths.py +0 -0
  27. {dulus-3.11.2 → dulus-3.11.4}/backend/personas.py +0 -0
  28. {dulus-3.11.2 → dulus-3.11.4}/backend/plugins.py +0 -0
  29. {dulus-3.11.2 → dulus-3.11.4}/backend/server.py +0 -0
  30. {dulus-3.11.2 → dulus-3.11.4}/backend/tasks.py +0 -0
  31. {dulus-3.11.2 → dulus-3.11.4}/banners/__init__.py +0 -0
  32. {dulus-3.11.2 → dulus-3.11.4}/banners/banner_rotator.py +0 -0
  33. {dulus-3.11.2 → dulus-3.11.4}/banners/banners_dulus_ai.txt +0 -0
  34. {dulus-3.11.2 → dulus-3.11.4}/batch_api.py +0 -0
  35. {dulus-3.11.2 → dulus-3.11.4}/checkpoint/__init__.py +0 -0
  36. {dulus-3.11.2 → dulus-3.11.4}/checkpoint/hooks.py +0 -0
  37. {dulus-3.11.2 → dulus-3.11.4}/checkpoint/store.py +0 -0
  38. {dulus-3.11.2 → dulus-3.11.4}/checkpoint/types.py +0 -0
  39. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/__init__.py +0 -0
  40. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/__main__.py +0 -0
  41. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/ansi.py +0 -0
  42. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/assets/ascii/dulus_logo.txt +0 -0
  43. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/assets/ascii/eagle.txt +0 -0
  44. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/assets/ascii/terminal_art.txt +0 -0
  45. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/banners.py +0 -0
  46. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/demo.py +0 -0
  47. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/effects.py +0 -0
  48. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/effort_slider.py +0 -0
  49. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/progress.py +0 -0
  50. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/py.typed +0 -0
  51. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/spinners.py +0 -0
  52. {dulus-3.11.2 → dulus-3.11.4}/cli_animations/status.py +0 -0
  53. {dulus-3.11.2 → dulus-3.11.4}/clipboard_utils.py +0 -0
  54. {dulus-3.11.2 → dulus-3.11.4}/cloudsave.py +0 -0
  55. {dulus-3.11.2 → dulus-3.11.4}/common.py +0 -0
  56. {dulus-3.11.2 → dulus-3.11.4}/compaction.py +0 -0
  57. {dulus-3.11.2 → dulus-3.11.4}/config.py +0 -0
  58. {dulus-3.11.2 → dulus-3.11.4}/context.py +0 -0
  59. {dulus-3.11.2 → dulus-3.11.4}/data/__init__.py +0 -0
  60. {dulus-3.11.2 → dulus-3.11.4}/data/active_persona.json +0 -0
  61. {dulus-3.11.2 → dulus-3.11.4}/data/marketplace.json +0 -0
  62. {dulus-3.11.2 → dulus-3.11.4}/data/personas.json +0 -0
  63. {dulus-3.11.2 → dulus-3.11.4}/data/plugins/__init__.py +0 -0
  64. {dulus-3.11.2 → dulus-3.11.4}/data/plugins/composio/__init__.py +0 -0
  65. {dulus-3.11.2 → dulus-3.11.4}/data/plugins/composio/composio_plugin/__init__.py +0 -0
  66. {dulus-3.11.2 → dulus-3.11.4}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
  67. {dulus-3.11.2 → dulus-3.11.4}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
  68. {dulus-3.11.2 → dulus-3.11.4}/data/plugins/composio/plugin.json +0 -0
  69. {dulus-3.11.2 → dulus-3.11.4}/data/plugins/composio/plugin_tool.py +0 -0
  70. {dulus-3.11.2 → dulus-3.11.4}/data/tasks.json +0 -0
  71. {dulus-3.11.2 → dulus-3.11.4}/display_blocks.py +0 -0
  72. {dulus-3.11.2 → dulus-3.11.4}/docs/API.md +0 -0
  73. {dulus-3.11.2 → dulus-3.11.4}/docs/BRAND_GUIDE.md +0 -0
  74. {dulus-3.11.2 → dulus-3.11.4}/docs/CONTRIBUTING.md +0 -0
  75. {dulus-3.11.2 → dulus-3.11.4}/docs/DEPLOYMENT.md +0 -0
  76. {dulus-3.11.2 → dulus-3.11.4}/docs/Dulus_AI_Whitepaper_(v2.0).pdf +0 -0
  77. {dulus-3.11.2 → dulus-3.11.4}/docs/Dulus_YC_Company_Overview_Aug_2026.pdf +0 -0
  78. {dulus-3.11.2 → dulus-3.11.4}/docs/FAQ.md +0 -0
  79. {dulus-3.11.2 → dulus-3.11.4}/docs/GETTING_STARTED.md +0 -0
  80. {dulus-3.11.2 → dulus-3.11.4}/docs/README.md +0 -0
  81. {dulus-3.11.2 → dulus-3.11.4}/docs/README_AR.md +0 -0
  82. {dulus-3.11.2 → dulus-3.11.4}/docs/README_EN.md +0 -0
  83. {dulus-3.11.2 → dulus-3.11.4}/docs/README_ES.md +0 -0
  84. {dulus-3.11.2 → dulus-3.11.4}/docs/README_FR.md +0 -0
  85. {dulus-3.11.2 → dulus-3.11.4}/docs/README_JA.md +0 -0
  86. {dulus-3.11.2 → dulus-3.11.4}/docs/README_KO.md +0 -0
  87. {dulus-3.11.2 → dulus-3.11.4}/docs/README_PT.md +0 -0
  88. {dulus-3.11.2 → dulus-3.11.4}/docs/README_RU.md +0 -0
  89. {dulus-3.11.2 → dulus-3.11.4}/docs/README_ZH.md +0 -0
  90. {dulus-3.11.2 → dulus-3.11.4}/docs/SECURITY.md +0 -0
  91. {dulus-3.11.2 → dulus-3.11.4}/docs/__init__.py +0 -0
  92. {dulus-3.11.2 → dulus-3.11.4}/docs/api.html +0 -0
  93. {dulus-3.11.2 → dulus-3.11.4}/docs/architecture.md +0 -0
  94. {dulus-3.11.2 → dulus-3.11.4}/docs/azure-speech-template.json +0 -0
  95. {dulus-3.11.2 → dulus-3.11.4}/docs/dashboard/index.html +0 -0
  96. {dulus-3.11.2 → dulus-3.11.4}/docs/divider.svg +0 -0
  97. {dulus-3.11.2 → dulus-3.11.4}/docs/dulus-bird.png +0 -0
  98. {dulus-3.11.2 → dulus-3.11.4}/docs/generate.py +0 -0
  99. {dulus-3.11.2 → dulus-3.11.4}/docs/index.html +0 -0
  100. {dulus-3.11.2 → dulus-3.11.4}/docs/nvidia-models.svg +0 -0
  101. {dulus-3.11.2 → dulus-3.11.4}/docs/particle-playground.html +0 -0
  102. {dulus-3.11.2 → dulus-3.11.4}/docs/personas/index.html +0 -0
  103. {dulus-3.11.2 → dulus-3.11.4}/docs/poetry-banner.png +0 -0
  104. {dulus-3.11.2 → dulus-3.11.4}/docs/preview.html +0 -0
  105. {dulus-3.11.2 → dulus-3.11.4}/docs/python-console.md +0 -0
  106. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/auto-adapter.png +0 -0
  107. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/dulus-os.png +0 -0
  108. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/hero.png +0 -0
  109. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/lookback_two_windows.png +0 -0
  110. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/pyc_heap.svg +0 -0
  111. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/pyc_receipts.svg +0 -0
  112. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/pyc_trap.svg +0 -0
  113. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/roundtable.png +0 -0
  114. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/task-board.png +0 -0
  115. {dulus-3.11.2 → dulus-3.11.4}/docs/readme/webchat.png +0 -0
  116. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-agents.svg +0 -0
  117. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-brainstorm.svg +0 -0
  118. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-bridges.svg +0 -0
  119. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-features.svg +0 -0
  120. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-freetier.svg +0 -0
  121. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-memory.svg +0 -0
  122. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-models.svg +0 -0
  123. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-perms.svg +0 -0
  124. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-plugins.svg +0 -0
  125. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-quickstart.svg +0 -0
  126. {dulus-3.11.2 → dulus-3.11.4}/docs/sec-ssj.svg +0 -0
  127. {dulus-3.11.2 → dulus-3.11.4}/docs/spinners.svg +0 -0
  128. {dulus-3.11.2 → dulus-3.11.4}/docs/split-pane.svg +0 -0
  129. {dulus-3.11.2 → dulus-3.11.4}/docs/terminal-boot.svg +0 -0
  130. {dulus-3.11.2 → dulus-3.11.4}/docs/termux-edge.md +0 -0
  131. {dulus-3.11.2 → dulus-3.11.4}/dulus.egg-info/dependency_links.txt +0 -0
  132. {dulus-3.11.2 → dulus-3.11.4}/dulus.egg-info/entry_points.txt +0 -0
  133. {dulus-3.11.2 → dulus-3.11.4}/dulus.egg-info/requires.txt +0 -0
  134. {dulus-3.11.2 → dulus-3.11.4}/dulus.egg-info/top_level.txt +0 -0
  135. {dulus-3.11.2 → dulus-3.11.4}/dulus_bar_client.py +0 -0
  136. {dulus-3.11.2 → dulus-3.11.4}/dulus_gui.py +0 -0
  137. {dulus-3.11.2 → dulus-3.11.4}/dulus_mcp/__init__.py +0 -0
  138. {dulus-3.11.2 → dulus-3.11.4}/dulus_mcp/client.py +0 -0
  139. {dulus-3.11.2 → dulus-3.11.4}/dulus_mcp/commands.py +0 -0
  140. {dulus-3.11.2 → dulus-3.11.4}/dulus_mcp/config.py +0 -0
  141. {dulus-3.11.2 → dulus-3.11.4}/dulus_mcp/hub.py +0 -0
  142. {dulus-3.11.2 → dulus-3.11.4}/dulus_mcp/tools.py +0 -0
  143. {dulus-3.11.2 → dulus-3.11.4}/dulus_mcp/types.py +0 -0
  144. {dulus-3.11.2 → dulus-3.11.4}/dulus_quick_menu.py +0 -0
  145. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/__init__.py +0 -0
  146. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/add_dir_manager.py +0 -0
  147. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/afk_mode.py +0 -0
  148. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/approval_runtime.py +0 -0
  149. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/background_tasks.py +0 -0
  150. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/clipboard_utils.py +0 -0
  151. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/diff_visualizer.py +0 -0
  152. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/export_import.py +0 -0
  153. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/hook_engine.py +0 -0
  154. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/notification_manager.py +0 -0
  155. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/session_fork.py +0 -0
  156. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/shell_mode.py +0 -0
  157. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/todo_visualizer.py +0 -0
  158. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/tree_ls.py +0 -0
  159. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/wire_events.py +0 -0
  160. {dulus-3.11.2 → dulus-3.11.4}/dulus_tools/yolo_mode.py +0 -0
  161. {dulus-3.11.2 → dulus-3.11.4}/file_filter.py +0 -0
  162. {dulus-3.11.2 → dulus-3.11.4}/fs_compass.py +0 -0
  163. {dulus-3.11.2 → dulus-3.11.4}/governance.py +0 -0
  164. {dulus-3.11.2 → dulus-3.11.4}/gui/__init__.py +0 -0
  165. {dulus-3.11.2 → dulus-3.11.4}/gui/agent_bridge.py +0 -0
  166. {dulus-3.11.2 → dulus-3.11.4}/gui/chat_widget.py +0 -0
  167. {dulus-3.11.2 → dulus-3.11.4}/gui/main_window.py +0 -0
  168. {dulus-3.11.2 → dulus-3.11.4}/gui/personas.py +0 -0
  169. {dulus-3.11.2 → dulus-3.11.4}/gui/sandbox_server.py +0 -0
  170. {dulus-3.11.2 → dulus-3.11.4}/gui/session_utils.py +0 -0
  171. {dulus-3.11.2 → dulus-3.11.4}/gui/settings_dialog.py +0 -0
  172. {dulus-3.11.2 → dulus-3.11.4}/gui/sidebar.py +0 -0
  173. {dulus-3.11.2 → dulus-3.11.4}/gui/tasks_view.py +0 -0
  174. {dulus-3.11.2 → dulus-3.11.4}/gui/themes.py +0 -0
  175. {dulus-3.11.2 → dulus-3.11.4}/gui/tool_panel.py +0 -0
  176. {dulus-3.11.2 → dulus-3.11.4}/input.py +0 -0
  177. {dulus-3.11.2 → dulus-3.11.4}/license_manager.py +0 -0
  178. {dulus-3.11.2 → dulus-3.11.4}/lookback.py +0 -0
  179. {dulus-3.11.2 → dulus-3.11.4}/memory/__init__.py +0 -0
  180. {dulus-3.11.2 → dulus-3.11.4}/memory/audit.py +0 -0
  181. {dulus-3.11.2 → dulus-3.11.4}/memory/consolidator.py +0 -0
  182. {dulus-3.11.2 → dulus-3.11.4}/memory/context.py +0 -0
  183. {dulus-3.11.2 → dulus-3.11.4}/memory/mempalace_bridge.py +0 -0
  184. {dulus-3.11.2 → dulus-3.11.4}/memory/offload.py +0 -0
  185. {dulus-3.11.2 → dulus-3.11.4}/memory/palace.py +0 -0
  186. {dulus-3.11.2 → dulus-3.11.4}/memory/scan.py +0 -0
  187. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/adaptation_guides.md +0 -0
  188. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/askuserquestion_after_numbered_list.md +0 -0
  189. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/background_jobs_no_extra_timers.md +0 -0
  190. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/cleverness_over_resources.md +0 -0
  191. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/corporate_tls_breaks_curl_cffi.md +0 -0
  192. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/hardened_rules.md +0 -0
  193. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/investigator_workflow_heuristic.md +0 -0
  194. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/native_code_over_sketchy_plugins.md +0 -0
  195. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/no_personal_info_in_code.md +0 -0
  196. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/searches_include_modified_date.md +0 -0
  197. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/tmux_send_keys_power_patterns.md +0 -0
  198. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/token_optimization_technique.md +0 -0
  199. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/tool_chaining_capability.md +0 -0
  200. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/insights/webbridge_evaluate_first_pattern.md +0 -0
  201. {dulus-3.11.2 → dulus-3.11.4}/memory/seeds/short_memory.md +0 -0
  202. {dulus-3.11.2 → dulus-3.11.4}/memory/sessions.py +0 -0
  203. {dulus-3.11.2 → dulus-3.11.4}/memory/store.py +0 -0
  204. {dulus-3.11.2 → dulus-3.11.4}/memory/tools.py +0 -0
  205. {dulus-3.11.2 → dulus-3.11.4}/memory/types.py +0 -0
  206. {dulus-3.11.2 → dulus-3.11.4}/memory/vector_search.py +0 -0
  207. {dulus-3.11.2 → dulus-3.11.4}/model_params.py +0 -0
  208. {dulus-3.11.2 → dulus-3.11.4}/multi_agent/__init__.py +0 -0
  209. {dulus-3.11.2 → dulus-3.11.4}/multi_agent/subagent.py +0 -0
  210. {dulus-3.11.2 → dulus-3.11.4}/multi_agent/tools.py +0 -0
  211. {dulus-3.11.2 → dulus-3.11.4}/paste_placeholders.py +0 -0
  212. {dulus-3.11.2 → dulus-3.11.4}/personality_config.py +0 -0
  213. {dulus-3.11.2 → dulus-3.11.4}/plugin/__init__.py +0 -0
  214. {dulus-3.11.2 → dulus-3.11.4}/plugin/autoadapter.py +0 -0
  215. {dulus-3.11.2 → dulus-3.11.4}/plugin/loader.py +0 -0
  216. {dulus-3.11.2 → dulus-3.11.4}/plugin/recommend.py +0 -0
  217. {dulus-3.11.2 → dulus-3.11.4}/plugin/store.py +0 -0
  218. {dulus-3.11.2 → dulus-3.11.4}/plugin/types.py +0 -0
  219. {dulus-3.11.2 → dulus-3.11.4}/profiles.py +0 -0
  220. {dulus-3.11.2 → dulus-3.11.4}/pyconsole.py +0 -0
  221. {dulus-3.11.2 → dulus-3.11.4}/sandbox/README.md +0 -0
  222. {dulus-3.11.2 → dulus-3.11.4}/sandbox/__init__.py +0 -0
  223. {dulus-3.11.2 → dulus-3.11.4}/sandbox/components.json +0 -0
  224. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
  225. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
  226. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/index.html +0 -0
  227. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/wallpaper-default.jpg +0 -0
  228. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/wallpapers/default.jpeg +0 -0
  229. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/wallpapers/light.jpeg +0 -0
  230. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/wallpapers/nature.jpeg +0 -0
  231. {dulus-3.11.2 → dulus-3.11.4}/sandbox/dist/wallpapers/tech.jpeg +0 -0
  232. {dulus-3.11.2 → dulus-3.11.4}/sandbox/eslint.config.js +0 -0
  233. {dulus-3.11.2 → dulus-3.11.4}/sandbox/index.html +0 -0
  234. {dulus-3.11.2 → dulus-3.11.4}/sandbox/info.md +0 -0
  235. {dulus-3.11.2 → dulus-3.11.4}/sandbox/package-lock.json +0 -0
  236. {dulus-3.11.2 → dulus-3.11.4}/sandbox/package.json +0 -0
  237. {dulus-3.11.2 → dulus-3.11.4}/sandbox/postcss.config.js +0 -0
  238. {dulus-3.11.2 → dulus-3.11.4}/sandbox/tailwind.config.js +0 -0
  239. {dulus-3.11.2 → dulus-3.11.4}/sandbox/tsconfig.app.json +0 -0
  240. {dulus-3.11.2 → dulus-3.11.4}/sandbox/tsconfig.json +0 -0
  241. {dulus-3.11.2 → dulus-3.11.4}/sandbox/tsconfig.node.json +0 -0
  242. {dulus-3.11.2 → dulus-3.11.4}/sandbox/vite.config.ts +0 -0
  243. {dulus-3.11.2 → dulus-3.11.4}/sandbox_bootstrap.py +0 -0
  244. {dulus-3.11.2 → dulus-3.11.4}/setup.cfg +0 -0
  245. {dulus-3.11.2 → dulus-3.11.4}/skill/__init__.py +0 -0
  246. {dulus-3.11.2 → dulus-3.11.4}/skill/builtin.py +0 -0
  247. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/LICENSE-obsidian-skills +0 -0
  248. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/README.md +0 -0
  249. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/defuddle/SKILL.md +0 -0
  250. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/json-canvas/SKILL.md +0 -0
  251. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/json-canvas/references/EXAMPLES.md +0 -0
  252. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/obsidian-bases/SKILL.md +0 -0
  253. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +0 -0
  254. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/obsidian-cli/SKILL.md +0 -0
  255. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/obsidian-markdown/SKILL.md +0 -0
  256. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/obsidian-markdown/references/CALLOUTS.md +0 -0
  257. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/obsidian-markdown/references/EMBEDS.md +0 -0
  258. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/obsidian-markdown/references/PROPERTIES.md +0 -0
  259. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/README.md +0 -0
  260. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/SKILL.md +0 -0
  261. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/addresses.json +0 -0
  262. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/advancedbackgroundchecks.json +0 -0
  263. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/beenverified.json +0 -0
  264. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/clustal.json +0 -0
  265. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/clustrmaps.json +0 -0
  266. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/cyberbackgroundchecks.json +0 -0
  267. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/familytreenow.json +0 -0
  268. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/fastpeoplesearch.json +0 -0
  269. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/intelius.json +0 -0
  270. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/mylife.json +0 -0
  271. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/nuwber.json +0 -0
  272. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/peekyou.json +0 -0
  273. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/peoplefinders.json +0 -0
  274. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/radaris.json +0 -0
  275. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/rehold.json +0 -0
  276. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/searchpeoplefree.json +0 -0
  277. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/socialcatfish.json +0 -0
  278. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/spokeo.json +0 -0
  279. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/thatsthem.json +0 -0
  280. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/truepeoplesearch.json +0 -0
  281. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/usphonebook.json +0 -0
  282. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/brokers/whitepages.json +0 -0
  283. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/legal/ccpa.md +0 -0
  284. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/legal/drop.md +0 -0
  285. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/legal/gdpr.md +0 -0
  286. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/methods.md +0 -0
  287. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/site-playbooks.md +0 -0
  288. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/references/state-machine.md +0 -0
  289. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/autopilot.py +0 -0
  290. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/badbool.py +0 -0
  291. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/brokers.py +0 -0
  292. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/cdp.py +0 -0
  293. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/config.py +0 -0
  294. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/crypto.py +0 -0
  295. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/dossier.py +0 -0
  296. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/email_modes.py +0 -0
  297. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/emailer.py +0 -0
  298. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/ledger.py +0 -0
  299. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/legal.py +0 -0
  300. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/paths.py +0 -0
  301. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/pdd.py +0 -0
  302. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/registry.py +0 -0
  303. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/report.py +0 -0
  304. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/scan.py +0 -0
  305. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/storage.py +0 -0
  306. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/tiers.py +0 -0
  307. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/scripts/vectors.py +0 -0
  308. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/templates/consent/authorization.md +0 -0
  309. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/templates/emails/ccpa-authorized-agent.txt +0 -0
  310. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/templates/emails/ccpa-deletion.txt +0 -0
  311. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/templates/emails/ccpa-indirect-deletion.txt +0 -0
  312. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/templates/emails/gdpr-erasure.txt +0 -0
  313. {dulus-3.11.2 → dulus-3.11.4}/skill/bundled/unbroker/templates/emails/generic-optout.txt +0 -0
  314. {dulus-3.11.2 → dulus-3.11.4}/skill/clawhub.py +0 -0
  315. {dulus-3.11.2 → dulus-3.11.4}/skill/executor.py +0 -0
  316. {dulus-3.11.2 → dulus-3.11.4}/skill/loader.py +0 -0
  317. {dulus-3.11.2 → dulus-3.11.4}/skill/tools.py +0 -0
  318. {dulus-3.11.2 → dulus-3.11.4}/skills.py +0 -0
  319. {dulus-3.11.2 → dulus-3.11.4}/soul.py +0 -0
  320. {dulus-3.11.2 → dulus-3.11.4}/spinner.py +0 -0
  321. {dulus-3.11.2 → dulus-3.11.4}/steer_input.py +0 -0
  322. {dulus-3.11.2 → dulus-3.11.4}/subagent.py +0 -0
  323. {dulus-3.11.2 → dulus-3.11.4}/task/__init__.py +0 -0
  324. {dulus-3.11.2 → dulus-3.11.4}/task/store.py +0 -0
  325. {dulus-3.11.2 → dulus-3.11.4}/task/tools.py +0 -0
  326. {dulus-3.11.2 → dulus-3.11.4}/task/types.py +0 -0
  327. {dulus-3.11.2 → dulus-3.11.4}/tests/test_afk_yolo.py +0 -0
  328. {dulus-3.11.2 → dulus-3.11.4}/tests/test_approval_runtime.py +0 -0
  329. {dulus-3.11.2 → dulus-3.11.4}/tests/test_background_task_tools.py +0 -0
  330. {dulus-3.11.2 → dulus-3.11.4}/tests/test_background_tasks.py +0 -0
  331. {dulus-3.11.2 → dulus-3.11.4}/tests/test_chatgpt_oauth.py +0 -0
  332. {dulus-3.11.2 → dulus-3.11.4}/tests/test_checkpoint.py +0 -0
  333. {dulus-3.11.2 → dulus-3.11.4}/tests/test_cli_animations_integration.py +0 -0
  334. {dulus-3.11.2 → dulus-3.11.4}/tests/test_clipboard_utils.py +0 -0
  335. {dulus-3.11.2 → dulus-3.11.4}/tests/test_compaction.py +0 -0
  336. {dulus-3.11.2 → dulus-3.11.4}/tests/test_composio_tool_generator.py +0 -0
  337. {dulus-3.11.2 → dulus-3.11.4}/tests/test_diff_view.py +0 -0
  338. {dulus-3.11.2 → dulus-3.11.4}/tests/test_diff_visualization.py +0 -0
  339. {dulus-3.11.2 → dulus-3.11.4}/tests/test_display_blocks.py +0 -0
  340. {dulus-3.11.2 → dulus-3.11.4}/tests/test_export_import.py +0 -0
  341. {dulus-3.11.2 → dulus-3.11.4}/tests/test_grok_usage.py +0 -0
  342. {dulus-3.11.2 → dulus-3.11.4}/tests/test_hook_engine.py +0 -0
  343. {dulus-3.11.2 → dulus-3.11.4}/tests/test_injection_fix.py +0 -0
  344. {dulus-3.11.2 → dulus-3.11.4}/tests/test_license.py +0 -0
  345. {dulus-3.11.2 → dulus-3.11.4}/tests/test_lookback_cache.py +0 -0
  346. {dulus-3.11.2 → dulus-3.11.4}/tests/test_mcp.py +0 -0
  347. {dulus-3.11.2 → dulus-3.11.4}/tests/test_mcp_algolia.py +0 -0
  348. {dulus-3.11.2 → dulus-3.11.4}/tests/test_memory.py +0 -0
  349. {dulus-3.11.2 → dulus-3.11.4}/tests/test_memory_home_resolution.py +0 -0
  350. {dulus-3.11.2 → dulus-3.11.4}/tests/test_notification_manager.py +0 -0
  351. {dulus-3.11.2 → dulus-3.11.4}/tests/test_plugin.py +0 -0
  352. {dulus-3.11.2 → dulus-3.11.4}/tests/test_profile_isolation.py +0 -0
  353. {dulus-3.11.2 → dulus-3.11.4}/tests/test_session_fork.py +0 -0
  354. {dulus-3.11.2 → dulus-3.11.4}/tests/test_shell_mode.py +0 -0
  355. {dulus-3.11.2 → dulus-3.11.4}/tests/test_skills.py +0 -0
  356. {dulus-3.11.2 → dulus-3.11.4}/tests/test_skillssh_algolia.py +0 -0
  357. {dulus-3.11.2 → dulus-3.11.4}/tests/test_steer_input.py +0 -0
  358. {dulus-3.11.2 → dulus-3.11.4}/tests/test_subagent.py +0 -0
  359. {dulus-3.11.2 → dulus-3.11.4}/tests/test_task.py +0 -0
  360. {dulus-3.11.2 → dulus-3.11.4}/tests/test_telegram_buffer.py +0 -0
  361. {dulus-3.11.2 → dulus-3.11.4}/tests/test_think_tool.py +0 -0
  362. {dulus-3.11.2 → dulus-3.11.4}/tests/test_todo_tool.py +0 -0
  363. {dulus-3.11.2 → dulus-3.11.4}/tests/test_todo_visualization.py +0 -0
  364. {dulus-3.11.2 → dulus-3.11.4}/tests/test_tool_registry.py +0 -0
  365. {dulus-3.11.2 → dulus-3.11.4}/tests/test_tree_ls.py +0 -0
  366. {dulus-3.11.2 → dulus-3.11.4}/tests/test_voice.py +0 -0
  367. {dulus-3.11.2 → dulus-3.11.4}/tests/test_webbridge_tabs.py +0 -0
  368. {dulus-3.11.2 → dulus-3.11.4}/tests/test_wire_events.py +0 -0
  369. {dulus-3.11.2 → dulus-3.11.4}/tmux_tools.py +0 -0
  370. {dulus-3.11.2 → dulus-3.11.4}/tool_registry.py +0 -0
  371. {dulus-3.11.2 → dulus-3.11.4}/tools.py +0 -0
  372. {dulus-3.11.2 → dulus-3.11.4}/tools_background.py +0 -0
  373. {dulus-3.11.2 → dulus-3.11.4}/tools_think.py +0 -0
  374. {dulus-3.11.2 → dulus-3.11.4}/tools_todo.py +0 -0
  375. {dulus-3.11.2 → dulus-3.11.4}/ui/__init__.py +0 -0
  376. {dulus-3.11.2 → dulus-3.11.4}/ui/input.py +0 -0
  377. {dulus-3.11.2 → dulus-3.11.4}/ui/render.py +0 -0
  378. {dulus-3.11.2 → dulus-3.11.4}/updater.py +0 -0
  379. {dulus-3.11.2 → dulus-3.11.4}/voice/__init__.py +0 -0
  380. {dulus-3.11.2 → dulus-3.11.4}/voice/audio_utils.py +0 -0
  381. {dulus-3.11.2 → dulus-3.11.4}/voice/keyterms.py +0 -0
  382. {dulus-3.11.2 → dulus-3.11.4}/voice/recorder.py +0 -0
  383. {dulus-3.11.2 → dulus-3.11.4}/voice/stt.py +0 -0
  384. {dulus-3.11.2 → dulus-3.11.4}/voice/tts.py +0 -0
  385. {dulus-3.11.2 → dulus-3.11.4}/voice/wake_word.py +0 -0
  386. {dulus-3.11.2 → dulus-3.11.4}/webbridge/__init__.py +0 -0
  387. {dulus-3.11.2 → dulus-3.11.4}/webbridge/core.py +0 -0
  388. {dulus-3.11.2 → dulus-3.11.4}/webbridge/tools.py +0 -0
  389. {dulus-3.11.2 → dulus-3.11.4}/webchat.py +0 -0
  390. {dulus-3.11.2 → dulus-3.11.4}/webchat_ui/__init__.py +0 -0
  391. {dulus-3.11.2 → dulus-3.11.4}/webchat_ui/index.html +0 -0
  392. {dulus-3.11.2 → dulus-3.11.4}/webchat_ui/style.css +0 -0
  393. {dulus-3.11.2 → dulus-3.11.4}/welcome.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dulus
3
- Version: 3.11.2
3
+ Version: 3.11.4
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>
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.11.4] - 2026-08-19
11
+
12
+ ### Fixed
13
+ - **Web chat no longer stutters on long replies.** The streaming bubble
14
+ re-parsed the whole markdown document and re-highlighted every code block on
15
+ each token, and the cost grows with message length. Re-renders are now
16
+ coalesced into one per animation frame, with a final flush so no tokens are
17
+ dropped.
18
+ - **Repeated calls to the same tool now complete the right pill.** Five Bash
19
+ commands in a row kept marking the first pill "done" and left the rest
20
+ spinning "running" forever. The result now resolves to the newest still-
21
+ running pill and lands directly beneath it.
22
+ - **A late click on an expired question can no longer fake an answer.**
23
+ AskUserQuestion banners outlived the agent's 300s timeout, so clicking one
24
+ afterwards looked "answered" while nobody was listening. Stale entries are
25
+ now purged before that can happen.
26
+ - **Kimi quota exhaustion is reported honestly.** When a Kimi membership's
27
+ billing-cycle quota is spent, the API answers `403 access_terminated_error`
28
+ — which Dulus used to misread as an invalid API key, sending you to mint a
29
+ new key that cannot help (quota is billed per account). It now says what
30
+ happened and points at `/model`.
31
+ - **`/login kimi force` actually switches accounts now.** It skipped the
32
+ "already logged in" check but left the stored refresh token and device id in
33
+ place, so the device flow silently re-bound the same account. Both are wiped
34
+ first.
35
+ - **Removed a duplicated line in the Ollama streaming loop.** Harmless but
36
+ dead.
37
+
10
38
  ## [3.11.1] - 2026-08-18
11
39
 
12
40
  ### Fixed
@@ -2,6 +2,8 @@
2
2
 
3
3
  ## 🔥🔥🔥 News (Pacific Time)
4
4
 
5
+ - August 19, 2026 (**v3.11.4**): **🩹 Web chat polish + honest Kimi errors.** Long replies no longer stutter (streaming re-renders are coalesced to one per frame), repeated calls to the same tool complete the right pill instead of leaving the rest spinning forever, and a late click on an expired question can't fake an answer anymore. On the Kimi side: a spent membership quota is now reported as quota — not as a bogus "invalid API key" — and `/login kimi force` wipes the old session and device id so switching accounts actually works. `pip install --upgrade dulus`. 🦅🇩🇴
6
+
5
7
  - August 18, 2026 (**v3.11.1**): **🔧 Kimi Code connects again.** Kimi moved its coding endpoint from `api.kimi.com` to `api.kimi.ai`, and Dulus only knew the old address — so requests to the new one went out without the user-agent Kimi requires and bounced back with a 403. Dulus now recognizes both hosts, so `kimi-code` and `/login kimi` models talk to Kimi cleanly again. `pip install --upgrade dulus`. 🦅🇩🇴
6
8
 
7
9
  - August 17, 2026 (**v3.11.0**): **🧬 Profiles are real agents now.** A profile always had its own `plugins/`, `skills/` and `memory/` — but almost nothing read them, and switching profiles only ever *added* tools, so you'd see plugins you couldn't actually use. Every one of those paths is now scoped to the active profile: a named profile is **lean** by default (its own stuff plus the bundled skills), reads only its own memory, and can't accidentally uninstall a plugin out from under your other profiles. `/profile inherit <name> on` if you want one that sees everything. Your `default` profile is untouched.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dulus
3
- Version: 3.11.2
3
+ Version: 3.11.4
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>
@@ -341,6 +341,7 @@ tests/test_composio_tool_generator.py
341
341
  tests/test_diff_view.py
342
342
  tests/test_diff_visualization.py
343
343
  tests/test_display_blocks.py
344
+ tests/test_dulus_account.py
344
345
  tests/test_export_import.py
345
346
  tests/test_grok_usage.py
346
347
  tests/test_hook_engine.py
@@ -380,7 +380,7 @@ try:
380
380
  from importlib.metadata import version as _pkg_version
381
381
  VERSION = _pkg_version("dulus")
382
382
  except Exception:
383
- VERSION = "3.11.2" # dev fallback — keep in sync with pyproject.toml
383
+ VERSION = "3.11.4" # dev fallback — keep in sync with pyproject.toml
384
384
 
385
385
  # ── ANSI helpers (used even with rich for non-markdown output) ─────────────
386
386
  from common import C, clr, info, ok, warn, err, stream_thinking, sanitize_text
@@ -3246,11 +3246,7 @@ def cmd_harvest(_args: str, _state, config) -> bool:
3246
3246
  info("Login page detected. Please log in manually, then press ENTER here...")
3247
3247
  input()
3248
3248
 
3249
- # claude.ai is a long-lived SPA that never reaches "networkidle"
3250
- # (it keeps streaming/websocket connections open), so that wait
3251
- # always burned the full 30s and timed out. Wait for the DOM
3252
- # instead and give it a real ceiling.
3253
- page.goto("https://claude.ai/new", wait_until="domcontentloaded", timeout=60000)
3249
+ page.goto("https://claude.ai/new", wait_until="networkidle")
3254
3250
  time.sleep(2)
3255
3251
 
3256
3252
  user_agent = page.evaluate("navigator.userAgent") if browser.pages else ""
@@ -9171,6 +9167,41 @@ def cmd_login(args: str, _state, config) -> bool:
9171
9167
  """
9172
9168
  sub = (args or "").strip().lower()
9173
9169
 
9170
+ # ── Dulus account (OAuth 2.0 + PKCE against Dulus's own API) ──
9171
+ # Unlike the provider logins below, this one authenticates you to Dulus
9172
+ # itself, so the CLI can reach the Dulus API without an API key pasted in
9173
+ # by hand. `/login dulus key` mints a dulus_sk_* key for CI and servers.
9174
+ if sub.split(" ")[0] in ("dulus", "account"):
9175
+ import dulus_account
9176
+
9177
+ rest = (args or "").strip().split()
9178
+ force = "force" in rest
9179
+ if len(rest) > 1 and rest[1] in ("key", "apikey", "api-key"):
9180
+ name = rest[2] if len(rest) > 2 else "cli"
9181
+ dulus_account.create_api_key(name, notify=lambda m: print(clr(m, "cyan")))
9182
+ return True
9183
+
9184
+ if not force:
9185
+ store = dulus_account.load_store()
9186
+ if store.get("access_token") and not dulus_account._token_expired(store):
9187
+ print(clr("✅ Dulus account already signed in.", "green"))
9188
+ print(clr("Use `/login dulus force` to sign in again, or `/login dulus key` to mint an API key.", "green"))
9189
+ return True
9190
+
9191
+ if force:
9192
+ dulus_account.clear_store()
9193
+ print(clr("Starting Dulus sign-in (OAuth 2.0 + PKCE)…", "cyan"))
9194
+ try:
9195
+ token = dulus_account.login(notify=lambda m: print(clr(m, "cyan")))
9196
+ except Exception as e:
9197
+ token = None
9198
+ print(clr(f"Sign-in error: {e}", "red"))
9199
+ if token:
9200
+ print(clr("✅ Signed in to your Dulus account.", "green"))
9201
+ return True
9202
+ print(clr("Could not sign in. Re-run `/login dulus`.", "yellow"))
9203
+ return True
9204
+
9174
9205
  # ── Claude / Anthropic subscription OAuth (no API key, no cookie webbridge) ──
9175
9206
  # Mirrors the Grok flow: opens claude.ai in the browser, you approve and paste
9176
9207
  # back the code, Dulus exchanges it for an OAuth token. claude-* models then run
@@ -9258,7 +9289,10 @@ def cmd_login(args: str, _state, config) -> bool:
9258
9289
  # /login kimi — Kimi membership via the official Kimi Code CLI device OAuth.
9259
9290
  # Reuses ~/.kimi-code credentials when present; otherwise opens auth.kimi.com.
9260
9291
  if sub.split(" ")[0] in ("kimi", "kimi-oauth", "moonshot-oauth"):
9261
- from providers import _kimi_oauth_login, _kimi_oauth_get_token
9292
+ from providers import (
9293
+ _kimi_oauth_login, _kimi_oauth_get_token,
9294
+ _kimi_oauth_clear_store,
9295
+ )
9262
9296
  force = "force" in (args or "").lower()
9263
9297
  if not force:
9264
9298
  tok = _kimi_oauth_get_token(config)
@@ -9266,6 +9300,13 @@ def cmd_login(args: str, _state, config) -> bool:
9266
9300
  print(clr("✅ Kimi OAuth session already active (Kimi membership, no API key).", "green"))
9267
9301
  print(clr("kimi-oauth/* models use your Kimi membership automatically. Use `/login kimi force` to re-login.", "green"))
9268
9302
  return True
9303
+ else:
9304
+ # A fresh device flow is not enough to switch accounts: the stored
9305
+ # refresh token revives the old session and the persisted device id
9306
+ # keeps us bound to it. Wipe both so the login binds cleanly.
9307
+ removed = _kimi_oauth_clear_store()
9308
+ if removed:
9309
+ print(clr(f"Cleared previous Kimi session ({', '.join(removed)}).", "cyan"))
9269
9310
  print(clr("Starting Kimi membership login (device OAuth via auth.kimi.com)…", "cyan"))
9270
9311
  print(clr("A browser will open — approve the shown device code with your Kimi account.", "cyan"))
9271
9312
  try:
@@ -91,15 +91,6 @@ class _ProviderRetry:
91
91
  # Rate limit / server overload
92
92
  if "429" in msg or "rate limit" in msg or "too many requests" in msg:
93
93
  return True
94
- # Capacity pushback. Anthropic answers 529 "overloaded_error" and other
95
- # providers say "overloaded"/"capacity"/"server is busy" with assorted
96
- # status codes; the next request usually succeeds, so this is exactly
97
- # the case worth absorbing silently instead of surfacing to the user.
98
- if ("529" in msg or "overloaded" in msg or "over capacity" in msg
99
- or "at capacity" in msg or "server is busy" in msg
100
- or "model is currently loading" in msg
101
- or type(exc).__name__ in ("OverloadedError", "InternalServerError")):
102
- return True
103
94
  # Server errors
104
95
  if "500" in msg or "502" in msg or "503" in msg or "504" in msg:
105
96
  return True
@@ -5253,6 +5244,13 @@ def friendly_api_error(exc: Exception) -> str:
5253
5244
  s = str(exc).lower()
5254
5245
  etype = type(exc).__name__
5255
5246
 
5247
+ # Kimi Code quota. Checked before the generic auth/permission branches: the
5248
+ # payload carries a 403 and the word "access", which would otherwise be read
5249
+ # as a bad key. Quota is billed per ACCOUNT, so a new key changes nothing.
5250
+ if "access_terminated_error" in s:
5251
+ return ("Kimi membership quota is spent for this billing cycle. It is billed per "
5252
+ "account, so a new API key won't help — switch model with /model, or wait "
5253
+ "for the cycle to reset.")
5256
5254
  # Auth / key problems
5257
5255
  if "authentication" in s or "invalid_api_key" in s or "401" in s or etype == "AuthenticationError":
5258
5256
  return "API key is missing or invalid. Run /config <provider>_api_key=... or set the env var."
@@ -5532,13 +5530,6 @@ def stream_anthropic(
5532
5530
  yield AssistantTurn(text, tool_calls, 0, 0, thinking=thinking,
5533
5531
  thinking_signature=thinking_signature, error=False)
5534
5532
  return
5535
- # Nothing emitted yet and the failure is transient (529 overloaded, a
5536
- # blip, a rate limit): let it propagate so _ProviderRetry can quietly
5537
- # try again. Turning it into text here would strand the retry wrapper,
5538
- # which only ever sees a well-behaved generator. Once output has been
5539
- # emitted a retry would duplicate it, so those still report.
5540
- if not text and not tool_calls and _ProviderRetry.is_retryable(_e):
5541
- raise
5542
5533
  msg = friendly_api_error(_e)
5543
5534
  yield TextChunk(msg)
5544
5535
  yield AssistantTurn(msg, [], 0, 0, error=True)
@@ -6167,11 +6158,6 @@ def stream_openai_compat(
6167
6158
  {**config, "_modelstudio_remaining": _ms_remaining[1:]},
6168
6159
  )
6169
6160
  return
6170
- # Transient capacity pushback before the stream opened: propagate so
6171
- # _ProviderRetry absorbs it silently. This is the create() call, so
6172
- # nothing has been emitted yet and a retry cannot duplicate output.
6173
- if _ProviderRetry.is_retryable(e):
6174
- raise
6175
6161
  msg = friendly_api_error(e)
6176
6162
  yield TextChunk(msg)
6177
6163
  yield AssistantTurn(msg, [], 0, 0, error=True)
@@ -6187,10 +6173,6 @@ def stream_openai_compat(
6187
6173
  {**config, "_modelstudio_remaining": _ms_remaining[1:]},
6188
6174
  )
6189
6175
  return
6190
- # Same as above: still inside the create() call, so a transient failure
6191
- # can be retried without duplicating anything.
6192
- if _ProviderRetry.is_retryable(e):
6193
- raise
6194
6176
  msg = friendly_api_error(e)
6195
6177
  yield TextChunk(msg)
6196
6178
  yield AssistantTurn(msg, [], 0, 0, error=True)
@@ -6841,7 +6823,6 @@ def stream_ollama(
6841
6823
  yield ThinkingChunk(_thinking_buffer)
6842
6824
  _thinking_buffer = ""
6843
6825
 
6844
- content = msg.get("content", "") if "content" in msg else ""
6845
6826
  content = msg.get("content", "") if "content" in msg else ""
6846
6827
  if content:
6847
6828
  # Flush thinking buffer before content
@@ -6985,6 +6966,28 @@ def _kimi_oauth_save_store(data: dict) -> None:
6985
6966
  _oauth_save_store(_kimi_oauth_store_path(), data)
6986
6967
 
6987
6968
 
6969
+ def _kimi_oauth_clear_store() -> list[str]:
6970
+ """Delete the Kimi OAuth session *and* the device identity.
6971
+
6972
+ A plain re-login is not enough to switch Kimi accounts: the refresh token in
6973
+ ``kimi_oauth.json`` silently revives the previous session, and the stable
6974
+ ``kimi_device_id`` keeps the host tied to the old device. Both files have to
6975
+ go before the device flow can bind a different account.
6976
+
6977
+ Returns the names of the files actually removed (for user feedback).
6978
+ """
6979
+ removed: list[str] = []
6980
+ for path in (_kimi_oauth_store_path(), _oauth_store_path("kimi_device_id")):
6981
+ try:
6982
+ path.unlink()
6983
+ removed.append(path.name)
6984
+ except FileNotFoundError:
6985
+ pass
6986
+ except OSError:
6987
+ pass
6988
+ return removed
6989
+
6990
+
6988
6991
  def _kimi_device_headers() -> dict:
6989
6992
  """X-Msh-* device identity headers the official CLI sends on OAuth calls.
6990
6993