voidx 2.0.6__tar.gz → 2.2.0__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 (258) hide show
  1. {voidx-2.0.6 → voidx-2.2.0}/PKG-INFO +1 -1
  2. {voidx-2.0.6 → voidx-2.2.0}/pyproject.toml +5 -3
  3. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/__init__.py +1 -1
  4. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/agents.py +65 -31
  5. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/compaction.py +69 -57
  6. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/contracts.py +28 -11
  7. voidx-2.2.0/src/voidx/agent/graph/convergence.py +169 -0
  8. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/core.py +329 -133
  9. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/permissions.py +1 -2
  10. voidx-2.2.0/src/voidx/agent/graph/run_loop.py +304 -0
  11. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/runtime.py +1 -3
  12. voidx-2.2.0/src/voidx/agent/graph/session_mixin.py +60 -0
  13. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/streaming.py +5 -2
  14. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/subagent.py +76 -41
  15. voidx-2.2.0/src/voidx/agent/graph/title_mixin.py +211 -0
  16. voidx-2.2.0/src/voidx/agent/graph/todo_events.py +21 -0
  17. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/tool_execution.py +162 -14
  18. voidx-2.2.0/src/voidx/agent/graph/topology.py +95 -0
  19. voidx-2.2.0/src/voidx/agent/graph/transcript_mixin.py +34 -0
  20. voidx-2.2.0/src/voidx/agent/graph/turn_mixin.py +430 -0
  21. voidx-2.2.0/src/voidx/agent/graph/wiring.py +105 -0
  22. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/intent_refinement.py +7 -5
  23. voidx-2.2.0/src/voidx/agent/message_rows.py +99 -0
  24. voidx-2.2.0/src/voidx/agent/runtime_context.py +608 -0
  25. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/code_ide.py +1 -1
  26. voidx-2.2.0/src/voidx/agent/slash/guide.py +19 -0
  27. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/handler.py +301 -149
  28. voidx-2.2.0/src/voidx/agent/slash/init.py +95 -0
  29. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/lsp.py +7 -0
  30. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/mcp.py +32 -2
  31. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/model.py +0 -20
  32. voidx-2.2.0/src/voidx/agent/slash/profile.py +161 -0
  33. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/runtime.py +22 -0
  34. voidx-2.2.0/src/voidx/agent/slash/session.py +239 -0
  35. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/state.py +1 -0
  36. voidx-2.2.0/src/voidx/agent/task_state.py +23 -0
  37. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/tool_filters.py +2 -5
  38. voidx-2.2.0/src/voidx/config/__init__.py +47 -0
  39. voidx-2.2.0/src/voidx/config/cli.py +1 -0
  40. voidx-2.2.0/src/voidx/config/enums.py +60 -0
  41. voidx-2.2.0/src/voidx/config/models.py +145 -0
  42. voidx-2.2.0/src/voidx/config/permissions.py +24 -0
  43. voidx-2.2.0/src/voidx/config/settings.py +327 -0
  44. voidx-2.2.0/src/voidx/config/settings_agent.py +34 -0
  45. voidx-2.2.0/src/voidx/config/settings_api_keys.py +53 -0
  46. voidx-2.2.0/src/voidx/config/settings_code_ide.py +21 -0
  47. voidx-2.2.0/src/voidx/config/settings_custom.py +70 -0
  48. voidx-2.2.0/src/voidx/config/settings_mcp.py +65 -0
  49. voidx-2.2.0/src/voidx/config/settings_permissions.py +86 -0
  50. voidx-2.2.0/src/voidx/config/settings_skills.py +52 -0
  51. voidx-2.2.0/src/voidx/config/settings_utils.py +9 -0
  52. voidx-2.2.0/src/voidx/config/settings_web.py +42 -0
  53. voidx-2.2.0/src/voidx/data/__init__.py +1 -0
  54. voidx-2.2.0/src/voidx/data/intent_classifier.json +1 -0
  55. voidx-2.0.6/src/voidx/ui/output/diff.py → voidx-2.2.0/src/voidx/diffing.py +37 -141
  56. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/llm/catalog.py +10 -1
  57. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/llm/compaction.py +218 -26
  58. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/llm/instruction.py +91 -15
  59. voidx-2.2.0/src/voidx/llm/message_markers.py +14 -0
  60. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/llm/provider.py +8 -0
  61. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/llm/usage.py +35 -0
  62. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/detector.py +8 -125
  63. voidx-2.2.0/src/voidx/lsp/detector_data.py +130 -0
  64. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/manager.py +62 -2
  65. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/schema.py +1 -1
  66. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/main.py +9 -4
  67. voidx-2.2.0/src/voidx/mcp/client/__init__.py +11 -0
  68. voidx-2.2.0/src/voidx/mcp/client/base.py +352 -0
  69. voidx-2.2.0/src/voidx/mcp/client/errors.py +15 -0
  70. voidx-2.2.0/src/voidx/mcp/client/http_transport.py +120 -0
  71. voidx-2.2.0/src/voidx/mcp/client/sse_transport.py +151 -0
  72. voidx-2.2.0/src/voidx/mcp/client/stdio_transport.py +114 -0
  73. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/memory/context_frames.py +1 -1
  74. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/memory/runtime_state.py +21 -4
  75. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/memory/session.py +38 -3
  76. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/memory/store.py +2 -0
  77. voidx-2.2.0/src/voidx/permission/context.py +59 -0
  78. voidx-2.2.0/src/voidx/permission/engine.py +183 -0
  79. voidx-2.0.6/src/voidx/permission/engine.py → voidx-2.2.0/src/voidx/permission/rules.py +27 -222
  80. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/permission/service.py +17 -16
  81. voidx-2.2.0/src/voidx/runtime/__init__.py +39 -0
  82. voidx-2.2.0/src/voidx/runtime/intent.py +96 -0
  83. voidx-2.2.0/src/voidx/runtime/intent_classifier.py +265 -0
  84. {voidx-2.0.6/src/voidx/agent → voidx-2.2.0/src/voidx/runtime}/task_state.py +80 -23
  85. voidx-2.2.0/src/voidx/runtime/ui.py +249 -0
  86. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/skills/__init__.py +12 -1
  87. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/brainstorming/SKILL.md +1 -1
  88. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/writing-design-docs/SKILL.md +1 -1
  89. voidx-2.2.0/src/voidx/skills/context.py +168 -0
  90. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/skills/policy.py +21 -2
  91. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/skills/registry.py +49 -4
  92. voidx-2.2.0/src/voidx/skills/runtime.py +252 -0
  93. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/skills/service.py +39 -7
  94. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/agent.py +44 -15
  95. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/base.py +2 -0
  96. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/bash.py +1 -1
  97. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/clarify.py +12 -3
  98. voidx-2.2.0/src/voidx/tools/file_ops.py +618 -0
  99. voidx-2.2.0/src/voidx/tools/git.py +644 -0
  100. voidx-2.2.0/src/voidx/tools/load_skills.py +203 -0
  101. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/lsp.py +1 -1
  102. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/on_intent.py +2 -8
  103. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/plan_checkpoint.py +6 -3
  104. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/registry.py +28 -5
  105. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/repomap.py +3 -0
  106. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/search.py +4 -0
  107. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/task_tracker.py +1 -1
  108. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/todo.py +8 -1
  109. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/websearch.py +4 -1
  110. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/commands.py +13 -0
  111. voidx-2.2.0/src/voidx/ui/output/agent_display.py +25 -0
  112. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/browse.py +38 -24
  113. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/capture.py +18 -5
  114. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/console/formatting.py +3 -1
  115. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/console/streaming.py +42 -86
  116. voidx-2.2.0/src/voidx/ui/output/diff.py +130 -0
  117. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/dock/__init__.py +3 -0
  118. voidx-2.2.0/src/voidx/ui/output/dock/agent_placeholder.py +21 -0
  119. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/dock/app.py +56 -182
  120. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/dock/nodes.py +16 -159
  121. voidx-2.2.0/src/voidx/ui/output/dock/nodes_permission.py +50 -0
  122. voidx-2.2.0/src/voidx/ui/output/dock/nodes_startup.py +60 -0
  123. voidx-2.2.0/src/voidx/ui/output/dock/nodes_status.py +79 -0
  124. voidx-2.2.0/src/voidx/ui/output/dock/status.py +66 -0
  125. voidx-2.2.0/src/voidx/ui/output/dock/stream.py +156 -0
  126. voidx-2.2.0/src/voidx/ui/output/dock/todo.py +110 -0
  127. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/events/__init__.py +57 -12
  128. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/events/schema.py +34 -0
  129. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/tree.py +85 -3
  130. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/session.py +39 -2
  131. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tools/__init__.py +3 -0
  132. voidx-2.2.0/src/voidx/ui/tools/clipboard_text.py +72 -0
  133. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/transcript.py +1 -0
  134. voidx-2.2.0/src/voidx/ui/tui/activity.py +64 -0
  135. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tui/app.py +210 -247
  136. voidx-2.2.0/src/voidx/ui/tui/choice_mixin.py +48 -0
  137. voidx-2.2.0/src/voidx/ui/tui/clipboard_mixin.py +83 -0
  138. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tui/input.py +114 -2
  139. voidx-2.2.0/src/voidx/ui/tui/overlays.py +113 -0
  140. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tui/panels.py +5 -2
  141. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tui/parser.py +52 -11
  142. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tui/renderer.py +459 -146
  143. voidx-2.2.0/src/voidx/ui/tui/state.py +218 -0
  144. voidx-2.2.0/src/voidx/ui/tui/terminal_mixin.py +95 -0
  145. voidx-2.2.0/src/voidx/ui/tui/text_prompt_mixin.py +49 -0
  146. {voidx-2.0.6 → voidx-2.2.0}/src/voidx.egg-info/PKG-INFO +1 -1
  147. {voidx-2.0.6 → voidx-2.2.0}/src/voidx.egg-info/SOURCES.txt +80 -16
  148. voidx-2.2.0/tests/test_clipboard_text.py +22 -0
  149. {voidx-2.0.6 → voidx-2.2.0}/tests/test_compaction.py +270 -2
  150. {voidx-2.0.6 → voidx-2.2.0}/tests/test_config.py +141 -2
  151. voidx-2.2.0/tests/test_instruction_cache.py +88 -0
  152. voidx-2.2.0/tests/test_intent_classifier_phase_a.py +159 -0
  153. voidx-2.2.0/tests/test_llm_catalog.py +46 -0
  154. {voidx-2.0.6 → voidx-2.2.0}/tests/test_llm_usage.py +28 -0
  155. {voidx-2.0.6 → voidx-2.2.0}/tests/test_lsp.py +100 -1
  156. voidx-2.2.0/tests/test_main_startup.py +129 -0
  157. {voidx-2.0.6 → voidx-2.2.0}/tests/test_npm_package.py +8 -8
  158. voidx-2.2.0/tests/test_output_browse.py +23 -0
  159. {voidx-2.0.6 → voidx-2.2.0}/tests/test_pure_tui.py +1416 -28
  160. voidx-2.2.0/tests/test_runtime_intent_classifier.py +191 -0
  161. voidx-2.2.0/tests/test_runtime_ui.py +71 -0
  162. voidx-2.2.0/tests/test_skills.py +890 -0
  163. {voidx-2.0.6 → voidx-2.2.0}/tests/test_tree_smoke.py +71 -0
  164. {voidx-2.0.6 → voidx-2.2.0}/tests/test_ui_events.py +511 -8
  165. {voidx-2.0.6 → voidx-2.2.0}/tests/test_ui_gateway.py +96 -0
  166. {voidx-2.0.6 → voidx-2.2.0}/tests/test_ui_session_changes.py +85 -0
  167. voidx-2.0.6/src/voidx/agent/graph/run_loop.py +0 -673
  168. voidx-2.0.6/src/voidx/agent/message_rows.py +0 -36
  169. voidx-2.0.6/src/voidx/agent/runtime_context.py +0 -397
  170. voidx-2.0.6/src/voidx/config.py +0 -737
  171. voidx-2.0.6/src/voidx/mcp/client.py +0 -723
  172. voidx-2.0.6/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/api-doc.md +0 -64
  173. voidx-2.0.6/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/prd.md +0 -117
  174. voidx-2.0.6/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/readme.md +0 -55
  175. voidx-2.0.6/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/rfc.md +0 -38
  176. voidx-2.0.6/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/tech-design.md +0 -68
  177. voidx-2.0.6/src/voidx/skills/runtime.py +0 -90
  178. voidx-2.0.6/src/voidx/tools/doc_template.py +0 -96
  179. voidx-2.0.6/src/voidx/tools/file_ops.py +0 -208
  180. voidx-2.0.6/tests/test_skills.py +0 -410
  181. {voidx-2.0.6 → voidx-2.2.0}/README.md +0 -0
  182. {voidx-2.0.6 → voidx-2.2.0}/setup.cfg +0 -0
  183. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/__init__.py +0 -0
  184. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/attachments.py +0 -0
  185. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/graph/__init__.py +0 -0
  186. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/__init__.py +0 -0
  187. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/slash/skills.py +0 -0
  188. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/agent/tool_messages.py +0 -0
  189. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/llm/__init__.py +0 -0
  190. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/llm/context.py +0 -0
  191. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/__init__.py +0 -0
  192. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/client.py +0 -0
  193. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/config.py +0 -0
  194. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/errors.py +0 -0
  195. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/lsp/service.py +0 -0
  196. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/mcp/__init__.py +0 -0
  197. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/mcp/manager.py +0 -0
  198. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/mcp/schema.py +0 -0
  199. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/mcp/tool.py +0 -0
  200. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/mcp_servers/__init__.py +0 -0
  201. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/mcp_servers/web.py +0 -0
  202. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/memory/__init__.py +0 -0
  203. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/memory/model_profiles.py +0 -0
  204. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/memory/transcript.py +0 -0
  205. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/permission/__init__.py +0 -0
  206. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/permission/evaluate.py +0 -0
  207. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/permission/sandbox.py +0 -0
  208. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/permission/schema.py +0 -0
  209. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/permission/wildcard.py +0 -0
  210. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/receiving-code-review/SKILL.md +0 -0
  211. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/requesting-code-review/SKILL.md +0 -0
  212. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/systematic-debugging/SKILL.md +0 -0
  213. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/test-driven-development/SKILL.md +0 -0
  214. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/verification-before-completion/SKILL.md +0 -0
  215. {voidx-2.0.6/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/writing-plans/SKILL.md +0 -0
  216. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/skills/schema.py +0 -0
  217. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/__init__.py +0 -0
  218. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/task_status.py +0 -0
  219. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/web_content.py +0 -0
  220. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/web_mcp.py +0 -0
  221. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/tools/webfetch.py +0 -0
  222. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/__init__.py +0 -0
  223. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/frontend.py +0 -0
  224. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/gateway/__init__.py +0 -0
  225. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/gateway/bootstrap.py +0 -0
  226. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/gateway/server.py +0 -0
  227. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/gateway/session.py +0 -0
  228. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/__init__.py +0 -0
  229. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/console/__init__.py +0 -0
  230. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/console/app.py +0 -0
  231. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/dock/formatting.py +0 -0
  232. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/dock/state.py +0 -0
  233. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/output/types.py +0 -0
  234. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/protocol/__init__.py +0 -0
  235. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/protocol/commands.py +0 -0
  236. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/protocol/envelope.py +0 -0
  237. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/protocol/requests.py +0 -0
  238. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/protocol/schema.py +0 -0
  239. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/protocol/transcript.py +0 -0
  240. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tools/attachment_tokens.py +0 -0
  241. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tools/clipboard_image.py +0 -0
  242. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tools/code_ide.py +0 -0
  243. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tools/file_picker.py +0 -0
  244. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tui/__init__.py +0 -0
  245. {voidx-2.0.6 → voidx-2.2.0}/src/voidx/ui/tui/helpers.py +0 -0
  246. {voidx-2.0.6 → voidx-2.2.0}/src/voidx.egg-info/dependency_links.txt +0 -0
  247. {voidx-2.0.6 → voidx-2.2.0}/src/voidx.egg-info/entry_points.txt +0 -0
  248. {voidx-2.0.6 → voidx-2.2.0}/src/voidx.egg-info/requires.txt +0 -0
  249. {voidx-2.0.6 → voidx-2.2.0}/src/voidx.egg-info/top_level.txt +0 -0
  250. {voidx-2.0.6 → voidx-2.2.0}/tests/test_clipboard_image.py +0 -0
  251. {voidx-2.0.6 → voidx-2.2.0}/tests/test_code_ide.py +0 -0
  252. {voidx-2.0.6 → voidx-2.2.0}/tests/test_llm_provider.py +0 -0
  253. {voidx-2.0.6 → voidx-2.2.0}/tests/test_main.py +0 -0
  254. {voidx-2.0.6 → voidx-2.2.0}/tests/test_mcp.py +0 -0
  255. {voidx-2.0.6 → voidx-2.2.0}/tests/test_scrollback_flush.py +0 -0
  256. {voidx-2.0.6 → voidx-2.2.0}/tests/test_startup.py +0 -0
  257. {voidx-2.0.6 → voidx-2.2.0}/tests/test_ui_diff.py +0 -0
  258. {voidx-2.0.6 → voidx-2.2.0}/tests/test_ui_frontend_protocol.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voidx
3
- Version: 2.0.6
3
+ Version: 2.2.0
4
4
  Summary: A coding agent that quantifies everything and solves with tools, not fuzzy prompts.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "voidx"
3
- version = "2.0.6"
3
+ version = "2.2.0"
4
4
  description = "A coding agent that quantifies everything and solves with tools, not fuzzy prompts."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -44,8 +44,10 @@ where = ["src"]
44
44
 
45
45
  [tool.setuptools.package-data]
46
46
  "voidx.skills" = [
47
- "bundled/superpowers/*/SKILL.md",
48
- "bundled/superpowers/*/templates/*.md",
47
+ "bundled/*/SKILL.md",
48
+ ]
49
+ "voidx.data" = [
50
+ "intent_classifier.json",
49
51
  ]
50
52
 
51
53
  [tool.pytest.ini_options]
@@ -1,3 +1,3 @@
1
1
  """VoidX - A coding agent that quantifies everything."""
2
2
 
3
- __version__ = "2.0.6"
3
+ __version__ = "2.2.0"
@@ -47,20 +47,15 @@ BASE_SYSTEM_PROMPT = """You are voidx, a coding agent that lives in the terminal
47
47
  - Do not expose internal role names unless the user asks about architecture.
48
48
  - Never claim work is complete until it has been verified.
49
49
 
50
- ## Workflow Skills
51
-
52
- - voidx may activate workflow skills such as systematic-debugging,
53
- test-driven-development, verification-before-completion,
54
- receiving-code-review, requesting-code-review, and writing-plans.
55
- - The Current Task State lists active workflow skills for this turn.
56
- - The Active Skills section contains the full instructions for active skills.
57
- - Follow active workflow skills before acting.
58
50
 
59
- ## Parallel Execution
51
+ ## Workflow Skills
60
52
 
61
- - Multiple tool calls in one model response run in parallel.
62
- - Tool calls across separate model responses run sequentially.
63
- - Batch independent reads/searches together; keep dependent work sequential.
53
+ - voidx has a workflow skill system.
54
+ - Current Task State is the activation source for this turn's workflow skills.
55
+ - Skill Context messages contain bundled workflow skill bodies as a reference
56
+ library. Follow only skills listed as active in Current Task State, unless the
57
+ user explicitly references another skill.
58
+ - load_skills can return project/global skill bodies for the current turn.
64
59
  """
65
60
 
66
61
 
@@ -103,7 +98,7 @@ surgical edits directly when that is the shortest safe path.
103
98
  running write-capable commands, or delegating implement.
104
99
 
105
100
  4. **Code changes**
106
- - Small, local, or mechanical changes → read first, then call write/edit
101
+ - Small, local, or mechanical changes → read first, then call write/edit/apply_patch
107
102
  yourself and verify.
108
103
  - If investigation finds a concrete edit but the user asked only to inspect,
109
104
  design, or review, stop and report the proposed change. Ask for
@@ -127,12 +122,6 @@ surgical edits directly when that is the shortest safe path.
127
122
  approval or clarification, call plan_checkpoint or clarify yourself.
128
123
  - Don't tell the user "done" until changes are verified.
129
124
  - Child agents have isolated context — give them complete, self-contained briefs.
130
-
131
- ## Parallel Execution
132
- - Multiple tool calls in ONE response → they run in parallel.
133
- - Tool calls across SEPARATE responses → they run sequentially.
134
- - Batch independent work: read 3 files at once, search + fetch in the same step.
135
- - Sequential work: search first, then read based on what you found.
136
125
  """
137
126
 
138
127
  # Plan mode prompt — injected when plan_mode=True
@@ -199,7 +188,7 @@ You are the dedicated executor for broad or isolated implementation tasks.
199
188
 
200
189
  ## Rules
201
190
  - Read before writing. Never guess file contents.
202
- - Make minimal, precise edits. Use edit with exact old_string matches.
191
+ - Make minimal, precise edits. Use edit with exact old_string matches, or apply_patch for unified diffs and multi-file changes.
203
192
  - Follow the plan if one was provided.
204
193
  - Run tests/bash after changes to verify.
205
194
  - Return: what files were changed, what was done, any issues encountered.
@@ -260,6 +249,13 @@ class AgentDef(BaseModel):
260
249
  max_steps: int = 25
261
250
  hidden: bool = False # hidden from user-facing lists?
262
251
  model: str | None = None # None = inherit from parent
252
+ mcp_tools: bool = False # can see registered MCP tools
253
+
254
+ def with_max_steps(self, value: int) -> "AgentDef":
255
+ """Return a copy with max_steps overridden."""
256
+ if value == self.max_steps:
257
+ return self
258
+ return self.model_copy(update={"max_steps": value})
263
259
 
264
260
  @property
265
261
  def role_prompt(self) -> str:
@@ -282,6 +278,8 @@ class AgentDef(BaseModel):
282
278
  lines.append(f"- Available tools: {', '.join(self.tools)}")
283
279
  else:
284
280
  lines.append("- Available tools: none")
281
+ if self.mcp_tools:
282
+ lines.append("- MCP tools: available when configured; each call is permission-gated")
285
283
  if not self.can_write:
286
284
  lines.append("- Constraint: this role must not write or edit files.")
287
285
  if not self.can_delegate:
@@ -298,6 +296,39 @@ ROLE_PROMPTS = {
298
296
  PROMPTLESS_AGENTS = {"compaction", "title"}
299
297
 
300
298
 
299
+ def role_prompt_for_llm(agent: AgentDef, *, parallel_subagents_enabled: bool = False) -> str:
300
+ """Return the role prompt with runtime-gated child-agent scheduling rules."""
301
+
302
+ prompt = agent.role_prompt
303
+ if agent.name != "orchestrator":
304
+ return prompt
305
+ child_agent_prompt = _parallel_subagents_prompt(
306
+ enabled=parallel_subagents_enabled,
307
+ )
308
+ if not prompt:
309
+ return child_agent_prompt
310
+ return f"{prompt.rstrip()}\n\n{child_agent_prompt}"
311
+
312
+
313
+ def _parallel_subagents_prompt(*, enabled: bool) -> str:
314
+ if enabled:
315
+ return """## Child-Agent Scheduling
316
+
317
+ - For independent child-agent tasks, you may issue multiple `agent` tool calls
318
+ in one response. They will run concurrently up to the configured limit.
319
+ - Each child-agent brief must be complete and self-contained.
320
+ - Keep dependent child-agent work sequential: wait for the result before
321
+ delegating follow-up work that depends on it.
322
+ - Batch independent read/search tools when useful; keep dependent tool work
323
+ sequential."""
324
+ return """## Child-Agent Scheduling
325
+
326
+ - Delegate at most one child agent in a response. Wait for that result before
327
+ deciding whether another child agent is needed.
328
+ - Batch independent non-agent read/search tools when useful; keep dependent
329
+ work sequential."""
330
+
331
+
301
332
  # ── built-in agents ────────────────────────────────────────────────────────
302
333
 
303
334
  BUILTIN_AGENTS: dict[str, AgentDef] = {
@@ -308,15 +339,16 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
308
339
  when_to_use="Default agent for all user interactions. Always use first.",
309
340
  tools=[
310
341
  "on_intent", "clarify", "plan_checkpoint",
311
- "read", "glob", "grep", "bash", "agent", "task_status", "todo",
342
+ "read", "glob", "grep", "bash", "agent", "task_status", "todo", "load_skills",
312
343
  "webfetch", "websearch", "repo_map",
313
344
  "lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
314
- "write", "edit", "lsp_format",
345
+ "write", "edit", "apply_patch", "lsp_format",
315
346
  ],
316
347
  can_write=True,
317
348
  can_delegate=True,
318
- max_steps=20,
349
+ max_steps=100,
319
350
  hidden=False,
351
+ mcp_tools=True,
320
352
  ),
321
353
  "explore": AgentDef(
322
354
  name="explore",
@@ -326,12 +358,12 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
326
358
  "or answer 'how does X work' questions. Specify thoroughness: "
327
359
  "'quick' for basic, 'medium' for moderate, 'very thorough' for exhaustive.",
328
360
  tools=[
329
- "read", "glob", "grep", "webfetch", "websearch", "repo_map",
361
+ "read", "glob", "grep", "load_skills", "webfetch", "websearch", "repo_map",
330
362
  "lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
331
363
  ],
332
364
  can_write=False,
333
365
  can_delegate=False,
334
- max_steps=10,
366
+ max_steps=25,
335
367
  hidden=False,
336
368
 
337
369
  ),
@@ -342,12 +374,12 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
342
374
  when_to_use="Use for design/architecture questions, before complex implementations, "
343
375
  "or when the user asks for a plan/approach/solution design.",
344
376
  tools=[
345
- "read", "glob", "grep", "webfetch", "websearch", "repo_map",
377
+ "read", "glob", "grep", "load_skills", "webfetch", "websearch", "repo_map",
346
378
  "lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
347
379
  ],
348
380
  can_write=False,
349
381
  can_delegate=False,
350
- max_steps=15,
382
+ max_steps=30,
351
383
  hidden=False,
352
384
  ),
353
385
  "implement": AgentDef(
@@ -356,13 +388,13 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
356
388
  when_to_use="Use for all code writing, file editing, refactoring, bug fixing, "
357
389
  "and bash execution. Give complete, self-contained task descriptions.",
358
390
  tools=[
359
- "read", "write", "edit", "glob", "grep", "bash", "todo", "repo_map",
391
+ "read", "write", "edit", "apply_patch", "glob", "grep", "bash", "todo", "load_skills", "repo_map",
360
392
  "lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
361
393
  "lsp_format",
362
394
  ],
363
395
  can_write=True,
364
396
  can_delegate=False,
365
- max_steps=25,
397
+ max_steps=100,
366
398
  hidden=False,
367
399
  ),
368
400
  "review": AgentDef(
@@ -372,12 +404,12 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
372
404
  when_to_use="ALWAYS invoke after implement finishes non-trivial work. "
373
405
  "Use to verify correctness before reporting completion to the user.",
374
406
  tools=[
375
- "read", "glob", "grep", "bash", "webfetch", "websearch", "repo_map",
407
+ "read", "glob", "grep", "bash", "load_skills", "webfetch", "websearch", "repo_map",
376
408
  "lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
377
409
  ],
378
410
  can_write=False,
379
411
  can_delegate=False,
380
- max_steps=10,
412
+ max_steps=30,
381
413
  hidden=False,
382
414
  ),
383
415
  # ── hidden agents (not user-visible, internal only) ───────────────
@@ -435,6 +467,8 @@ def child_agent_descriptions_for_llm() -> str:
435
467
  lines = ["Available child agents and the tools they have access to:"]
436
468
  for agent in get_subagents():
437
469
  tools_str = ", ".join(agent.tools)
470
+ if agent.mcp_tools:
471
+ tools_str = f"{tools_str}, MCP tools" if tools_str else "MCP tools"
438
472
  lines.append(
439
473
  f"- {agent.name}: {agent.description}\n"
440
474
  f" Tools: {tools_str}\n"
@@ -2,9 +2,10 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
5
6
  from typing import TYPE_CHECKING
6
7
 
7
- from langchain_core.messages import AIMessage, SystemMessage
8
+ from langchain_core.messages import HumanMessage, SystemMessage
8
9
 
9
10
  from voidx.agent.message_rows import messages_from_rows
10
11
  from voidx.agent.graph.runtime import console, ui
@@ -13,14 +14,22 @@ from voidx.llm.compaction import COMPACTION_MAX_RETRIES, CompactionService
13
14
  from voidx.llm.provider import resolve_protocol
14
15
  from voidx.llm.usage import estimate_context_tokens, estimate_message_tokens, extract_token_usage
15
16
  from voidx.memory.context_frames import save_context_frame_from_messages
16
- from voidx.ui.output.console import StreamingRenderer
17
- from voidx.ui.output.dock import dock
18
- from voidx.ui.output.events import StatusFinished, StatusUpdated, ui_events, via_events
17
+ from voidx.runtime.ui import (
18
+ StatusFinished,
19
+ StatusUpdated,
20
+ StreamingRenderer,
21
+ dock,
22
+ ui_events,
23
+ via_events,
24
+ )
19
25
 
20
26
  if TYPE_CHECKING:
21
27
  from voidx.agent.graph.contracts import GraphCompactionHost
22
28
 
23
29
 
30
+ logger = logging.getLogger(__name__)
31
+
32
+
24
33
  class GraphCompactionMixin:
25
34
  async def _maybe_compact(
26
35
  self: GraphCompactionHost,
@@ -33,8 +42,7 @@ class GraphCompactionMixin:
33
42
  """Check overflow and compact if needed.
34
43
 
35
44
  Returns the messages removed from the live context and the persisted
36
- tail anchor id when one is available. Fallback truncation has no stable
37
- tail anchor, so it returns None for the second value.
45
+ tail anchor id when compaction removes an older complete turn.
38
46
  """
39
47
  total_tokens = estimate_context_tokens(messages, self.config.model.model)
40
48
  tokens = {"total": total_tokens, "input": total_tokens, "output": 0, "reasoning": 0}
@@ -73,40 +81,23 @@ class GraphCompactionMixin:
73
81
  else "[yellow]Compacting context...[/yellow]"
74
82
  )
75
83
 
76
- head_msgs, tail_id = self._compaction.select(messages)
77
-
78
- if not head_msgs or not tail_id:
79
- # Hard fallback: keep only last 6 messages
80
- keep = min(6, len(messages))
81
- if via_events():
82
- await ui_events.emit(StatusUpdated(
83
- status_id="compaction",
84
- label="Compacting context",
85
- detail=f"fallback truncation, keeping last {keep} messages",
86
- stage="compacting",
87
- ))
88
- else:
89
- ui.print(f"[dim]Aggressive truncation: keeping last {keep} messages[/dim]")
90
-
91
- removed = _truncate_to_recent_messages(messages, keep)
92
- # Generate a basic summary from the removed messages
93
- fallback = CompactionService.fallback_summary(removed)
94
- self._pending_summary = fallback
95
- self._compaction_summary = fallback
96
- self._compaction.compaction_count += 1
84
+ selection = self._compaction.select_details(messages)
85
+ head_msgs, tail_id = selection.head, selection.tail_id
97
86
 
87
+ if not selection.should_compact:
98
88
  if via_events():
99
89
  await ui_events.emit(StatusFinished(
100
90
  status_id="compaction",
101
- label=f"Compaction fallback kept last {keep} messages (with extracted summary)",
91
+ label="Compaction skipped: no older complete turn to summarize",
102
92
  remove=False,
103
93
  ))
104
- return removed, None
94
+ return None, None
105
95
 
106
96
  # Run compaction agent with retries
107
97
  summary = None
108
98
  previous_summary = getattr(self, "_compaction_summary", "") or None
109
99
  last_error: Exception | None = None
100
+ returned_no_summary = False
110
101
 
111
102
  for attempt in range(1, COMPACTION_MAX_RETRIES + 2): # 1 initial + N retries
112
103
  try:
@@ -121,8 +112,11 @@ class GraphCompactionMixin:
121
112
  summary = await self._run_compaction_agent(head_msgs, previous_summary)
122
113
  if summary:
123
114
  break
115
+ returned_no_summary = True
116
+ last_error = None
124
117
  except Exception as e:
125
118
  last_error = e
119
+ returned_no_summary = False
126
120
  if attempt <= COMPACTION_MAX_RETRIES:
127
121
  if via_events():
128
122
  await ui_events.emit(StatusUpdated(
@@ -135,37 +129,43 @@ class GraphCompactionMixin:
135
129
  ui.print(f"[dim]Compaction agent failed ({e}) — retrying ({attempt}/{COMPACTION_MAX_RETRIES})[/dim]")
136
130
 
137
131
  if not summary:
138
- # All retries exhausted — fallback truncation with basic summary
132
+ # All retries exhausted — use an extracted summary, but keep the selected tail.
133
+ if last_error:
134
+ failure_detail = f"{type(last_error).__name__}: {last_error}"
135
+ elif returned_no_summary:
136
+ failure_detail = "compaction agent returned no summary"
137
+ else:
138
+ failure_detail = "compaction agent did not produce a summary"
139
139
  if via_events():
140
- err_detail = f"{last_error}; " if last_error else ""
141
140
  await ui_events.emit(StatusUpdated(
142
141
  status_id="compaction",
143
142
  label="Compaction agent failed",
144
- detail=f"{err_detail}falling back to truncation with extracted summary",
143
+ detail=f"{failure_detail}; using extracted summary",
145
144
  stage="compacting",
146
145
  ))
147
146
  else:
148
- err_msg = f" ({last_error})" if last_error else ""
149
- ui.print(f"[dim]Compaction agent failed{err_msg} — aggressive truncation with summary[/dim]")
150
- keep = min(6, len(messages))
151
- removed = _truncate_to_recent_messages(messages, keep)
152
- # Generate a basic summary from the removed messages
153
- fallback = CompactionService.fallback_summary(head_msgs if head_msgs else removed)
147
+ err_msg = f" ({failure_detail})"
148
+ ui.print(f"[dim]Compaction agent failed{err_msg} — using extracted summary[/dim]")
149
+ fallback = CompactionService.fallback_summary(head_msgs)
154
150
  self._pending_summary = fallback
155
151
  self._compaction_summary = fallback
156
152
  self._compaction.compaction_count += 1
153
+ tail_msgs = messages[selection.keep_from:]
154
+ messages.clear()
155
+ messages.extend(tail_msgs)
156
+ await self._persist_compaction(head_msgs)
157
157
  if via_events():
158
158
  await ui_events.emit(StatusFinished(
159
159
  status_id="compaction",
160
- label=f"Compaction fallback kept last {keep} messages (with extracted summary)",
160
+ label=f"Compaction fallback summarized {len(head_msgs)} messages",
161
+ detail=f"{failure_detail}; using extracted summary",
161
162
  ok=False,
162
163
  remove=False,
163
164
  ))
164
- return removed, None
165
+ return head_msgs, tail_id
165
166
 
166
167
  if summary:
167
- keep_from = len(head_msgs)
168
- tail_msgs = messages[keep_from:]
168
+ tail_msgs = messages[selection.keep_from:]
169
169
  messages.clear()
170
170
  messages.extend(tail_msgs)
171
171
  self._pending_summary = summary
@@ -222,6 +222,13 @@ class GraphCompactionMixin:
222
222
  cache = getattr(self, "_session_msg_cache", None)
223
223
  if cache is not None:
224
224
  self._session_msg_cache = [r for r in cache if r.id is not None and r.id > last_message_id]
225
+ context_cache = getattr(self, "_context_cache", None)
226
+ if context_cache is not None:
227
+ context_cache.row_messages = {
228
+ row_id: entry
229
+ for row_id, entry in context_cache.row_messages.items()
230
+ if row_id > last_message_id
231
+ }
225
232
 
226
233
  async def _compact_session_history(self: GraphCompactionHost, *, force: bool = True) -> bool:
227
234
  if getattr(self, "_session", None) is None:
@@ -252,7 +259,12 @@ class GraphCompactionMixin:
252
259
  return None
253
260
 
254
261
  prompt = self._compaction.build_prompt(head_messages, previous_summary)
255
- renderer = StreamingRenderer(console, debug=self._debug, stream_to_dock=False)
262
+ renderer = StreamingRenderer(
263
+ console,
264
+ debug=self._debug,
265
+ stream_to_dock=False,
266
+ headless=True,
267
+ )
256
268
 
257
269
  messages = [SystemMessage(content=COMPACTION_PROMPT)]
258
270
  messages.append(HumanMessage(content=prompt))
@@ -284,7 +296,20 @@ class GraphCompactionMixin:
284
296
  cache_key=f"{self.config.model.provider}/{self.config.model.model}",
285
297
  )
286
298
  text = extract_text(assistant_msg)
287
- return text if text else None
299
+ if text:
300
+ return text
301
+ logger.warning(
302
+ "Compaction agent returned empty text: message_type=%s content_type=%s",
303
+ type(assistant_msg).__name__,
304
+ _content_type_summary(getattr(assistant_msg, "content", None)),
305
+ )
306
+ return None
307
+
308
+
309
+ def _content_type_summary(content: object) -> str:
310
+ if isinstance(content, list):
311
+ return ",".join(type(item).__name__ for item in content) or "list(empty)"
312
+ return type(content).__name__
288
313
 
289
314
 
290
315
  def _max_persisted_message_id(messages: list) -> int | None:
@@ -296,16 +321,3 @@ def _max_persisted_message_id(messages: list) -> int | None:
296
321
  except (TypeError, ValueError):
297
322
  continue
298
323
  return max(ids) if ids else None
299
-
300
-
301
- def _truncate_to_recent_messages(messages: list, keep: int) -> list:
302
- original = list(messages)
303
- system_msgs = [m for m in original if isinstance(m, SystemMessage)]
304
- other_msgs = [m for m in original if not isinstance(m, SystemMessage)]
305
- tail_msgs = other_msgs[-keep:] if keep > 0 else []
306
- retained_ids = {id(m) for m in [*system_msgs, *tail_msgs]}
307
- removed = [m for m in original if id(m) not in retained_ids]
308
- messages.clear()
309
- messages.extend(system_msgs)
310
- messages.extend(tail_msgs)
311
- return removed
@@ -7,11 +7,12 @@ changing the runtime inheritance model.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- from typing import TYPE_CHECKING, Any, Protocol
10
+ import asyncio
11
+ from typing import Any, Protocol
11
12
 
12
13
  from langchain_core.messages import BaseMessage
13
14
 
14
- from voidx.agent.runtime_context import InteractionMode
15
+ from voidx.agent.runtime_context import ContextCompilerCache, InteractionMode
15
16
  from voidx.agent.state import AgentState
16
17
  from voidx.agent.task_state import TaskRun, TaskState
17
18
  from voidx.config import Config, Settings
@@ -22,10 +23,6 @@ from voidx.memory.session import SessionInfo
22
23
  from voidx.permission.service import PermissionService
23
24
  from voidx.tools.registry import ToolRegistry
24
25
  from voidx.tools.task_tracker import TaskTracker
25
- from voidx.ui.output.tree import OutputNode, OutputTree
26
-
27
- if TYPE_CHECKING:
28
- from voidx.ui.tui import PureTui
29
26
 
30
27
 
31
28
  class GraphComponentHost(Protocol):
@@ -47,18 +44,22 @@ class GraphComponentHost(Protocol):
47
44
  _interaction_mode: InteractionMode
48
45
  _debug: bool
49
46
  _file_mtimes: dict[str, float]
50
- _turn_node: OutputNode | None
51
- _current_tree: OutputTree | None
47
+ _turn_node: Any | None
48
+ _current_tree: Any | None
52
49
  _current_messages: list[BaseMessage] | None
53
50
  _sub_buffers: dict[str, list[BaseMessage]]
54
51
  _pending_summary: str | None
55
52
  _compaction_summary: str
56
53
  _session_msg_cache: list[Any] | None
57
- _app: PureTui | None
54
+ _context_cache: ContextCompilerCache
55
+ _app: Any | None
58
56
  _usage_stats: UsageStats
59
57
  _compaction: CompactionService
60
58
  _task_state: TaskState
61
59
  _task_run: TaskRun
60
+ _clear_session_tasks: set[asyncio.Task[None]]
61
+ _title_generation: int
62
+ _title_task: asyncio.Task[None] | None
62
63
 
63
64
  _mcp_manager: Any
64
65
  _lsp_manager: Any
@@ -70,15 +71,31 @@ class GraphComponentHost(Protocol):
70
71
  @property
71
72
  def _plan_mode(self) -> bool: ...
72
73
 
73
- async def _show_startup(self, *, append_transcript: bool = False) -> None: ...
74
+ async def _show_startup(
75
+ self,
76
+ *,
77
+ append_transcript: bool = False,
78
+ prefer_direct: bool = False,
79
+ ) -> None: ...
74
80
  def _startup_title(self) -> str: ...
75
81
  async def _handle_web_command(self, app: Any, command: Any) -> None: ...
76
82
  async def _handle_user_input(self, app: Any, user_input: str) -> tuple[bool, str | None]: ...
77
- async def _run_once(self, user_text: str) -> None: ...
83
+ async def _run_once(self, user_text: str, *, display_text: str | None = None) -> None: ...
78
84
  async def _dispatch_slash(self, inp: str) -> bool: ...
79
85
  async def _restore_runtime_state(self) -> None: ...
80
86
  async def _persist_runtime_state(self) -> None: ...
81
87
  async def _clear_runtime_state(self) -> None: ...
88
+ def _reset_runtime_state_memory(self) -> None: ...
89
+ def _invalidate_session_title_generation(self) -> None: ...
90
+ def _temporary_session_title(self, text: str) -> str: ...
91
+ def _schedule_session_title_generation(
92
+ self,
93
+ session_id: str,
94
+ first_user_text: str,
95
+ temporary_title: str,
96
+ ) -> None: ...
97
+ async def regenerate_session_title(self) -> bool: ...
98
+ async def _delete_empty_current_session(self) -> None: ...
82
99
  async def _persist_transcript_snapshot(self) -> None: ...
83
100
  async def _restore_transcript_snapshot(self, *, append: bool = False) -> bool: ...
84
101