dulus 3.10.1__tar.gz → 3.10.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 (347) hide show
  1. {dulus-3.10.1/dulus.egg-info → dulus-3.10.2}/PKG-INFO +1 -1
  2. dulus-3.10.2/display_blocks.py +529 -0
  3. {dulus-3.10.1 → dulus-3.10.2/dulus.egg-info}/PKG-INFO +1 -1
  4. {dulus-3.10.1 → dulus-3.10.2}/dulus.egg-info/SOURCES.txt +8 -0
  5. {dulus-3.10.1 → dulus-3.10.2}/dulus.egg-info/top_level.txt +8 -0
  6. dulus-3.10.2/file_filter.py +91 -0
  7. dulus-3.10.2/paste_placeholders.py +49 -0
  8. dulus-3.10.2/profiles.py +340 -0
  9. {dulus-3.10.1 → dulus-3.10.2}/pyproject.toml +9 -1
  10. dulus-3.10.2/steer_input.py +148 -0
  11. dulus-3.10.2/tools_background.py +416 -0
  12. dulus-3.10.2/tools_think.py +68 -0
  13. dulus-3.10.2/tools_todo.py +196 -0
  14. {dulus-3.10.1 → dulus-3.10.2}/LICENSE +0 -0
  15. {dulus-3.10.1 → dulus-3.10.2}/MANIFEST.in +0 -0
  16. {dulus-3.10.1 → dulus-3.10.2}/README.md +0 -0
  17. {dulus-3.10.1 → dulus-3.10.2}/agent.py +0 -0
  18. {dulus-3.10.1 → dulus-3.10.2}/analytics.py +0 -0
  19. {dulus-3.10.1 → dulus-3.10.2}/backend/__init__.py +0 -0
  20. {dulus-3.10.1 → dulus-3.10.2}/backend/agents_bridge.py +0 -0
  21. {dulus-3.10.1 → dulus-3.10.2}/backend/compressor.py +0 -0
  22. {dulus-3.10.1 → dulus-3.10.2}/backend/context.py +0 -0
  23. {dulus-3.10.1 → dulus-3.10.2}/backend/githook.py +0 -0
  24. {dulus-3.10.1 → dulus-3.10.2}/backend/marketplace.py +0 -0
  25. {dulus-3.10.1 → dulus-3.10.2}/backend/mempalace_bridge.py +0 -0
  26. {dulus-3.10.1 → dulus-3.10.2}/backend/personas.py +0 -0
  27. {dulus-3.10.1 → dulus-3.10.2}/backend/plugins.py +0 -0
  28. {dulus-3.10.1 → dulus-3.10.2}/backend/server.py +0 -0
  29. {dulus-3.10.1 → dulus-3.10.2}/backend/tasks.py +0 -0
  30. {dulus-3.10.1 → dulus-3.10.2}/batch_api.py +0 -0
  31. {dulus-3.10.1 → dulus-3.10.2}/checkpoint/__init__.py +0 -0
  32. {dulus-3.10.1 → dulus-3.10.2}/checkpoint/hooks.py +0 -0
  33. {dulus-3.10.1 → dulus-3.10.2}/checkpoint/store.py +0 -0
  34. {dulus-3.10.1 → dulus-3.10.2}/checkpoint/types.py +0 -0
  35. {dulus-3.10.1 → dulus-3.10.2}/claude_code_watcher.py +0 -0
  36. {dulus-3.10.1 → dulus-3.10.2}/clipboard_utils.py +0 -0
  37. {dulus-3.10.1 → dulus-3.10.2}/cloudsave.py +0 -0
  38. {dulus-3.10.1 → dulus-3.10.2}/common.py +0 -0
  39. {dulus-3.10.1 → dulus-3.10.2}/compaction.py +0 -0
  40. {dulus-3.10.1 → dulus-3.10.2}/config.py +0 -0
  41. {dulus-3.10.1 → dulus-3.10.2}/context.py +0 -0
  42. {dulus-3.10.1 → dulus-3.10.2}/data/__init__.py +0 -0
  43. {dulus-3.10.1 → dulus-3.10.2}/data/active_persona.json +0 -0
  44. {dulus-3.10.1 → dulus-3.10.2}/data/marketplace.json +0 -0
  45. {dulus-3.10.1 → dulus-3.10.2}/data/personas.json +0 -0
  46. {dulus-3.10.1 → dulus-3.10.2}/data/plugins/__init__.py +0 -0
  47. {dulus-3.10.1 → dulus-3.10.2}/data/plugins/composio/__init__.py +0 -0
  48. {dulus-3.10.1 → dulus-3.10.2}/data/plugins/composio/composio_plugin/__init__.py +0 -0
  49. {dulus-3.10.1 → dulus-3.10.2}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
  50. {dulus-3.10.1 → dulus-3.10.2}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
  51. {dulus-3.10.1 → dulus-3.10.2}/data/plugins/composio/plugin.json +0 -0
  52. {dulus-3.10.1 → dulus-3.10.2}/data/plugins/composio/plugin_tool.py +0 -0
  53. {dulus-3.10.1 → dulus-3.10.2}/data/tasks.json +0 -0
  54. {dulus-3.10.1 → dulus-3.10.2}/docs/API.md +0 -0
  55. {dulus-3.10.1 → dulus-3.10.2}/docs/BRAND_GUIDE.md +0 -0
  56. {dulus-3.10.1 → dulus-3.10.2}/docs/CHANGELOG.md +0 -0
  57. {dulus-3.10.1 → dulus-3.10.2}/docs/CONTRIBUTING.md +0 -0
  58. {dulus-3.10.1 → dulus-3.10.2}/docs/DEPLOYMENT.md +0 -0
  59. {dulus-3.10.1 → dulus-3.10.2}/docs/Dulus_AI_Whitepaper_(v2.0).pdf +0 -0
  60. {dulus-3.10.1 → dulus-3.10.2}/docs/FAQ.md +0 -0
  61. {dulus-3.10.1 → dulus-3.10.2}/docs/GETTING_STARTED.md +0 -0
  62. {dulus-3.10.1 → dulus-3.10.2}/docs/INVESTOR_BRIEF.md +0 -0
  63. {dulus-3.10.1 → dulus-3.10.2}/docs/README.md +0 -0
  64. {dulus-3.10.1 → dulus-3.10.2}/docs/README_AR.md +0 -0
  65. {dulus-3.10.1 → dulus-3.10.2}/docs/README_EN.md +0 -0
  66. {dulus-3.10.1 → dulus-3.10.2}/docs/README_ES.md +0 -0
  67. {dulus-3.10.1 → dulus-3.10.2}/docs/README_FR.md +0 -0
  68. {dulus-3.10.1 → dulus-3.10.2}/docs/README_JA.md +0 -0
  69. {dulus-3.10.1 → dulus-3.10.2}/docs/README_KO.md +0 -0
  70. {dulus-3.10.1 → dulus-3.10.2}/docs/README_PT.md +0 -0
  71. {dulus-3.10.1 → dulus-3.10.2}/docs/README_RU.md +0 -0
  72. {dulus-3.10.1 → dulus-3.10.2}/docs/README_ZH.md +0 -0
  73. {dulus-3.10.1 → dulus-3.10.2}/docs/SECURITY.md +0 -0
  74. {dulus-3.10.1 → dulus-3.10.2}/docs/__init__.py +0 -0
  75. {dulus-3.10.1 → dulus-3.10.2}/docs/api.html +0 -0
  76. {dulus-3.10.1 → dulus-3.10.2}/docs/architecture.md +0 -0
  77. {dulus-3.10.1 → dulus-3.10.2}/docs/azure-speech-template.json +0 -0
  78. {dulus-3.10.1 → dulus-3.10.2}/docs/dashboard/index.html +0 -0
  79. {dulus-3.10.1 → dulus-3.10.2}/docs/divider.svg +0 -0
  80. {dulus-3.10.1 → dulus-3.10.2}/docs/dulus-bird.png +0 -0
  81. {dulus-3.10.1 → dulus-3.10.2}/docs/generate.py +0 -0
  82. {dulus-3.10.1 → dulus-3.10.2}/docs/hero.svg +0 -0
  83. {dulus-3.10.1 → dulus-3.10.2}/docs/index.html +0 -0
  84. {dulus-3.10.1 → dulus-3.10.2}/docs/news.md +0 -0
  85. {dulus-3.10.1 → dulus-3.10.2}/docs/nvidia-models.svg +0 -0
  86. {dulus-3.10.1 → dulus-3.10.2}/docs/particle-playground.html +0 -0
  87. {dulus-3.10.1 → dulus-3.10.2}/docs/personas/index.html +0 -0
  88. {dulus-3.10.1 → dulus-3.10.2}/docs/poetry-banner.png +0 -0
  89. {dulus-3.10.1 → dulus-3.10.2}/docs/preview.html +0 -0
  90. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-agents.svg +0 -0
  91. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-brainstorm.svg +0 -0
  92. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-bridges.svg +0 -0
  93. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-features.svg +0 -0
  94. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-freetier.svg +0 -0
  95. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-memory.svg +0 -0
  96. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-models.svg +0 -0
  97. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-perms.svg +0 -0
  98. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-plugins.svg +0 -0
  99. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-quickstart.svg +0 -0
  100. {dulus-3.10.1 → dulus-3.10.2}/docs/sec-ssj.svg +0 -0
  101. {dulus-3.10.1 → dulus-3.10.2}/docs/spinners.svg +0 -0
  102. {dulus-3.10.1 → dulus-3.10.2}/docs/split-pane.svg +0 -0
  103. {dulus-3.10.1 → dulus-3.10.2}/docs/terminal-boot.svg +0 -0
  104. {dulus-3.10.1 → dulus-3.10.2}/dulus.egg-info/dependency_links.txt +0 -0
  105. {dulus-3.10.1 → dulus-3.10.2}/dulus.egg-info/entry_points.txt +0 -0
  106. {dulus-3.10.1 → dulus-3.10.2}/dulus.egg-info/requires.txt +0 -0
  107. {dulus-3.10.1 → dulus-3.10.2}/dulus.py +0 -0
  108. {dulus-3.10.1 → dulus-3.10.2}/dulus_gui.py +0 -0
  109. {dulus-3.10.1 → dulus-3.10.2}/dulus_gui_web.py +0 -0
  110. {dulus-3.10.1 → dulus-3.10.2}/dulus_mcp/__init__.py +0 -0
  111. {dulus-3.10.1 → dulus-3.10.2}/dulus_mcp/client.py +0 -0
  112. {dulus-3.10.1 → dulus-3.10.2}/dulus_mcp/commands.py +0 -0
  113. {dulus-3.10.1 → dulus-3.10.2}/dulus_mcp/config.py +0 -0
  114. {dulus-3.10.1 → dulus-3.10.2}/dulus_mcp/hub.py +0 -0
  115. {dulus-3.10.1 → dulus-3.10.2}/dulus_mcp/tools.py +0 -0
  116. {dulus-3.10.1 → dulus-3.10.2}/dulus_mcp/types.py +0 -0
  117. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/__init__.py +0 -0
  118. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/add_dir_manager.py +0 -0
  119. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/afk_mode.py +0 -0
  120. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/approval_runtime.py +0 -0
  121. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/background_tasks.py +0 -0
  122. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/clipboard_utils.py +0 -0
  123. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/diff_visualizer.py +0 -0
  124. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/export_import.py +0 -0
  125. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/hook_engine.py +0 -0
  126. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/notification_manager.py +0 -0
  127. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/session_fork.py +0 -0
  128. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/shell_mode.py +0 -0
  129. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/todo_visualizer.py +0 -0
  130. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/tree_ls.py +0 -0
  131. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/wire_events.py +0 -0
  132. {dulus-3.10.1 → dulus-3.10.2}/dulus_tools/yolo_mode.py +0 -0
  133. {dulus-3.10.1 → dulus-3.10.2}/governance.py +0 -0
  134. {dulus-3.10.1 → dulus-3.10.2}/gui/__init__.py +0 -0
  135. {dulus-3.10.1 → dulus-3.10.2}/gui/agent_bridge.py +0 -0
  136. {dulus-3.10.1 → dulus-3.10.2}/gui/chat_widget.py +0 -0
  137. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/App.tsx +0 -0
  138. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/THIRD_PARTY_NOTICES.md +0 -0
  139. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/dist/index.html +0 -0
  140. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/dist/standalone.html +0 -0
  141. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/index.html +0 -0
  142. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/index.tsx +0 -0
  143. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/metadata.json +0 -0
  144. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/package-lock.json +0 -0
  145. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/package.json +0 -0
  146. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/types.ts +0 -0
  147. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/utils.ts +0 -0
  148. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/vertex-ai-proxy-interceptor.js +0 -0
  149. {dulus-3.10.1 → dulus-3.10.2}/gui/dulus_orchestrator/vite.config.ts +0 -0
  150. {dulus-3.10.1 → dulus-3.10.2}/gui/main_window.py +0 -0
  151. {dulus-3.10.1 → dulus-3.10.2}/gui/personas.py +0 -0
  152. {dulus-3.10.1 → dulus-3.10.2}/gui/sandbox_server.py +0 -0
  153. {dulus-3.10.1 → dulus-3.10.2}/gui/session_utils.py +0 -0
  154. {dulus-3.10.1 → dulus-3.10.2}/gui/settings_dialog.py +0 -0
  155. {dulus-3.10.1 → dulus-3.10.2}/gui/sidebar.py +0 -0
  156. {dulus-3.10.1 → dulus-3.10.2}/gui/tasks_view.py +0 -0
  157. {dulus-3.10.1 → dulus-3.10.2}/gui/themes.py +0 -0
  158. {dulus-3.10.1 → dulus-3.10.2}/gui/tool_panel.py +0 -0
  159. {dulus-3.10.1 → dulus-3.10.2}/input.py +0 -0
  160. {dulus-3.10.1 → dulus-3.10.2}/license_manager.py +0 -0
  161. {dulus-3.10.1 → dulus-3.10.2}/memory/__init__.py +0 -0
  162. {dulus-3.10.1 → dulus-3.10.2}/memory/audit.py +0 -0
  163. {dulus-3.10.1 → dulus-3.10.2}/memory/consolidator.py +0 -0
  164. {dulus-3.10.1 → dulus-3.10.2}/memory/context.py +0 -0
  165. {dulus-3.10.1 → dulus-3.10.2}/memory/offload.py +0 -0
  166. {dulus-3.10.1 → dulus-3.10.2}/memory/palace.py +0 -0
  167. {dulus-3.10.1 → dulus-3.10.2}/memory/scan.py +0 -0
  168. {dulus-3.10.1 → dulus-3.10.2}/memory/sessions.py +0 -0
  169. {dulus-3.10.1 → dulus-3.10.2}/memory/store.py +0 -0
  170. {dulus-3.10.1 → dulus-3.10.2}/memory/tools.py +0 -0
  171. {dulus-3.10.1 → dulus-3.10.2}/memory/types.py +0 -0
  172. {dulus-3.10.1 → dulus-3.10.2}/memory/vector_search.py +0 -0
  173. {dulus-3.10.1 → dulus-3.10.2}/multi_agent/__init__.py +0 -0
  174. {dulus-3.10.1 → dulus-3.10.2}/multi_agent/subagent.py +0 -0
  175. {dulus-3.10.1 → dulus-3.10.2}/multi_agent/tools.py +0 -0
  176. {dulus-3.10.1 → dulus-3.10.2}/offload_helper.py +0 -0
  177. {dulus-3.10.1 → dulus-3.10.2}/personality_config.py +0 -0
  178. {dulus-3.10.1 → dulus-3.10.2}/plugin/__init__.py +0 -0
  179. {dulus-3.10.1 → dulus-3.10.2}/plugin/autoadapter.py +0 -0
  180. {dulus-3.10.1 → dulus-3.10.2}/plugin/loader.py +0 -0
  181. {dulus-3.10.1 → dulus-3.10.2}/plugin/recommend.py +0 -0
  182. {dulus-3.10.1 → dulus-3.10.2}/plugin/store.py +0 -0
  183. {dulus-3.10.1 → dulus-3.10.2}/plugin/types.py +0 -0
  184. {dulus-3.10.1 → dulus-3.10.2}/providers.py +0 -0
  185. {dulus-3.10.1 → dulus-3.10.2}/sandbox/README.md +0 -0
  186. {dulus-3.10.1 → dulus-3.10.2}/sandbox/__init__.py +0 -0
  187. {dulus-3.10.1 → dulus-3.10.2}/sandbox/components.json +0 -0
  188. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
  189. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
  190. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/index.html +0 -0
  191. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/wallpaper-default.jpg +0 -0
  192. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/wallpapers/default.jpeg +0 -0
  193. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/wallpapers/light.jpeg +0 -0
  194. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/wallpapers/nature.jpeg +0 -0
  195. {dulus-3.10.1 → dulus-3.10.2}/sandbox/dist/wallpapers/tech.jpeg +0 -0
  196. {dulus-3.10.1 → dulus-3.10.2}/sandbox/eslint.config.js +0 -0
  197. {dulus-3.10.1 → dulus-3.10.2}/sandbox/index.html +0 -0
  198. {dulus-3.10.1 → dulus-3.10.2}/sandbox/info.md +0 -0
  199. {dulus-3.10.1 → dulus-3.10.2}/sandbox/package-lock.json +0 -0
  200. {dulus-3.10.1 → dulus-3.10.2}/sandbox/package.json +0 -0
  201. {dulus-3.10.1 → dulus-3.10.2}/sandbox/postcss.config.js +0 -0
  202. {dulus-3.10.1 → dulus-3.10.2}/sandbox/tailwind.config.js +0 -0
  203. {dulus-3.10.1 → dulus-3.10.2}/sandbox/tsconfig.app.json +0 -0
  204. {dulus-3.10.1 → dulus-3.10.2}/sandbox/tsconfig.json +0 -0
  205. {dulus-3.10.1 → dulus-3.10.2}/sandbox/tsconfig.node.json +0 -0
  206. {dulus-3.10.1 → dulus-3.10.2}/sandbox/vite.config.ts +0 -0
  207. {dulus-3.10.1 → dulus-3.10.2}/sandbox_bootstrap.py +0 -0
  208. {dulus-3.10.1 → dulus-3.10.2}/setup.cfg +0 -0
  209. {dulus-3.10.1 → dulus-3.10.2}/skill/__init__.py +0 -0
  210. {dulus-3.10.1 → dulus-3.10.2}/skill/builtin.py +0 -0
  211. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/LICENSE-obsidian-skills +0 -0
  212. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/README.md +0 -0
  213. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/defuddle/SKILL.md +0 -0
  214. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/json-canvas/SKILL.md +0 -0
  215. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/json-canvas/references/EXAMPLES.md +0 -0
  216. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/obsidian-bases/SKILL.md +0 -0
  217. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +0 -0
  218. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/obsidian-cli/SKILL.md +0 -0
  219. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/obsidian-markdown/SKILL.md +0 -0
  220. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/obsidian-markdown/references/CALLOUTS.md +0 -0
  221. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/obsidian-markdown/references/EMBEDS.md +0 -0
  222. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/obsidian-markdown/references/PROPERTIES.md +0 -0
  223. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/README.md +0 -0
  224. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/SKILL.md +0 -0
  225. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/addresses.json +0 -0
  226. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/advancedbackgroundchecks.json +0 -0
  227. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/beenverified.json +0 -0
  228. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/clustal.json +0 -0
  229. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/clustrmaps.json +0 -0
  230. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/cyberbackgroundchecks.json +0 -0
  231. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/familytreenow.json +0 -0
  232. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/fastpeoplesearch.json +0 -0
  233. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/intelius.json +0 -0
  234. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/mylife.json +0 -0
  235. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/nuwber.json +0 -0
  236. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/peekyou.json +0 -0
  237. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/peoplefinders.json +0 -0
  238. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/radaris.json +0 -0
  239. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/rehold.json +0 -0
  240. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/searchpeoplefree.json +0 -0
  241. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/socialcatfish.json +0 -0
  242. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/spokeo.json +0 -0
  243. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/thatsthem.json +0 -0
  244. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/truepeoplesearch.json +0 -0
  245. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/usphonebook.json +0 -0
  246. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/brokers/whitepages.json +0 -0
  247. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/legal/ccpa.md +0 -0
  248. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/legal/drop.md +0 -0
  249. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/legal/gdpr.md +0 -0
  250. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/methods.md +0 -0
  251. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/site-playbooks.md +0 -0
  252. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/references/state-machine.md +0 -0
  253. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/autopilot.py +0 -0
  254. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/badbool.py +0 -0
  255. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/brokers.py +0 -0
  256. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/cdp.py +0 -0
  257. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/config.py +0 -0
  258. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/crypto.py +0 -0
  259. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/dossier.py +0 -0
  260. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/email_modes.py +0 -0
  261. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/emailer.py +0 -0
  262. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/ledger.py +0 -0
  263. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/legal.py +0 -0
  264. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/paths.py +0 -0
  265. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/pdd.py +0 -0
  266. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/registry.py +0 -0
  267. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/report.py +0 -0
  268. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/scan.py +0 -0
  269. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/storage.py +0 -0
  270. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/tiers.py +0 -0
  271. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/scripts/vectors.py +0 -0
  272. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/templates/consent/authorization.md +0 -0
  273. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/templates/emails/ccpa-authorized-agent.txt +0 -0
  274. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/templates/emails/ccpa-deletion.txt +0 -0
  275. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/templates/emails/ccpa-indirect-deletion.txt +0 -0
  276. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/templates/emails/gdpr-erasure.txt +0 -0
  277. {dulus-3.10.1 → dulus-3.10.2}/skill/bundled/unbroker/templates/emails/generic-optout.txt +0 -0
  278. {dulus-3.10.1 → dulus-3.10.2}/skill/clawhub.py +0 -0
  279. {dulus-3.10.1 → dulus-3.10.2}/skill/executor.py +0 -0
  280. {dulus-3.10.1 → dulus-3.10.2}/skill/loader.py +0 -0
  281. {dulus-3.10.1 → dulus-3.10.2}/skill/tools.py +0 -0
  282. {dulus-3.10.1 → dulus-3.10.2}/skills.py +0 -0
  283. {dulus-3.10.1 → dulus-3.10.2}/soul.py +0 -0
  284. {dulus-3.10.1 → dulus-3.10.2}/spinner.py +0 -0
  285. {dulus-3.10.1 → dulus-3.10.2}/string_utils.py +0 -0
  286. {dulus-3.10.1 → dulus-3.10.2}/subagent.py +0 -0
  287. {dulus-3.10.1 → dulus-3.10.2}/task/__init__.py +0 -0
  288. {dulus-3.10.1 → dulus-3.10.2}/task/store.py +0 -0
  289. {dulus-3.10.1 → dulus-3.10.2}/task/tools.py +0 -0
  290. {dulus-3.10.1 → dulus-3.10.2}/task/types.py +0 -0
  291. {dulus-3.10.1 → dulus-3.10.2}/tests/test_afk_yolo.py +0 -0
  292. {dulus-3.10.1 → dulus-3.10.2}/tests/test_approval_runtime.py +0 -0
  293. {dulus-3.10.1 → dulus-3.10.2}/tests/test_background_task_tools.py +0 -0
  294. {dulus-3.10.1 → dulus-3.10.2}/tests/test_background_tasks.py +0 -0
  295. {dulus-3.10.1 → dulus-3.10.2}/tests/test_checkpoint.py +0 -0
  296. {dulus-3.10.1 → dulus-3.10.2}/tests/test_clipboard_utils.py +0 -0
  297. {dulus-3.10.1 → dulus-3.10.2}/tests/test_compaction.py +0 -0
  298. {dulus-3.10.1 → dulus-3.10.2}/tests/test_diff_view.py +0 -0
  299. {dulus-3.10.1 → dulus-3.10.2}/tests/test_diff_visualization.py +0 -0
  300. {dulus-3.10.1 → dulus-3.10.2}/tests/test_display_blocks.py +0 -0
  301. {dulus-3.10.1 → dulus-3.10.2}/tests/test_export_import.py +0 -0
  302. {dulus-3.10.1 → dulus-3.10.2}/tests/test_hook_engine.py +0 -0
  303. {dulus-3.10.1 → dulus-3.10.2}/tests/test_injection_fix.py +0 -0
  304. {dulus-3.10.1 → dulus-3.10.2}/tests/test_license.py +0 -0
  305. {dulus-3.10.1 → dulus-3.10.2}/tests/test_mcp.py +0 -0
  306. {dulus-3.10.1 → dulus-3.10.2}/tests/test_memory.py +0 -0
  307. {dulus-3.10.1 → dulus-3.10.2}/tests/test_notification_manager.py +0 -0
  308. {dulus-3.10.1 → dulus-3.10.2}/tests/test_plugin.py +0 -0
  309. {dulus-3.10.1 → dulus-3.10.2}/tests/test_session_fork.py +0 -0
  310. {dulus-3.10.1 → dulus-3.10.2}/tests/test_shell_mode.py +0 -0
  311. {dulus-3.10.1 → dulus-3.10.2}/tests/test_skills.py +0 -0
  312. {dulus-3.10.1 → dulus-3.10.2}/tests/test_steer_input.py +0 -0
  313. {dulus-3.10.1 → dulus-3.10.2}/tests/test_subagent.py +0 -0
  314. {dulus-3.10.1 → dulus-3.10.2}/tests/test_task.py +0 -0
  315. {dulus-3.10.1 → dulus-3.10.2}/tests/test_telegram_buffer.py +0 -0
  316. {dulus-3.10.1 → dulus-3.10.2}/tests/test_think_tool.py +0 -0
  317. {dulus-3.10.1 → dulus-3.10.2}/tests/test_todo_tool.py +0 -0
  318. {dulus-3.10.1 → dulus-3.10.2}/tests/test_todo_visualization.py +0 -0
  319. {dulus-3.10.1 → dulus-3.10.2}/tests/test_tool_registry.py +0 -0
  320. {dulus-3.10.1 → dulus-3.10.2}/tests/test_tree_ls.py +0 -0
  321. {dulus-3.10.1 → dulus-3.10.2}/tests/test_voice.py +0 -0
  322. {dulus-3.10.1 → dulus-3.10.2}/tests/test_wire_events.py +0 -0
  323. {dulus-3.10.1 → dulus-3.10.2}/tmux_offloader.py +0 -0
  324. {dulus-3.10.1 → dulus-3.10.2}/tmux_tools.py +0 -0
  325. {dulus-3.10.1 → dulus-3.10.2}/tool_registry.py +0 -0
  326. {dulus-3.10.1 → dulus-3.10.2}/tools.py +0 -0
  327. {dulus-3.10.1 → dulus-3.10.2}/ui/__init__.py +0 -0
  328. {dulus-3.10.1 → dulus-3.10.2}/ui/input.py +0 -0
  329. {dulus-3.10.1 → dulus-3.10.2}/ui/render.py +0 -0
  330. {dulus-3.10.1 → dulus-3.10.2}/updater.py +0 -0
  331. {dulus-3.10.1 → dulus-3.10.2}/voice/__init__.py +0 -0
  332. {dulus-3.10.1 → dulus-3.10.2}/voice/audio_utils.py +0 -0
  333. {dulus-3.10.1 → dulus-3.10.2}/voice/keyterms.py +0 -0
  334. {dulus-3.10.1 → dulus-3.10.2}/voice/recorder.py +0 -0
  335. {dulus-3.10.1 → dulus-3.10.2}/voice/stt.py +0 -0
  336. {dulus-3.10.1 → dulus-3.10.2}/voice/tts.py +0 -0
  337. {dulus-3.10.1 → dulus-3.10.2}/voice/wake_word.py +0 -0
  338. {dulus-3.10.1 → dulus-3.10.2}/webbridge/__init__.py +0 -0
  339. {dulus-3.10.1 → dulus-3.10.2}/webbridge/core.py +0 -0
  340. {dulus-3.10.1 → dulus-3.10.2}/webbridge/tools.py +0 -0
  341. {dulus-3.10.1 → dulus-3.10.2}/webchat.py +0 -0
  342. {dulus-3.10.1 → dulus-3.10.2}/webchat_server.py +0 -0
  343. {dulus-3.10.1 → dulus-3.10.2}/webchat_ui/__init__.py +0 -0
  344. {dulus-3.10.1 → dulus-3.10.2}/webchat_ui/app.js +0 -0
  345. {dulus-3.10.1 → dulus-3.10.2}/webchat_ui/index.html +0 -0
  346. {dulus-3.10.1 → dulus-3.10.2}/webchat_ui/style.css +0 -0
  347. {dulus-3.10.1 → dulus-3.10.2}/welcome.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 3.10.1
3
+ Version: 3.10.2
4
4
  Summary: Use IA as an agent without an API key. $0. Harvest your browser session for Gemini (guest, no login), Claude.ai, Claude Code, Kimi.com, Qwen, DeepSeek — drive them like an API, no per-token billing. Plus LiteLLM (100+ paid providers), local models via Ollama, /lang in 34 languages, Mesa Redonda, voice, OCR, MemPalace, embedded sandbox OS.
5
5
  Author: KevRojo
6
6
  License: GPL-3.0
@@ -0,0 +1,529 @@
1
+ """Display Blocks System - Visual representations of agent actions.
2
+
3
+ Provides a structured way to represent agent actions visually across multiple
4
+ frontends: CLI (dulus.py), WebChat (webchat_server.py), and Telegram.
5
+
6
+ Display blocks are dict-based and can be rendered in various formats.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import difflib
11
+ import html
12
+ from typing import Literal
13
+
14
+
15
+ # ── Display block type hints ─────────────────────────────────────────────────
16
+
17
+ # These are the supported display block types
18
+ DisplayBlockType = Literal[
19
+ "diff",
20
+ "todo",
21
+ "shell",
22
+ "background_task",
23
+ "think",
24
+ "code",
25
+ "table",
26
+ "error",
27
+ ]
28
+
29
+
30
+ # ── CLI Renderer ─────────────────────────────────────────────────────────────
31
+
32
+
33
+ class DisplayBlockRenderer:
34
+ """Render display blocks in various formats for different frontends.
35
+
36
+ Supports CLI (terminal), HTML (WebChat), and Telegram (Markdown) output formats.
37
+ Each render method takes a display block dict and returns a formatted string.
38
+ """
39
+
40
+ # ── Public API ──────────────────────────────────────────────────────────
41
+
42
+ @staticmethod
43
+ def render_cli(block: dict) -> str:
44
+ """Render a display block for CLI (terminal) output.
45
+
46
+ Args:
47
+ block: Display block dict with at least a 'type' key.
48
+
49
+ Returns:
50
+ Formatted string suitable for terminal display.
51
+ """
52
+ block_type = block.get("type", "unknown")
53
+ renderer = _CLI_RENDERERS.get(block_type, _CLI_RENDERERS["unknown"])
54
+ return renderer(block)
55
+
56
+ @staticmethod
57
+ def render_html(block: dict) -> str:
58
+ """Render a display block as HTML for WebChat.
59
+
60
+ Args:
61
+ block: Display block dict with at least a 'type' key.
62
+
63
+ Returns:
64
+ HTML string suitable for web display.
65
+ """
66
+ block_type = block.get("type", "unknown")
67
+ renderer = _HTML_RENDERERS.get(block_type, _HTML_RENDERERS["unknown"])
68
+ return renderer(block)
69
+
70
+ @staticmethod
71
+ def render_telegram(block: dict) -> str:
72
+ """Render a display block for Telegram (Markdown V2).
73
+
74
+ Args:
75
+ block: Display block dict with at least a 'type' key.
76
+
77
+ Returns:
78
+ Formatted string using Telegram Markdown V2 syntax.
79
+ """
80
+ block_type = block.get("type", "unknown")
81
+ renderer = _TG_RENDERERS.get(block_type, _TG_RENDERERS["unknown"])
82
+ return renderer(block)
83
+
84
+ @staticmethod
85
+ def render(block: dict, format: Literal["cli", "html", "telegram"] = "cli") -> str:
86
+ """Render a display block in the specified format.
87
+
88
+ Args:
89
+ block: Display block dict with at least a 'type' key.
90
+ format: Target format - "cli", "html", or "telegram".
91
+
92
+ Returns:
93
+ Formatted string in the requested format.
94
+ """
95
+ if format == "cli":
96
+ return DisplayBlockRenderer.render_cli(block)
97
+ elif format == "html":
98
+ return DisplayBlockRenderer.render_html(block)
99
+ elif format == "telegram":
100
+ return DisplayBlockRenderer.render_telegram(block)
101
+ return str(block)
102
+
103
+
104
+ # ── CLI Renderers ────────────────────────────────────────────────────────────
105
+
106
+
107
+ def _render_diff_cli(block: dict) -> str:
108
+ """Render a diff display block for CLI.
109
+
110
+ Shows a unified diff between old and new text.
111
+ """
112
+ path = block.get("path", "<unknown>")
113
+ old_text = block.get("old_text", "")
114
+ new_text = block.get("new_text", "")
115
+ old_start = block.get("old_start", 1)
116
+ new_start = block.get("new_start", 1)
117
+ is_summary = block.get("is_summary", False)
118
+
119
+ if is_summary:
120
+ return f"[Diff: {path}] (summary)"
121
+
122
+ old_lines = old_text.splitlines(keepends=True)
123
+ new_lines = new_text.splitlines(keepends=True)
124
+
125
+ # Generate unified diff
126
+ diff = difflib.unified_diff(
127
+ old_lines,
128
+ new_lines,
129
+ fromfile=f"a/{path}",
130
+ tofile=f"b/{path}",
131
+ lineterm="",
132
+ )
133
+ diff_text = "\n".join(diff)
134
+
135
+ if not diff_text:
136
+ return f"[Diff: {path}] No changes"
137
+
138
+ return f"[Diff: {path}]\n{diff_text}"
139
+
140
+
141
+ def _render_todo_cli(block: dict) -> str:
142
+ """Render a todo display block for CLI."""
143
+ items = block.get("items", [])
144
+ if not items:
145
+ return "[Todo] No items"
146
+
147
+ lines = ["Todo List:"]
148
+ icons = {"pending": "[ ]", "in_progress": "[/]", "done": "[x]"}
149
+ for item in items:
150
+ status = item.get("status", "pending")
151
+ icon = icons.get(status, "[ ]")
152
+ lines.append(f" {icon} {item.get('title', '')}")
153
+ return "\n".join(lines)
154
+
155
+
156
+ def _render_shell_cli(block: dict) -> str:
157
+ """Render a shell command display block for CLI."""
158
+ command = block.get("command", "")
159
+ language = block.get("language", "bash")
160
+ return f"$ {command}"
161
+
162
+
163
+ def _render_bg_task_cli(block: dict) -> str:
164
+ """Render a background task display block for CLI."""
165
+ task_id = block.get("task_id", "?")
166
+ kind = block.get("kind", "?")
167
+ status = block.get("status", "?")
168
+ description = block.get("description", "")
169
+
170
+ icons = {
171
+ "running": "●",
172
+ "completed": "✓",
173
+ "failed": "✗",
174
+ "stopped": "■",
175
+ }
176
+ icon = icons.get(status, "?")
177
+ return f"{icon} [{task_id}] {description} ({kind}, {status})"
178
+
179
+
180
+ def _render_think_cli(block: dict) -> str:
181
+ """Render a think display block for CLI."""
182
+ thought = block.get("thought", "")
183
+ lines = thought.splitlines()
184
+ # Indent thought content for visual distinction
185
+ indented = "\n".join(f" | {line}" for line in lines)
186
+ return f"[Thinking...]\n{indented}"
187
+
188
+
189
+ def _render_code_cli(block: dict) -> str:
190
+ """Render a code display block for CLI."""
191
+ code = block.get("code", "")
192
+ language = block.get("language", "")
193
+ header = f"```{language}" if language else "```"
194
+ return f"{header}\n{code}\n```"
195
+
196
+
197
+ def _render_table_cli(block: dict) -> str:
198
+ """Render a table display block for CLI."""
199
+ headers = block.get("headers", [])
200
+ rows = block.get("rows", [])
201
+ if not headers and not rows:
202
+ return "[Table] Empty"
203
+
204
+ # Simple text table rendering
205
+ all_rows = [headers] + rows if headers else rows
206
+ # Calculate column widths
207
+ col_count = max(len(r) for r in all_rows) if all_rows else 0
208
+ widths = [0] * col_count
209
+ for row in all_rows:
210
+ for i, cell in enumerate(row):
211
+ widths[i] = max(widths[i], len(str(cell)))
212
+
213
+ def _fmt_row(row: list) -> str:
214
+ cells = [str(cell).ljust(widths[i]) for i, cell in enumerate(row)]
215
+ return " | ".join(cells)
216
+
217
+ lines = []
218
+ if headers:
219
+ lines.append(_fmt_row(headers))
220
+ lines.append("-" * (sum(widths) + 3 * (col_count - 1)))
221
+ for row in rows:
222
+ lines.append(_fmt_row(row))
223
+ return "\n".join(lines)
224
+
225
+
226
+ def _render_error_cli(block: dict) -> str:
227
+ """Render an error display block for CLI."""
228
+ message = block.get("message", "Unknown error")
229
+ return f"[Error] {message}"
230
+
231
+
232
+ def _render_unknown_cli(block: dict) -> str:
233
+ """Render an unknown display block type for CLI."""
234
+ return f"[Display Block: {block.get('type', 'unknown')}]\n{str(block)}"
235
+
236
+
237
+ _CLI_RENDERERS: dict[str, callable] = {
238
+ "diff": _render_diff_cli,
239
+ "todo": _render_todo_cli,
240
+ "shell": _render_shell_cli,
241
+ "background_task": _render_bg_task_cli,
242
+ "think": _render_think_cli,
243
+ "code": _render_code_cli,
244
+ "table": _render_table_cli,
245
+ "error": _render_error_cli,
246
+ "unknown": _render_unknown_cli,
247
+ }
248
+
249
+
250
+ # ── HTML Renderers ───────────────────────────────────────────────────────────
251
+
252
+
253
+ def _render_diff_html(block: dict) -> str:
254
+ """Render a diff display block as HTML."""
255
+ path = html.escape(block.get("path", "<unknown>"))
256
+ old_text = html.escape(block.get("old_text", ""))
257
+ new_text = html.escape(block.get("new_text", ""))
258
+
259
+ return (
260
+ f'<div class="display-block diff-block">\n'
261
+ f' <div class="diff-header">{path}</div>\n'
262
+ f' <pre class="diff-old"><code>{old_text}</code></pre>\n'
263
+ f' <pre class="diff-new"><code>{new_text}</code></pre>\n'
264
+ f"</div>"
265
+ )
266
+
267
+
268
+ def _render_todo_html(block: dict) -> str:
269
+ """Render a todo display block as HTML."""
270
+ items = block.get("items", [])
271
+ if not items:
272
+ return '<div class="display-block todo-block"><p>No items</p></div>'
273
+
274
+ lines = ['<div class="display-block todo-block"><ul>']
275
+ icons = {
276
+ "pending": "⬜",
277
+ "in_progress": "🔄",
278
+ "done": "✅",
279
+ }
280
+ for item in items:
281
+ status = item.get("status", "pending")
282
+ icon = icons.get(status, "⬜")
283
+ title = html.escape(item.get("title", ""))
284
+ css_class = f"todo-{status}"
285
+ lines.append(f' <li class="{css_class}">{icon} {title}</li>')
286
+ lines.append("</ul></div>")
287
+ return "\n".join(lines)
288
+
289
+
290
+ def _render_shell_html(block: dict) -> str:
291
+ """Render a shell command display block as HTML."""
292
+ command = html.escape(block.get("command", ""))
293
+ language = html.escape(block.get("language", "bash"))
294
+ return (
295
+ f'<div class="display-block shell-block">\n'
296
+ f' <pre><code class="language-{language}">${command}</code></pre>\n'
297
+ f"</div>"
298
+ )
299
+
300
+
301
+ def _render_bg_task_html(block: dict) -> str:
302
+ """Render a background task display block as HTML."""
303
+ task_id = html.escape(block.get("task_id", "?"))
304
+ kind = html.escape(block.get("kind", "?"))
305
+ status = html.escape(block.get("status", "?"))
306
+ description = html.escape(block.get("description", ""))
307
+
308
+ status_colors = {
309
+ "running": "#2196F3",
310
+ "completed": "#4CAF50",
311
+ "failed": "#f44336",
312
+ "stopped": "#FF9800",
313
+ }
314
+ color = status_colors.get(status, "#9E9E9E")
315
+
316
+ return (
317
+ f'<div class="display-block bg-task-block">\n'
318
+ f' <span class="task-id">[{task_id}]</span>\n'
319
+ f' <span class="task-desc">{description}</span>\n'
320
+ f' <span class="task-kind">({kind})</span>\n'
321
+ f' <span class="task-status" style="color: {color};">{status}</span>\n'
322
+ f"</div>"
323
+ )
324
+
325
+
326
+ def _render_think_html(block: dict) -> str:
327
+ """Render a think display block as HTML."""
328
+ thought = html.escape(block.get("thought", ""))
329
+ return (
330
+ f'<div class="display-block think-block">\n'
331
+ f' <div class="think-header">💭 Thinking</div>\n'
332
+ f' <pre class="think-content">{thought}</pre>\n'
333
+ f"</div>"
334
+ )
335
+
336
+
337
+ def _render_code_html(block: dict) -> str:
338
+ """Render a code display block as HTML."""
339
+ code = html.escape(block.get("code", ""))
340
+ language = html.escape(block.get("language", ""))
341
+ return (
342
+ f'<div class="display-block code-block">\n'
343
+ f' <pre><code class="language-{language}">{code}</code></pre>\n'
344
+ f"</div>"
345
+ )
346
+
347
+
348
+ def _render_table_html(block: dict) -> str:
349
+ """Render a table display block as HTML."""
350
+ headers = block.get("headers", [])
351
+ rows = block.get("rows", [])
352
+ if not headers and not rows:
353
+ return '<div class="display-block table-block"><p>Empty table</p></div>'
354
+
355
+ lines = ['<div class="display-block table-block">', "<table>"]
356
+ if headers:
357
+ lines.append(" <tr>" + "".join(f"<th>{html.escape(str(h))}</th>" for h in headers) + "</tr>")
358
+ for row in rows:
359
+ lines.append(" <tr>" + "".join(f"<td>{html.escape(str(c))}</td>" for c in row) + "</tr>")
360
+ lines.extend(["</table>", "</div>"])
361
+ return "\n".join(lines)
362
+
363
+
364
+ def _render_error_html(block: dict) -> str:
365
+ """Render an error display block as HTML."""
366
+ message = html.escape(block.get("message", "Unknown error"))
367
+ return (
368
+ f'<div class="display-block error-block" style="color: #f44336;">\n'
369
+ f' <strong>Error:</strong> {message}\n'
370
+ f"</div>"
371
+ )
372
+
373
+
374
+ def _render_unknown_html(block: dict) -> str:
375
+ """Render an unknown display block type as HTML."""
376
+ content = html.escape(str(block))
377
+ return f'<div class="display-block unknown-block"><pre>{content}</pre></div>'
378
+
379
+
380
+ _HTML_RENDERERS: dict[str, callable] = {
381
+ "diff": _render_diff_html,
382
+ "todo": _render_todo_html,
383
+ "shell": _render_shell_html,
384
+ "background_task": _render_bg_task_html,
385
+ "think": _render_think_html,
386
+ "code": _render_code_html,
387
+ "table": _render_table_html,
388
+ "error": _render_error_html,
389
+ "unknown": _render_unknown_html,
390
+ }
391
+
392
+
393
+ # ── Telegram Renderers ───────────────────────────────────────────────────────
394
+
395
+
396
+ def _render_diff_telegram(block: dict) -> str:
397
+ """Render a diff display block for Telegram (Markdown V2).
398
+
399
+ Note: Telegram has limited formatting for diffs, so we keep it simple.
400
+ """
401
+ path = block.get("path", "<unknown>")
402
+ old_text = block.get("old_text", "")
403
+ new_text = block.get("new_text", "")
404
+
405
+ # Escape Telegram Markdown V2 special chars
406
+ def _esc(text: str) -> str:
407
+ for c in "_[]()~`>#+-=|{}.!":
408
+ text = text.replace(c, f"\\{c}")
409
+ return text
410
+
411
+ return f"📝 *{_esc(path)}*\n```diff\n- {_esc('Old')}\n+ {_esc('New')}\n```"
412
+
413
+
414
+ def _render_todo_telegram(block: dict) -> str:
415
+ """Render a todo display block for Telegram (Markdown V2)."""
416
+ items = block.get("items", [])
417
+ if not items:
418
+ return "📋 *Todo List:* Empty"
419
+
420
+ def _esc(text: str) -> str:
421
+ for c in "_[]()~`>#+-=|{}.!":
422
+ text = text.replace(c, f"\\{c}")
423
+ return text
424
+
425
+ icons = {
426
+ "pending": "⬜",
427
+ "in_progress": "🔄",
428
+ "done": "✅",
429
+ }
430
+ lines = ["📋 *Todo List:*"]
431
+ for item in items:
432
+ status = item.get("status", "pending")
433
+ icon = icons.get(status, "⬜")
434
+ title = _esc(item.get("title", ""))
435
+ lines.append(f"{icon} {title}")
436
+ return "\n".join(lines)
437
+
438
+
439
+ def _render_shell_telegram(block: dict) -> str:
440
+ """Render a shell command display block for Telegram."""
441
+ command = block.get("command", "")
442
+ return f"```bash\n$ {command}\n```"
443
+
444
+
445
+ def _render_bg_task_telegram(block: dict) -> str:
446
+ """Render a background task display block for Telegram."""
447
+ task_id = block.get("task_id", "?")
448
+ kind = block.get("kind", "?")
449
+ status = block.get("status", "?")
450
+ description = block.get("description", "")
451
+
452
+ icons = {
453
+ "running": "🔄",
454
+ "completed": "✅",
455
+ "failed": "❌",
456
+ "stopped": "🛑",
457
+ }
458
+ icon = icons.get(status, "❓")
459
+
460
+ def _esc(text: str) -> str:
461
+ for c in "_[]()~`>#+-=|{}.!":
462
+ text = text.replace(c, f"\\{c}")
463
+ return text
464
+
465
+ return f"{icon} *Task `{task_id}`*\n{_esc(description)}\n({_esc(kind)}, {_esc(status)})"
466
+
467
+
468
+ def _render_think_telegram(block: dict) -> str:
469
+ """Render a think display block for Telegram."""
470
+ thought = block.get("thought", "")
471
+ return f"💭 *Thinking*\n```\n{thought[:3000]}\n```"
472
+
473
+
474
+ def _render_code_telegram(block: dict) -> str:
475
+ """Render a code display block for Telegram."""
476
+ code = block.get("code", "")
477
+ language = block.get("language", "")
478
+ return f"```{language}\n{code[:3000]}\n```"
479
+
480
+
481
+ def _render_table_telegram(block: dict) -> str:
482
+ """Render a table display block for Telegram."""
483
+ headers = block.get("headers", [])
484
+ rows = block.get("rows", [])
485
+ if not headers and not rows:
486
+ return "📊 *Table:* Empty"
487
+
488
+ def _esc(text: str) -> str:
489
+ for c in "_[]()~`>#+-=|{}.!":
490
+ text = text.replace(c, f"\\{c}")
491
+ return text
492
+
493
+ lines = []
494
+ if headers:
495
+ lines.append(" | ".join(_esc(str(h)) for h in headers))
496
+ lines.append("—" * 20)
497
+ for row in rows:
498
+ lines.append(" | ".join(_esc(str(c)) for c in row))
499
+ return "\n".join(lines)
500
+
501
+
502
+ def _render_error_telegram(block: dict) -> str:
503
+ """Render an error display block for Telegram."""
504
+ message = block.get("message", "Unknown error")
505
+
506
+ def _esc(text: str) -> str:
507
+ for c in "_[]()~`>#+-=|{}.!":
508
+ text = text.replace(c, f"\\{c}")
509
+ return text
510
+
511
+ return f"❌ *Error:* {_esc(message)}"
512
+
513
+
514
+ def _render_unknown_telegram(block: dict) -> str:
515
+ """Render an unknown display block type for Telegram."""
516
+ return f"```\n{str(block)[:1000]}\n```"
517
+
518
+
519
+ _TG_RENDERERS: dict[str, callable] = {
520
+ "diff": _render_diff_telegram,
521
+ "todo": _render_todo_telegram,
522
+ "shell": _render_shell_telegram,
523
+ "background_task": _render_bg_task_telegram,
524
+ "think": _render_think_telegram,
525
+ "code": _render_code_telegram,
526
+ "table": _render_table_telegram,
527
+ "error": _render_error_telegram,
528
+ "unknown": _render_unknown_telegram,
529
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 3.10.1
3
+ Version: 3.10.2
4
4
  Summary: Use IA as an agent without an API key. $0. Harvest your browser session for Gemini (guest, no login), Claude.ai, Claude Code, Kimi.com, Qwen, DeepSeek — drive them like an API, no per-token billing. Plus LiteLLM (100+ paid providers), local models via Ollama, /lang in 34 languages, Mesa Redonda, voice, OCR, MemPalace, embedded sandbox OS.
5
5
  Author: KevRojo
6
6
  License: GPL-3.0
@@ -11,26 +11,34 @@ common.py
11
11
  compaction.py
12
12
  config.py
13
13
  context.py
14
+ display_blocks.py
14
15
  dulus.py
15
16
  dulus_gui.py
16
17
  dulus_gui_web.py
18
+ file_filter.py
17
19
  governance.py
18
20
  input.py
19
21
  license_manager.py
20
22
  offload_helper.py
23
+ paste_placeholders.py
21
24
  personality_config.py
25
+ profiles.py
22
26
  providers.py
23
27
  pyproject.toml
24
28
  sandbox_bootstrap.py
25
29
  skills.py
26
30
  soul.py
27
31
  spinner.py
32
+ steer_input.py
28
33
  string_utils.py
29
34
  subagent.py
30
35
  tmux_offloader.py
31
36
  tmux_tools.py
32
37
  tool_registry.py
33
38
  tools.py
39
+ tools_background.py
40
+ tools_think.py
41
+ tools_todo.py
34
42
  updater.py
35
43
  webchat.py
36
44
  webchat_server.py
@@ -11,12 +11,14 @@ compaction
11
11
  config
12
12
  context
13
13
  data
14
+ display_blocks
14
15
  docs
15
16
  dulus
16
17
  dulus_gui
17
18
  dulus_gui_web
18
19
  dulus_mcp
19
20
  dulus_tools
21
+ file_filter
20
22
  governance
21
23
  gui
22
24
  input
@@ -24,8 +26,10 @@ license_manager
24
26
  memory
25
27
  multi_agent
26
28
  offload_helper
29
+ paste_placeholders
27
30
  personality_config
28
31
  plugin
32
+ profiles
29
33
  providers
30
34
  sandbox
31
35
  sandbox_bootstrap
@@ -33,6 +37,7 @@ skill
33
37
  skills
34
38
  soul
35
39
  spinner
40
+ steer_input
36
41
  string_utils
37
42
  subagent
38
43
  task
@@ -40,6 +45,9 @@ tmux_offloader
40
45
  tmux_tools
41
46
  tool_registry
42
47
  tools
48
+ tools_background
49
+ tools_think
50
+ tools_todo
43
51
  ui
44
52
  updater
45
53
  voice