voidx 3.1.1__tar.gz → 3.1.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 (285) hide show
  1. {voidx-3.1.1 → voidx-3.1.2}/PKG-INFO +1 -1
  2. {voidx-3.1.1 → voidx-3.1.2}/pyproject.toml +1 -1
  3. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/__init__.py +1 -1
  4. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/core/llm.py +1 -0
  5. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/runtime_guards.py +1 -1
  6. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/streaming.py +1 -1
  7. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/tool_exchange_sanitizer.py +23 -13
  8. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/diffing.py +96 -0
  9. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/compaction.py +5 -3
  10. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/engine.py +2 -2
  11. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/evaluate.py +1 -1
  12. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/rules.py +4 -4
  13. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/bash_router.py +11 -11
  14. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/file_ops/__init__.py +4 -4
  15. voidx-3.1.2/src/voidx/tools/file_ops/edit_execute.py +279 -0
  16. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/file_ops/edit_resolve.py +0 -96
  17. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/file_ops/file.py +2 -2
  18. voidx-3.1.2/src/voidx/tools/file_ops/types.py +35 -0
  19. voidx-3.1.2/src/voidx/tools/file_ops/write.py +140 -0
  20. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/registry.py +2 -2
  21. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/console/app.py +1 -1
  22. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/console/formatting.py +1 -1
  23. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/display_policy.py +1 -1
  24. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/__init__.py +7 -1
  25. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/app.py +6 -1
  26. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/nodes.py +2 -2
  27. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/nodes_status.py +2 -2
  28. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/status.py +11 -1
  29. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/events/consumers.py +7 -0
  30. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/session.py +1 -1
  31. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/render_activity.py +4 -1
  32. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/nodes.py +7 -7
  33. {voidx-3.1.1 → voidx-3.1.2}/src/voidx.egg-info/PKG-INFO +1 -1
  34. {voidx-3.1.1 → voidx-3.1.2}/src/voidx.egg-info/SOURCES.txt +0 -1
  35. voidx-3.1.1/src/voidx/tools/file_ops/edit_execute.py +0 -559
  36. voidx-3.1.1/src/voidx/tools/file_ops/line.py +0 -150
  37. voidx-3.1.1/src/voidx/tools/file_ops/types.py +0 -79
  38. voidx-3.1.1/src/voidx/tools/file_ops/write.py +0 -89
  39. {voidx-3.1.1 → voidx-3.1.2}/README.md +0 -0
  40. {voidx-3.1.1 → voidx-3.1.2}/setup.cfg +0 -0
  41. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/__init__.py +0 -0
  42. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/agents.py +0 -0
  43. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/attachments.py +0 -0
  44. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/goal_resolver.py +0 -0
  45. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/__init__.py +0 -0
  46. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/compaction.py +0 -0
  47. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/compaction_coordinator.py +0 -0
  48. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/contracts.py +0 -0
  49. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/convergence.py +0 -0
  50. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/core/__init__.py +0 -0
  51. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/core/_voidx_graph.py +0 -0
  52. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/core/helpers.py +0 -0
  53. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/permissions.py +0 -0
  54. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/run_loop.py +0 -0
  55. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/runtime.py +0 -0
  56. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/session_mixin.py +0 -0
  57. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/session_runtime.py +0 -0
  58. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/subagent.py +0 -0
  59. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/title_mixin.py +0 -0
  60. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/todo_events.py +0 -0
  61. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_execution.py +0 -0
  62. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_executor/__init__.py +0 -0
  63. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_executor/executor.py +0 -0
  64. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_executor/guards.py +0 -0
  65. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_executor/helpers.py +0 -0
  66. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_executor/types.py +0 -0
  67. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_executor/ui.py +0 -0
  68. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/tool_executor/workflow.py +0 -0
  69. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/topology.py +0 -0
  70. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/transcript_mixin.py +0 -0
  71. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/turn_mixin.py +0 -0
  72. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/turn_runner.py +0 -0
  73. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/wiring.py +0 -0
  74. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/graph/workflow_utils.py +0 -0
  75. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/message_rows.py +0 -0
  76. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/prompts.py +0 -0
  77. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/runtime_context.py +0 -0
  78. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/__init__.py +0 -0
  79. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/code_ide.py +0 -0
  80. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/guide.py +0 -0
  81. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/handler.py +0 -0
  82. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/host.py +0 -0
  83. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/init.py +0 -0
  84. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/lsp.py +0 -0
  85. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/mcp.py +0 -0
  86. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/model.py +0 -0
  87. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/profile.py +0 -0
  88. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/runtime.py +0 -0
  89. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/session.py +0 -0
  90. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/skills.py +0 -0
  91. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/slash/upgrade.py +0 -0
  92. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/state.py +0 -0
  93. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/task_state.py +0 -0
  94. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/todo_state.py +0 -0
  95. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/tool_call_ids.py +0 -0
  96. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/tool_filters.py +0 -0
  97. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/tool_messages.py +0 -0
  98. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/agent/tool_result_storage.py +0 -0
  99. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/__init__.py +0 -0
  100. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/enums.py +0 -0
  101. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/models.py +0 -0
  102. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/permissions.py +0 -0
  103. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings.py +0 -0
  104. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_agent.py +0 -0
  105. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_api_keys.py +0 -0
  106. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_code_ide.py +0 -0
  107. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_custom.py +0 -0
  108. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_mcp.py +0 -0
  109. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_permissions.py +0 -0
  110. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_skills.py +0 -0
  111. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_update.py +0 -0
  112. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_utils.py +0 -0
  113. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/config/settings_web.py +0 -0
  114. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/data/__init__.py +0 -0
  115. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/data/templates/api-doc.md +0 -0
  116. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/data/templates/prd.md +0 -0
  117. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/data/templates/readme.md +0 -0
  118. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/data/templates/rfc.md +0 -0
  119. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/data/templates/tech-design.md +0 -0
  120. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/__init__.py +0 -0
  121. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/catalog.py +0 -0
  122. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/context.py +0 -0
  123. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/instruction.py +0 -0
  124. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/message_markers.py +0 -0
  125. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/message_status.py +0 -0
  126. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/provider.py +0 -0
  127. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/service.py +0 -0
  128. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/llm/usage.py +0 -0
  129. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/logging/__init__.py +0 -0
  130. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/logging/request_log.py +0 -0
  131. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/logging/tool_log.py +0 -0
  132. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/__init__.py +0 -0
  133. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/client.py +0 -0
  134. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/config.py +0 -0
  135. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/detector.py +0 -0
  136. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/detector_data.py +0 -0
  137. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/errors.py +0 -0
  138. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/manager.py +0 -0
  139. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/schema.py +0 -0
  140. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/lsp/service.py +0 -0
  141. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/main.py +0 -0
  142. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/__init__.py +0 -0
  143. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/client/__init__.py +0 -0
  144. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/client/base.py +0 -0
  145. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/client/errors.py +0 -0
  146. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/client/http_transport.py +0 -0
  147. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/client/sse_transport.py +0 -0
  148. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/client/stdio_transport.py +0 -0
  149. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/manager.py +0 -0
  150. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/schema.py +0 -0
  151. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp/tool.py +0 -0
  152. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp_servers/__init__.py +0 -0
  153. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/mcp_servers/web.py +0 -0
  154. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/__init__.py +0 -0
  155. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/cleanup.py +0 -0
  156. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/context_frames.py +0 -0
  157. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/jsonl_store.py +0 -0
  158. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/model_profiles.py +0 -0
  159. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/runtime_state.py +0 -0
  160. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/service.py +0 -0
  161. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/session.py +0 -0
  162. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/store.py +0 -0
  163. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/subagents.py +0 -0
  164. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/memory/transcript.py +0 -0
  165. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/__init__.py +0 -0
  166. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/context.py +0 -0
  167. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/sandbox.py +0 -0
  168. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/schema.py +0 -0
  169. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/service.py +0 -0
  170. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/permission/wildcard.py +0 -0
  171. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/__init__.py +0 -0
  172. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/attachments.py +0 -0
  173. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/intent.py +0 -0
  174. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/reference_tokens.py +0 -0
  175. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/task_state.py +0 -0
  176. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/todo.py +0 -0
  177. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/ui.py +0 -0
  178. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/runtime/ui_port.py +0 -0
  179. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/selfupdate.py +0 -0
  180. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/skills/__init__.py +0 -0
  181. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/skills/context.py +0 -0
  182. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/skills/references.py +0 -0
  183. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/skills/registry.py +0 -0
  184. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/skills/schema.py +0 -0
  185. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/skills/service.py +0 -0
  186. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/__init__.py +0 -0
  187. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/agent.py +0 -0
  188. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/base.py +0 -0
  189. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/bash.py +0 -0
  190. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/clarify.py +0 -0
  191. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/compact_context.py +0 -0
  192. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/file_ops/read.py +0 -0
  193. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/file_state.py +0 -0
  194. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/git.py +0 -0
  195. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/load_doc_template.py +0 -0
  196. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/load_skills.py +0 -0
  197. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/lsp.py +0 -0
  198. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/plan_checkpoint.py +0 -0
  199. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/repomap.py +0 -0
  200. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/search.py +0 -0
  201. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/service.py +0 -0
  202. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/task_status.py +0 -0
  203. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/task_tracker.py +0 -0
  204. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/todo.py +0 -0
  205. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/web_content.py +0 -0
  206. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/web_mcp.py +0 -0
  207. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/webfetch.py +0 -0
  208. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/websearch.py +0 -0
  209. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/tools/workflow.py +0 -0
  210. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/__init__.py +0 -0
  211. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/commands.py +0 -0
  212. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/frontend.py +0 -0
  213. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/gateway/__init__.py +0 -0
  214. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/gateway/bootstrap.py +0 -0
  215. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/gateway/server.py +0 -0
  216. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/gateway/session.py +0 -0
  217. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/__init__.py +0 -0
  218. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/agent_display.py +0 -0
  219. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/browse.py +0 -0
  220. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/capture.py +0 -0
  221. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/console/__init__.py +0 -0
  222. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/console/streaming.py +0 -0
  223. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/diff.py +0 -0
  224. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/agent_placeholder.py +0 -0
  225. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/formatting.py +0 -0
  226. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/nodes_permission.py +0 -0
  227. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/nodes_startup.py +0 -0
  228. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/state.py +0 -0
  229. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/stream.py +0 -0
  230. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/dock/todo.py +0 -0
  231. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/events/__init__.py +0 -0
  232. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/events/bus.py +0 -0
  233. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/events/schema.py +0 -0
  234. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/tree.py +0 -0
  235. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/output/types.py +0 -0
  236. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/protocol/__init__.py +0 -0
  237. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/protocol/commands.py +0 -0
  238. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/protocol/envelope.py +0 -0
  239. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/protocol/requests.py +0 -0
  240. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/protocol/schema.py +0 -0
  241. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/protocol/transcript.py +0 -0
  242. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tools/__init__.py +0 -0
  243. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tools/attachment_tokens.py +0 -0
  244. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tools/clipboard_image.py +0 -0
  245. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tools/clipboard_text.py +0 -0
  246. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tools/code_ide.py +0 -0
  247. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tools/file_picker.py +0 -0
  248. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tools/skill_picker.py +0 -0
  249. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/transcript.py +0 -0
  250. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/__init__.py +0 -0
  251. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/activity.py +0 -0
  252. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/app.py +0 -0
  253. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/choice_mixin.py +0 -0
  254. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/clipboard_mixin.py +0 -0
  255. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/helpers.py +0 -0
  256. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/input.py +0 -0
  257. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/overlays.py +0 -0
  258. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/panels.py +0 -0
  259. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/parser.py +0 -0
  260. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/render_frame.py +0 -0
  261. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/render_input.py +0 -0
  262. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/render_status.py +0 -0
  263. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/render_todo.py +0 -0
  264. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/renderer.py +0 -0
  265. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/state.py +0 -0
  266. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/terminal_mixin.py +0 -0
  267. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/ui/tui/text_prompt_mixin.py +0 -0
  268. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/__init__.py +0 -0
  269. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/auto_advance.py +0 -0
  270. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/context.py +0 -0
  271. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/dag.py +0 -0
  272. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/policy.py +0 -0
  273. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/reconcile.py +0 -0
  274. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/render.py +0 -0
  275. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/route.py +0 -0
  276. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/runtime.py +0 -0
  277. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/schema.py +0 -0
  278. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/service.py +0 -0
  279. {voidx-3.1.1 → voidx-3.1.2}/src/voidx/workflow/types.py +0 -0
  280. {voidx-3.1.1 → voidx-3.1.2}/src/voidx.egg-info/dependency_links.txt +0 -0
  281. {voidx-3.1.1 → voidx-3.1.2}/src/voidx.egg-info/entry_points.txt +0 -0
  282. {voidx-3.1.1 → voidx-3.1.2}/src/voidx.egg-info/requires.txt +0 -0
  283. {voidx-3.1.1 → voidx-3.1.2}/src/voidx.egg-info/top_level.txt +0 -0
  284. {voidx-3.1.1 → voidx-3.1.2}/tests/test_install_sh.py +0 -0
  285. {voidx-3.1.1 → voidx-3.1.2}/tests/test_npm_package.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voidx
3
- Version: 3.1.1
3
+ Version: 3.1.2
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 = "3.1.1"
3
+ version = "3.1.2"
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"
@@ -1,3 +1,3 @@
1
1
  """VoidX - A coding agent that quantifies everything."""
2
2
 
3
- __version__ = "3.1.1"
3
+ __version__ = "3.1.2"
@@ -184,6 +184,7 @@ class GraphLlmMixin:
184
184
  state_messages = sanitize_failed_tool_exchanges(
185
185
  state_messages,
186
186
  preserve_latest=True,
187
+ preserve_rounds=2,
187
188
  )
188
189
  compaction_happened = False
189
190
  raw_todo_state = (
@@ -284,7 +284,7 @@ def tool_call_key(tool_call: dict[str, Any]) -> str:
284
284
 
285
285
 
286
286
  def normalize_tool_args(tool_name: str, args: dict[str, Any]) -> str:
287
- if tool_name in {"read", "file", "line", "replace", "lsp_format"}:
287
+ if tool_name in {"read", "file", "write", "replace", "lsp_format"}:
288
288
  return str(args.get("file_path") or "")
289
289
  if tool_name == "lsp":
290
290
  return stable_json({
@@ -138,7 +138,7 @@ def _sanitize_messages_for_replay(messages: list, *, protocol: str = "") -> list
138
138
  sanitized,
139
139
  preserve_latest_tool_exchange=True,
140
140
  )
141
- sanitized = sanitize_failed_tool_exchanges(sanitized, preserve_latest=True)
141
+ sanitized = sanitize_failed_tool_exchanges(sanitized, preserve_latest=True, preserve_rounds=2)
142
142
  return _repair_tool_result_adjacency(sanitized)
143
143
 
144
144
 
@@ -14,10 +14,11 @@ def sanitize_failed_tool_exchanges(
14
14
  messages: list[BaseMessage],
15
15
  *,
16
16
  preserve_latest: bool = False,
17
+ preserve_rounds: int = 1,
17
18
  ) -> list[BaseMessage]:
18
19
  failed_ids = _failed_tool_call_ids(messages)
19
20
  if preserve_latest:
20
- failed_ids.difference_update(_latest_failed_tool_exchange_ids(messages))
21
+ failed_ids.difference_update(_latest_failed_tool_exchange_ids(messages, rounds=preserve_rounds))
21
22
  if not failed_ids:
22
23
  return messages
23
24
 
@@ -52,21 +53,30 @@ def _failed_tool_call_ids(messages: list[BaseMessage]) -> set[str]:
52
53
  return failed
53
54
 
54
55
 
55
- def _latest_failed_tool_exchange_ids(messages: list[BaseMessage]) -> set[str]:
56
- trailing_failed_ids: set[str] = set()
56
+ def _latest_failed_tool_exchange_ids(messages: list[BaseMessage], *, rounds: int = 1) -> set[str]:
57
+ preserved: set[str] = set()
57
58
  index = len(messages) - 1
58
- while index >= 0 and isinstance(messages[index], ToolMessage):
59
- message = messages[index]
60
- tool_call_id = str(getattr(message, "tool_call_id", "") or "")
61
- if tool_call_id and message_status(getattr(message, "status", None)) == "error":
62
- trailing_failed_ids.add(tool_call_id)
63
- index -= 1
59
+ rounds_found = 0
60
+
61
+ while index >= 0 and rounds_found < rounds:
62
+ # Collect trailing ToolMessages at current position
63
+ round_failed: set[str] = set()
64
+ while index >= 0 and isinstance(messages[index], ToolMessage):
65
+ message = messages[index]
66
+ tool_call_id = str(getattr(message, "tool_call_id", "") or "")
67
+ if tool_call_id and message_status(getattr(message, "status", None)) == "error":
68
+ round_failed.add(tool_call_id)
69
+ index -= 1
70
+
71
+ if not round_failed or index < 0 or not isinstance(messages[index], AIMessage):
72
+ break
64
73
 
65
- if not trailing_failed_ids or index < 0 or not isinstance(messages[index], AIMessage):
66
- return set()
74
+ ai_ids = set(ai_tool_call_ids(messages[index]))
75
+ preserved.update(round_failed.intersection(ai_ids))
76
+ rounds_found += 1
77
+ index -= 1
67
78
 
68
- ai_ids = set(ai_tool_call_ids(messages[index]))
69
- return trailing_failed_ids.intersection(ai_ids)
79
+ return preserved
70
80
 
71
81
 
72
82
  def _sanitize_ai_failed_calls(message: AIMessage, failed_ids: set[str]) -> AIMessage | None:
@@ -159,6 +159,102 @@ def make_file_diff(
159
159
  return "".join(diff)
160
160
 
161
161
 
162
+ def make_structured_diff(
163
+ filepath: str,
164
+ old_content: str,
165
+ new_content: str,
166
+ ) -> FileDiff:
167
+ """Generate a structured FileDiff directly, without a text round-trip.
168
+
169
+ Uses SequenceMatcher.get_grouped_opcodes(n=3) to match unified_diff's
170
+ hunk-grouping behavior. Handles the old_start=0 / new_start=0 convention
171
+ for pure-insert / pure-delete hunks.
172
+ """
173
+ old = old_content.splitlines(keepends=True)
174
+ new = new_content.splitlines(keepends=True)
175
+ sm = difflib.SequenceMatcher(a=old, b=new, autojunk=False)
176
+
177
+ file_diff = FileDiff(
178
+ old_path=f"a/{filepath}",
179
+ new_path=f"b/{filepath}",
180
+ path=filepath,
181
+ operation="Update",
182
+ )
183
+ if not old and not new:
184
+ return file_diff
185
+ if not old:
186
+ file_diff.operation = "Create"
187
+ elif not new:
188
+ file_diff.operation = "Delete"
189
+
190
+ for group in sm.get_grouped_opcodes(n=3):
191
+ first_tag, first_i1, first_i2, first_j1, first_j2 = group[0]
192
+ last_tag, last_i1, last_i2, last_j1, last_j2 = group[-1]
193
+
194
+ old_count = last_i2 - first_i1
195
+ new_count = last_j2 - first_j1
196
+ old_start = 0 if old_count == 0 else first_i1 + 1
197
+ new_start = 0 if new_count == 0 else first_j1 + 1
198
+
199
+ hunk = DiffHunk(
200
+ old_start=old_start,
201
+ old_count=old_count,
202
+ new_start=new_start,
203
+ new_count=new_count,
204
+ )
205
+
206
+ old_lineno = old_start
207
+ new_lineno = new_start
208
+ for tag, i1, i2, j1, j2 in group:
209
+ if tag == "equal":
210
+ for k in range(i2 - i1):
211
+ hunk.lines.append(DiffLine(
212
+ kind="context",
213
+ old_lineno=old_lineno,
214
+ new_lineno=new_lineno,
215
+ text=old[i1 + k].rstrip("\n"),
216
+ ))
217
+ old_lineno += 1
218
+ new_lineno += 1
219
+ elif tag == "replace":
220
+ for k in range(i2 - i1):
221
+ hunk.lines.append(DiffLine(
222
+ kind="remove",
223
+ old_lineno=old_lineno,
224
+ text=old[i1 + k].rstrip("\n"),
225
+ ))
226
+ old_lineno += 1
227
+ for k in range(j2 - j1):
228
+ hunk.lines.append(DiffLine(
229
+ kind="add",
230
+ new_lineno=new_lineno,
231
+ text=new[j1 + k].rstrip("\n"),
232
+ ))
233
+ new_lineno += 1
234
+ elif tag == "delete":
235
+ for k in range(i2 - i1):
236
+ hunk.lines.append(DiffLine(
237
+ kind="remove",
238
+ old_lineno=old_lineno,
239
+ text=old[i1 + k].rstrip("\n"),
240
+ ))
241
+ old_lineno += 1
242
+ elif tag == "insert":
243
+ for k in range(j2 - j1):
244
+ hunk.lines.append(DiffLine(
245
+ kind="add",
246
+ new_lineno=new_lineno,
247
+ text=new[j1 + k].rstrip("\n"),
248
+ ))
249
+ new_lineno += 1
250
+
251
+ file_diff.hunks.append(hunk)
252
+ file_diff.added += sum(1 for line in hunk.lines if line.kind == "add")
253
+ file_diff.removed += sum(1 for line in hunk.lines if line.kind == "remove")
254
+
255
+ return file_diff
256
+
257
+
162
258
  def diff_stat(diff_text: str) -> tuple[int, int]:
163
259
  """Return (added, removed) line counts from a unified diff."""
164
260
  added = 0
@@ -150,7 +150,7 @@ def _prune_ai_tool_call_args(
150
150
  saved_chars += len(args["new_string"]) - len(placeholder)
151
151
  args["new_string"] = placeholder
152
152
  changed = True
153
- elif name == "line" and args.get("op") == "insert" and "new_string" in args:
153
+ elif name == "write" and args.get("op") in ("insert", "append") and "new_string" in args:
154
154
  placeholder = PRUNE_ARGS_PLACEHOLDER_DIFF
155
155
  if len(args["new_string"]) > len(placeholder) and _tool_result_has_diff(messages, ai_msg_index, tc_id):
156
156
  saved_chars += len(args["new_string"]) - len(placeholder)
@@ -267,8 +267,6 @@ class CompactionService:
267
267
  # Count turns by user messages
268
268
  if isinstance(msg, HumanMessage):
269
269
  turns_seen += 1
270
- if turns_seen > 2:
271
- continue
272
270
 
273
271
  if isinstance(msg, AIMessage) and hasattr(msg, "summary") and msg.summary:
274
272
  break # stop at compaction boundary
@@ -301,6 +299,10 @@ class CompactionService:
301
299
  if accumulated <= PRUNE_PROTECT:
302
300
  continue
303
301
 
302
+ # Protect most recent 2 turns from ToolMessage truncation
303
+ if turns_seen < 2:
304
+ continue
305
+
304
306
  if len(content) > TOOL_OUTPUT_MAX_CHARS:
305
307
  truncated = content[:TOOL_OUTPUT_MAX_CHARS] + (
306
308
  f"\n\n[Tool output truncated for context: omitted {len(content) - TOOL_OUTPUT_MAX_CHARS} chars]"
@@ -119,7 +119,7 @@ def strategy_action_for_tool(classified: ClassifiedToolCall, context: Permission
119
119
  return "allow"
120
120
  if classified.capability in {PermissionCapability.BASH_READ, PermissionCapability.GIT_READ}:
121
121
  return "allow"
122
- permission = "edit" if classified.name in {"file", "line", "replace"} else classified.name
122
+ permission = "edit" if classified.name in {"file", "write", "replace"} else classified.name
123
123
  return evaluate(permission, classified.pattern, BASIC_RULES).action
124
124
 
125
125
 
@@ -148,7 +148,7 @@ def resolve_approval(classified: ClassifiedToolCall, context: PermissionContext)
148
148
 
149
149
 
150
150
  def _session_rule_matches(tool: str, rule: str) -> bool:
151
- if rule == "edit" and tool in {"file", "line", "replace"}:
151
+ if rule == "edit" and tool in {"file", "write", "replace"}:
152
152
  return True
153
153
  if wildcard_match(tool, rule):
154
154
  return True
@@ -83,7 +83,7 @@ def merge(*rulesets: Ruleset) -> Ruleset:
83
83
 
84
84
  def disabled_tools(all_tools: list[str], ruleset: Ruleset) -> set[str]:
85
85
  """Find which tools are completely disabled (denied with pattern="*")."""
86
- EDIT_TOOLS = {"file", "line", "replace"}
86
+ EDIT_TOOLS = {"file", "write", "replace"}
87
87
  disabled: set[str] = set()
88
88
  for tool in all_tools:
89
89
  permission = "edit" if tool in EDIT_TOOLS else tool
@@ -89,7 +89,7 @@ def tool_call_from_pattern(tool: str, pattern: str = "*") -> dict:
89
89
 
90
90
  def repair_tool_name(tool: str) -> str:
91
91
  tool_map = {
92
- "Read": "read", "Write": "file", "Edit": "replace", "Delete": "line",
92
+ "Read": "read", "Write": "file", "Edit": "replace", "Delete": "replace",
93
93
  "MultiEdit": "replace", "multiEdit": "replace", "multi_edit": "replace",
94
94
  "Glob": "glob", "Grep": "grep", "Bash": "bash",
95
95
  "Agent": "agent", "TodoWrite": "todo", "Todo": "todo",
@@ -102,7 +102,7 @@ def repair_tool_name(tool: str) -> str:
102
102
  "LspDiagnostics": "lsp", "LspSymbols": "lsp",
103
103
  "LspDefinition": "lsp", "LspReferences": "lsp",
104
104
  "CompactContext": "compact",
105
- "write": "file", "edit": "replace", "insert": "line", "delete": "line",
105
+ "edit": "replace", "insert": "write", "append": "write", "delete": "replace", "line": "write",
106
106
  }
107
107
  return tool_map.get(tool, tool_map.get(tool.lower(), tool))
108
108
 
@@ -357,7 +357,7 @@ def capability_for_tool(tool: str, args: dict) -> PermissionCapability:
357
357
  "repo_map", "lsp",
358
358
  }:
359
359
  return PermissionCapability.READ_TOOLS
360
- if tool in {"file", "line", "replace"}:
360
+ if tool in {"file", "write", "replace"}:
361
361
  return PermissionCapability.FILE_WRITE
362
362
  if tool == "bash":
363
363
  return PermissionCapability.BASH_READ if is_safe_bash(str(args.get("command", ""))) else PermissionCapability.BASH_WRITE
@@ -372,7 +372,7 @@ def capability_for_tool(tool: str, args: dict) -> PermissionCapability:
372
372
 
373
373
 
374
374
  _FILE_PATTERN_TOOLS = {
375
- "read", "file", "line", "replace",
375
+ "read", "file", "write", "replace",
376
376
  "lsp",
377
377
  }
378
378
 
@@ -7,7 +7,7 @@ import shlex
7
7
  from dataclasses import dataclass
8
8
  from typing import Literal
9
9
 
10
- _HintableTool = Literal["read", "git", "file", "line", "replace", "glob", "grep"]
10
+ _HintableTool = Literal["read", "git", "file", "write", "replace", "glob", "grep"]
11
11
 
12
12
  _HEREDOC_MAX_CONTENT = 200
13
13
 
@@ -835,12 +835,12 @@ def _hint_write_echo(stripped: str, words: list[str]) -> RouteHint | None:
835
835
 
836
836
  if is_append:
837
837
  return RouteHint(
838
- tool_id="line", ui_label="→ line",
839
- llm_hint=f'Prefer line(file_path="{path}", op="insert", lineno=-1, new_string="{content}") for file tracking and diff output.',
838
+ tool_id="write", ui_label="→ write",
839
+ llm_hint=f'Prefer write(file_path="{path}", op="append", new_string="{content}") for file tracking and diff output.',
840
840
  )
841
841
  return RouteHint(
842
842
  tool_id="file", ui_label="→ file",
843
- llm_hint=f'Prefer file(file_path="{path}", op="create") then line(file_path="{path}", op="insert", lineno=0, new_string="{content}") for file tracking and diff output.',
843
+ llm_hint=f'Prefer file(file_path="{path}", op="create") then line(file_path="{path}", op="append", new_string="{content}") for file tracking and diff output.',
844
844
  )
845
845
 
846
846
 
@@ -887,12 +887,12 @@ def _hint_write_heredoc(stripped: str) -> RouteHint | None:
887
887
 
888
888
  if is_append:
889
889
  return RouteHint(
890
- tool_id="line", ui_label="→ line",
891
- llm_hint=f'Prefer line(file_path="{path}", op="insert", lineno=-1, new_string="{content}") for file tracking and diff output.',
890
+ tool_id="write", ui_label="→ write",
891
+ llm_hint=f'Prefer write(file_path="{path}", op="append", new_string="{content}") for file tracking and diff output.',
892
892
  )
893
893
  return RouteHint(
894
894
  tool_id="file", ui_label="→ file",
895
- llm_hint=f'Prefer file(file_path="{path}", op="create") then line(file_path="{path}", op="insert", lineno=0, new_string="{content}") for file tracking and diff output.',
895
+ llm_hint=f'Prefer file(file_path="{path}", op="create") then line(file_path="{path}", op="append", new_string="{content}") for file tracking and diff output.',
896
896
  )
897
897
 
898
898
 
@@ -1199,8 +1199,8 @@ def _hint_sed(words: list[str]) -> RouteHint | None:
1199
1199
  if m:
1200
1200
  start, end = int(m.group(1)), int(m.group(2))
1201
1201
  return RouteHint(
1202
- tool_id="line", ui_label="→ line",
1203
- llm_hint=f'For line range deletion: first read {path} to see lines {start}-{end}, then use line(file_path="{path}", op="delete", lineno={start}, end_no={end}).',
1202
+ tool_id="replace", ui_label="→ replace",
1203
+ llm_hint=f'For line range deletion: first read {path} to see lines {start}-{end}, then use replace(file_path="{path}", start_no={start}, end_no={end}, prefix="...", suffix="...", new_string="").',
1204
1204
  )
1205
1205
 
1206
1206
 
@@ -1208,8 +1208,8 @@ def _hint_sed(words: list[str]) -> RouteHint | None:
1208
1208
  if m:
1209
1209
  line_no = int(m.group(1))
1210
1210
  return RouteHint(
1211
- tool_id="line", ui_label="→ line",
1212
- llm_hint=f'For single line deletion: first read {path} to see line {line_no}, then use line(file_path="{path}", op="delete", lineno={line_no}).',
1211
+ tool_id="replace", ui_label="→ replace",
1212
+ llm_hint=f'For single line deletion: first read {path} to see line {line_no}, then use replace(file_path="{path}", start_no={line_no}, end_no={line_no}, prefix="...", suffix="...", new_string="").',
1213
1213
  )
1214
1214
  m = _SED_PATTERN_DELETE.match(script)
1215
1215
  if m:
@@ -1,17 +1,17 @@
1
- """File operation tools — read, file, line, replace. Deterministic, typed I/O."""
1
+ """File operation tools — read, file, write, replace. Deterministic, typed I/O."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
5
  from .edit_execute import FileReplaceInput, FileReplaceTool
6
6
  from .file import FileInput, FileTool
7
- from .line import LineInput, LineTool
7
+ from .write import WriteInput, WriteTool
8
8
  from .read import FileReadInput, FileReadTool
9
9
 
10
10
  __all__ = [
11
11
  "FileTool",
12
12
  "FileInput",
13
- "LineTool",
14
- "LineInput",
13
+ "WriteTool",
14
+ "WriteInput",
15
15
  "FileReadTool",
16
16
  "FileReadInput",
17
17
  "FileReplaceTool",