voidx 3.4.0__tar.gz → 3.4.1__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.
- voidx-3.4.1/LICENSE +21 -0
- {voidx-3.4.0 → voidx-3.4.1}/PKG-INFO +5 -1
- {voidx-3.4.0 → voidx-3.4.1}/pyproject.toml +2 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/__init__.py +1 -1
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/run_loop.py +1 -1
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/wiring.py +1 -1
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/handler.py +3 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/model.py +41 -2
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/models.py +5 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings.py +1 -1
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/provider.py +3 -1
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/service.py +2 -2
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_ops/edit_execute.py +6 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/commands.py +1 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/app.py +43 -3
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/formatting.py +19 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/input.py +29 -4
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/parser.py +60 -22
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx.egg-info/PKG-INFO +5 -1
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx.egg-info/SOURCES.txt +1 -0
- {voidx-3.4.0 → voidx-3.4.1}/README.md +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/setup.cfg +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/agents.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/attachments.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/goal_resolver.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/compaction.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/compaction_coordinator.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/contracts.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/convergence.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/core/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/core/_voidx_graph.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/core/helpers.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/core/llm.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/permissions.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/runtime.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/runtime_guards.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/session_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/session_runtime.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/streaming.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/subagent.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/title_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/todo_events.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_execution.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_executor/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_executor/executor.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_executor/guards.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_executor/helpers.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_executor/types.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_executor/ui.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/tool_executor/workflow.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/topology.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/transcript_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/turn_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/turn_runner.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/graph/workflow_utils.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/message_rows.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/message_trimming.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/prompts.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/runtime_context.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/code_ide.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/guide.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/host.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/init.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/lsp.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/mcp.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/profile.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/runtime.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/session.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/skills.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/slash/upgrade.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/task_state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/todo_state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/tool_call_ids.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/tool_exchange_sanitizer.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/tool_filters.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/tool_messages.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/agent/tool_result_storage.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/enums.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/permissions.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_agent.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_api_keys.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_code_ide.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_custom.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_mcp.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_permissions.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_skills.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_update.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_utils.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/config/settings_web.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/data/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/data/templates/api-doc.md +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/data/templates/prd.md +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/data/templates/readme.md +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/data/templates/rfc.md +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/data/templates/tech-design.md +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/diffing.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/catalog.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/compaction.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/context.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/instruction.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/message_markers.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/message_status.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/llm/usage.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/logging/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/logging/request_log.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/logging/tool_log.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/client.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/config.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/detector.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/detector_data.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/errors.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/manager.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/schema.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/lsp/service.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/main.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/client/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/client/base.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/client/errors.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/client/http_transport.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/client/sse_transport.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/client/stdio_transport.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/manager.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/schema.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp/tool.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp_servers/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/mcp_servers/web.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/cleanup.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/context_frames.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/jsonl_store.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/model_profiles.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/runtime_state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/service.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/session.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/store.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/subagents.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/memory/transcript.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/context.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/engine.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/evaluate.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/rules.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/sandbox.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/schema.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/service.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/permission/wildcard.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/attachments.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/intent.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/reference_tokens.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/task_state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/todo.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/ui.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/runtime/ui_port.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/selfupdate.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/skills/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/skills/context.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/skills/references.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/skills/registry.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/skills/schema.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/skills/service.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/agent.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/base.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/core.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/hint/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/hint/file.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/hint/git.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/hint/search.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/router.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/safety.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/bash/tool.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/clarify.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/compact_context.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_ops/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_ops/edit_resolve.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_ops/file.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_ops/read.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_ops/types.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_ops/write.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/file_state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/git.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/load_doc_template.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/load_skills.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/lsp.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/plan_checkpoint.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/core.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/hint/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/hint/file.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/hint/search.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/router.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/safety.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/sandbox.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/powershell/tool.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/registry.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/search.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/service.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/shell/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/shell/common.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/shell/hint/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/shell/hint/git.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/skills.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/task_status.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/task_tracker.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/todo.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/web_content.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/web_mcp.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/webfetch.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/websearch.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/tools/workflow.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/frontend.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/gateway/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/gateway/adapter.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/gateway/bootstrap.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/gateway/diff_review.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/gateway/server.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/gateway/session.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/gateway/terminal.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/agent_display.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/browse.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/capture.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/console/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/console/app.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/console/formatting.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/console/streaming.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/diff.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/display_policy.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/agent_placeholder.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/nodes.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/nodes_checkpoint.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/nodes_clarify.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/nodes_permission.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/nodes_startup.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/nodes_status.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/status.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/stream.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/dock/todo.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/events/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/events/bus.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/events/consumers.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/events/schema.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/tree.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/output/types.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/commands.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/envelope.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/requests.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/schema.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/transcript.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/v2/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/v2/envelope.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/v2/methods.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/v2/snapshot.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/protocol/v2/threads.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/session.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tools/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tools/attachment_tokens.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tools/clipboard_image.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tools/clipboard_text.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tools/code_ide.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tools/file_picker.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tools/skill_picker.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/transcript.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/activity.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/app.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/choice_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/clipboard_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/helpers.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/overlays.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/panels.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/render_activity.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/render_frame.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/render_input.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/render_status.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/render_todo.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/renderer.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/state.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/terminal_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/ui/tui/text_prompt_mixin.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/__init__.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/auto_advance.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/context.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/dag.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/nodes.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/policy.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/reconcile.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/render.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/route.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/runtime.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/schema.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/service.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx/workflow/types.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx.egg-info/dependency_links.txt +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx.egg-info/entry_points.txt +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx.egg-info/requires.txt +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/src/voidx.egg-info/top_level.txt +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/tests/test_install_sh.py +0 -0
- {voidx-3.4.0 → voidx-3.4.1}/tests/test_npm_package.py +0 -0
voidx-3.4.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 chikhamx
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: voidx
|
|
3
|
-
Version: 3.4.
|
|
3
|
+
Version: 3.4.1
|
|
4
4
|
Summary: A coding agent that quantifies everything and solves with tools, not fuzzy prompts.
|
|
5
|
+
Author: chikhamx
|
|
6
|
+
License: MIT
|
|
5
7
|
Requires-Python: >=3.11
|
|
6
8
|
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
7
10
|
Requires-Dist: langgraph>=0.3.0
|
|
8
11
|
Requires-Dist: langchain>=0.3.0
|
|
9
12
|
Requires-Dist: langchain-anthropic>=0.3.0
|
|
@@ -23,6 +26,7 @@ Provides-Extra: dev
|
|
|
23
26
|
Requires-Dist: build>=1.2.0; extra == "dev"
|
|
24
27
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
25
28
|
Requires-Dist: pytest-asyncio>=0.25.0; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
26
30
|
|
|
27
31
|
# voidx
|
|
28
32
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
name = "voidx"
|
|
3
3
|
dynamic = ["version"]
|
|
4
4
|
description = "A coding agent that quantifies everything and solves with tools, not fuzzy prompts."
|
|
5
|
+
license = {text = "MIT"}
|
|
6
|
+
authors = [{name = "chikhamx"}]
|
|
5
7
|
readme = "README.md"
|
|
6
8
|
requires-python = ">=3.11"
|
|
7
9
|
dependencies = [
|
|
@@ -169,7 +169,7 @@ class GraphRunLoopMixin(GraphTurnMixin, GraphSessionMixin, GraphTranscriptMixin)
|
|
|
169
169
|
model=self.config.model.model,
|
|
170
170
|
workspace=self._workspace,
|
|
171
171
|
session_title=title,
|
|
172
|
-
context_limit=get_context_limit(self.config.model.provider),
|
|
172
|
+
context_limit=get_context_limit(self.config.model.provider, self.config.model.protocol or "", self.config.model.context_window),
|
|
173
173
|
reasoning_effort=self.config.model.reasoning_effort or "xhigh",
|
|
174
174
|
permission_label=self._permission.status_label,
|
|
175
175
|
sandbox_label=lambda: self._permission._sandbox_label(),
|
|
@@ -74,7 +74,7 @@ def build_permission_service(config: Config, *, notifier: Callable[[str], object
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
def build_compaction_service(config: Config) -> tuple[UsageStats, CompactionService]:
|
|
77
|
-
context_limit = get_context_limit(config.model.provider)
|
|
77
|
+
context_limit = get_context_limit(config.model.provider, config.model.protocol or "", config.model.context_window)
|
|
78
78
|
return (
|
|
79
79
|
UsageStats(context_limit=context_limit),
|
|
80
80
|
CompactionService(
|
|
@@ -146,6 +146,9 @@ class SlashHandler(
|
|
|
146
146
|
elif args == "reasoning" or args.startswith("reasoning "):
|
|
147
147
|
target = args.removeprefix("reasoning").strip()
|
|
148
148
|
await self._model_reasoning(target)
|
|
149
|
+
elif args == "ctx" or args.startswith("ctx "):
|
|
150
|
+
target = args.removeprefix("ctx").strip()
|
|
151
|
+
await self._model_ctx(target)
|
|
149
152
|
elif args:
|
|
150
153
|
await self._switch_model(args)
|
|
151
154
|
else:
|
|
@@ -190,7 +190,7 @@ class SlashModelMixin:
|
|
|
190
190
|
else:
|
|
191
191
|
ui.print("[dim](none)[/dim]")
|
|
192
192
|
ui.print()
|
|
193
|
-
ui.print("[dim]Usage: /model list|new|reasoning|test|del|switch|<name>[/dim]")
|
|
193
|
+
ui.print("[dim]Usage: /model list|new|reasoning|ctx|test|del|switch|<name>[/dim]")
|
|
194
194
|
|
|
195
195
|
async def _model_list(self) -> None:
|
|
196
196
|
cfg = self.host.config
|
|
@@ -341,6 +341,45 @@ class SlashModelMixin:
|
|
|
341
341
|
|
|
342
342
|
ui.print(f"Reasoning effort: [cyan]{new_effort}[/cyan] [green]✓[/green]")
|
|
343
343
|
|
|
344
|
+
async def _model_ctx(self, target: str) -> None:
|
|
345
|
+
choices_map: dict[str, int | None] = {
|
|
346
|
+
"128k": 128_000,
|
|
347
|
+
"256k": 256_000,
|
|
348
|
+
"384k": 384_000,
|
|
349
|
+
"512k": 512_000,
|
|
350
|
+
"1M": 1_000_000,
|
|
351
|
+
"Auto": None,
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if target:
|
|
355
|
+
key = target.lower()
|
|
356
|
+
normalized = {c.lower(): (c, v) for c, v in choices_map.items()}
|
|
357
|
+
if key not in normalized:
|
|
358
|
+
ui.error(f"Invalid context window: '{target}'. Use: {', '.join(choices_map)}")
|
|
359
|
+
return
|
|
360
|
+
new_label, new_value = normalized[key]
|
|
361
|
+
else:
|
|
362
|
+
choices = list(choices_map)
|
|
363
|
+
idx = await _select_from_list(self.host.app, "Context window", choices)
|
|
364
|
+
if idx is None:
|
|
365
|
+
ui.print("[dim]Cancelled.[/dim]")
|
|
366
|
+
return
|
|
367
|
+
new_value = choices_map[choices[idx]]
|
|
368
|
+
new_label = choices[idx]
|
|
369
|
+
|
|
370
|
+
self.host.config.model.context_window = new_value
|
|
371
|
+
self._sync_context_limit()
|
|
372
|
+
|
|
373
|
+
settings = self.host.settings
|
|
374
|
+
if settings is not None:
|
|
375
|
+
if new_value is None:
|
|
376
|
+
settings._pop_setting("context_window")
|
|
377
|
+
else:
|
|
378
|
+
settings._set_setting("context_window", new_value)
|
|
379
|
+
|
|
380
|
+
display = "Auto (provider default)" if new_value is None else f"{new_label}"
|
|
381
|
+
ui.print(f"Context window: [cyan]{display}[/cyan] [green]✓[/green]")
|
|
382
|
+
|
|
344
383
|
async def _switch_model(self, model_spec: str) -> None:
|
|
345
384
|
from voidx.llm.service import create_chat_model
|
|
346
385
|
from voidx.memory.service import update_session_model
|
|
@@ -412,7 +451,7 @@ class SlashModelMixin:
|
|
|
412
451
|
def _sync_context_limit(self) -> None:
|
|
413
452
|
from voidx.llm.service import get_context_limit
|
|
414
453
|
|
|
415
|
-
limit = get_context_limit(self.host.config.model.provider)
|
|
454
|
+
limit = get_context_limit(self.host.config.model.provider, self.host.config.model.protocol or "", self.host.config.model.context_window)
|
|
416
455
|
stats = self.host.usage_stats
|
|
417
456
|
if stats is not None:
|
|
418
457
|
stats.context_limit = limit
|
|
@@ -32,6 +32,11 @@ class ModelConfig(BaseModel):
|
|
|
32
32
|
default="xhigh",
|
|
33
33
|
description="Reasoning intensity: off, low, medium, high, xhigh, or None (provider default)",
|
|
34
34
|
)
|
|
35
|
+
context_window: int | None = Field(
|
|
36
|
+
default=None,
|
|
37
|
+
ge=1,
|
|
38
|
+
description="Override context window size in tokens. None = auto-detect by provider.",
|
|
39
|
+
)
|
|
35
40
|
|
|
36
41
|
|
|
37
42
|
class AgentConfig(BaseModel):
|
|
@@ -312,7 +312,7 @@ class Settings(
|
|
|
312
312
|
base_url = None
|
|
313
313
|
protocol = None
|
|
314
314
|
|
|
315
|
-
cfg = ModelConfig(provider=provider, model=model, base_url=base_url)
|
|
315
|
+
cfg = ModelConfig(provider=provider, model=model, base_url=base_url, context_window=self._effective_data().get("context_window"))
|
|
316
316
|
if protocol:
|
|
317
317
|
cfg.protocol = protocol
|
|
318
318
|
permission_mode = self.get_permission_mode()
|
|
@@ -584,8 +584,10 @@ def extract_thinking(chunk: AIMessageChunk, protocol: str) -> str:
|
|
|
584
584
|
|
|
585
585
|
# ── context limits ────────────────────────────────────────────────────────
|
|
586
586
|
|
|
587
|
-
def get_context_limit(provider: str, protocol: str = "") -> int:
|
|
587
|
+
def get_context_limit(provider: str, protocol: str = "", context_window: int | None = None) -> int:
|
|
588
588
|
"""Return context-window limit for *provider*. Falls back to *protocol* for unknown providers."""
|
|
589
|
+
if context_window is not None and context_window > 0:
|
|
590
|
+
return context_window
|
|
589
591
|
limits: dict[str, int] = {
|
|
590
592
|
PROTOCOL_DEEPSEEK: 1_000_000,
|
|
591
593
|
"anthropic": 200_000,
|
|
@@ -21,8 +21,8 @@ def extract_thinking(chunk: AIMessageChunk, protocol: str) -> str:
|
|
|
21
21
|
return provider.extract_thinking(chunk, protocol)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def get_context_limit(provider_name: str, protocol: str = "") -> int:
|
|
25
|
-
return provider.get_context_limit(provider_name, protocol)
|
|
24
|
+
def get_context_limit(provider_name: str, protocol: str = "", context_window: int | None = None) -> int:
|
|
25
|
+
return provider.get_context_limit(provider_name, protocol, context_window)
|
|
26
26
|
|
|
27
27
|
__all__ = [
|
|
28
28
|
"create_chat_model",
|
|
@@ -158,6 +158,12 @@ async def _execute_text_replace(
|
|
|
158
158
|
|
|
159
159
|
lines = list(display.lines)
|
|
160
160
|
new_lines = _split_edit_lines(new_string)
|
|
161
|
+
if (
|
|
162
|
+
start_line == end_line
|
|
163
|
+
and new_string in ("", "\n", " ")
|
|
164
|
+
and lines[start_line - 1] != ""
|
|
165
|
+
):
|
|
166
|
+
new_lines = []
|
|
161
167
|
lines[start_line - 1:end_line] = new_lines
|
|
162
168
|
|
|
163
169
|
# Head-line dedup: if the first line of new_string exactly matches the
|
|
@@ -49,6 +49,7 @@ COMMANDS: list[tuple[str, str]] = [
|
|
|
49
49
|
("/mode goal", "Keep multi-step work scoped"),
|
|
50
50
|
("/mode plan", "Read-only plan mode"),
|
|
51
51
|
("/model", "Switch configured model"),
|
|
52
|
+
("/model ctx", "Set context window size"),
|
|
52
53
|
("/model del", "Remove a profile"),
|
|
53
54
|
("/model list", "Show configured model details"),
|
|
54
55
|
("/model new", "Create or update a model profile"),
|
|
@@ -12,9 +12,12 @@ from rich.text import Text
|
|
|
12
12
|
|
|
13
13
|
from voidx.ui.output.dock.formatting import (
|
|
14
14
|
ANSI_LINE_PREFIX,
|
|
15
|
+
_ansi_line,
|
|
15
16
|
_clean,
|
|
17
|
+
_markdown_lines,
|
|
16
18
|
_strip_ansi_trailing_space,
|
|
17
19
|
_text_from_line,
|
|
20
|
+
split_pasted_segments,
|
|
18
21
|
)
|
|
19
22
|
from voidx.ui.output.dock.nodes import DockNodeMixin
|
|
20
23
|
from voidx.ui.output.dock.stream import DockStreamMixin
|
|
@@ -219,18 +222,55 @@ class BottomInputDock(DockStreamMixin, DockStatusMixin, DockNodeMixin):
|
|
|
219
222
|
if self._tree.root.children:
|
|
220
223
|
self._append_root_spacer()
|
|
221
224
|
preview = _clean(text)
|
|
222
|
-
|
|
225
|
+
segments = split_pasted_segments(preview)
|
|
226
|
+
header, body_lines = self._render_turn_segments(segments)
|
|
223
227
|
self._current_turn = self._tree.new_node(
|
|
224
228
|
parent=self._tree.root,
|
|
225
229
|
node_type="turn",
|
|
226
|
-
header=f"[bold white]❯[/] {
|
|
227
|
-
body_lines=
|
|
230
|
+
header=f"[bold white]❯[/] {header}",
|
|
231
|
+
body_lines=body_lines,
|
|
228
232
|
collapsed=False,
|
|
229
233
|
)
|
|
230
234
|
self._mark_settled(self._current_turn)
|
|
231
235
|
self.refresh()
|
|
232
236
|
return self._current_turn
|
|
233
237
|
|
|
238
|
+
def _render_turn_segments(
|
|
239
|
+
self, segments: list[tuple[bool, str]]
|
|
240
|
+
) -> tuple[str, list[str]]:
|
|
241
|
+
"""Render (is_pasted, content) segments into (header, body_lines).
|
|
242
|
+
|
|
243
|
+
Non-pasted segments use escape() plain text (existing behavior).
|
|
244
|
+
Pasted segments use _markdown_lines() with _ansi_line() wrapping.
|
|
245
|
+
"""
|
|
246
|
+
header = ""
|
|
247
|
+
body_lines: list[str] = []
|
|
248
|
+
width = self._markdown_width()
|
|
249
|
+
for index, (is_pasted, content) in enumerate(segments):
|
|
250
|
+
if is_pasted:
|
|
251
|
+
md_lines = [
|
|
252
|
+
_ansi_line(line)
|
|
253
|
+
for line in _markdown_lines(content, width)
|
|
254
|
+
]
|
|
255
|
+
if index == 0 and md_lines:
|
|
256
|
+
header = md_lines[0]
|
|
257
|
+
body_lines.extend(md_lines[1:])
|
|
258
|
+
else:
|
|
259
|
+
body_lines.extend(md_lines)
|
|
260
|
+
else:
|
|
261
|
+
lines = [
|
|
262
|
+
_strip_ansi_trailing_space(line)
|
|
263
|
+
for line in ((content.splitlines() or [content]) if content else [])
|
|
264
|
+
]
|
|
265
|
+
if index == 0 and lines:
|
|
266
|
+
header = escape(lines[0])
|
|
267
|
+
body_lines.extend(escape(line) for line in lines[1:])
|
|
268
|
+
else:
|
|
269
|
+
body_lines.extend(escape(line) for line in lines)
|
|
270
|
+
if not header and body_lines:
|
|
271
|
+
header = body_lines.pop(0)
|
|
272
|
+
return header, body_lines
|
|
273
|
+
|
|
234
274
|
def ensure_agent(self) -> OutputNode:
|
|
235
275
|
if self._current_agent is None:
|
|
236
276
|
self._append_root_spacer()
|
|
@@ -92,6 +92,25 @@ def _strip_ansi_trailing_space(line: str) -> str:
|
|
|
92
92
|
return buffer.getvalue()
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
_PASTED_RE = re.compile(r"<pasted>\n(.*?)\n</pasted>", re.DOTALL)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def split_pasted_segments(text: str) -> list[tuple[bool, str]]:
|
|
99
|
+
"""Split text into (is_pasted, content) segments by <pasted> blocks."""
|
|
100
|
+
segments: list[tuple[bool, str]] = []
|
|
101
|
+
pos = 0
|
|
102
|
+
for match in _PASTED_RE.finditer(text):
|
|
103
|
+
if match.start() > pos:
|
|
104
|
+
segments.append((False, text[pos:match.start()]))
|
|
105
|
+
segments.append((True, match.group(1)))
|
|
106
|
+
pos = match.end()
|
|
107
|
+
if pos < len(text):
|
|
108
|
+
segments.append((False, text[pos:]))
|
|
109
|
+
if not segments:
|
|
110
|
+
segments.append((False, text))
|
|
111
|
+
return segments
|
|
112
|
+
|
|
113
|
+
|
|
95
114
|
def short_value(value: object) -> str:
|
|
96
115
|
text = str(value).replace("\n", "\\n")
|
|
97
116
|
return text[:157] + "..." if len(text) > 160 else text
|
|
@@ -52,12 +52,15 @@ class _InputEditorMixin:
|
|
|
52
52
|
|
|
53
53
|
def _register_text_paste(self, text: str) -> str:
|
|
54
54
|
paste_id = self._paste_next_id
|
|
55
|
+
display = self._compute_text_paste_display(paste_id, text)
|
|
56
|
+
return self._register_paste_entry(kind="text", display=display, expanded=text)
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def _compute_text_paste_display(paste_id: int, text: str) -> str:
|
|
55
60
|
line_count = len(text.split("\n"))
|
|
56
61
|
if line_count > 1:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
display = f"[Pasted text #{paste_id} {len(text)} chars]"
|
|
60
|
-
return self._register_paste_entry(kind="text", display=display, expanded=text)
|
|
62
|
+
return f"[Pasted text #{paste_id} +{line_count - 1} lines]"
|
|
63
|
+
return f"[Pasted text #{paste_id} {len(text)} chars]"
|
|
61
64
|
|
|
62
65
|
def _register_image_paste(self, stem: str, size: int) -> str:
|
|
63
66
|
from voidx.ui.tools.attachment_tokens import image_attachment_token_text
|
|
@@ -80,6 +83,8 @@ class _InputEditorMixin:
|
|
|
80
83
|
display = str(entry.get("display", ""))
|
|
81
84
|
expanded = str(entry.get("expanded", ""))
|
|
82
85
|
if display:
|
|
86
|
+
if entry.get("kind") == "text":
|
|
87
|
+
expanded = f"<pasted>\n{expanded}\n</pasted>"
|
|
83
88
|
result = result.replace(display, expanded)
|
|
84
89
|
return result
|
|
85
90
|
|
|
@@ -343,3 +348,23 @@ class _InputEditorMixin:
|
|
|
343
348
|
self._cursor_col = col + len(token)
|
|
344
349
|
self._clear_attachment_suppression_on_edit()
|
|
345
350
|
self._update_input_panels()
|
|
351
|
+
|
|
352
|
+
def _replace_token_in_editor(self, old_token: str, new_token: str) -> None:
|
|
353
|
+
"""Replace a token string in the input with an updated version.
|
|
354
|
+
|
|
355
|
+
Used when merging split-batch pastes: the old token (e.g.
|
|
356
|
+
"[Pasted text #1 +3 lines]") is replaced in-place by the new
|
|
357
|
+
token (e.g. "[Pasted text #1 +4 lines]") with updated line count.
|
|
358
|
+
"""
|
|
359
|
+
self._reset_ctrl_c()
|
|
360
|
+
line = self._current_line()
|
|
361
|
+
idx = line.rfind(old_token)
|
|
362
|
+
if idx == -1:
|
|
363
|
+
# Token not found on current line — fall back to inserting new
|
|
364
|
+
self._insert_text_token(new_token)
|
|
365
|
+
return
|
|
366
|
+
new_line = line[:idx] + new_token + line[idx + len(old_token):]
|
|
367
|
+
self._set_current_line(new_line)
|
|
368
|
+
self._cursor_col = idx + len(new_token)
|
|
369
|
+
self._clear_attachment_suppression_on_edit()
|
|
370
|
+
self._update_input_panels()
|
|
@@ -77,30 +77,41 @@ class _InputParserMixin:
|
|
|
77
77
|
if mapped:
|
|
78
78
|
return mapped.encode("utf-8")
|
|
79
79
|
return ("\x00" + ch2).encode("utf-8")
|
|
80
|
-
#
|
|
81
|
-
if
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
# Any character could be the start of a paste. Probe for
|
|
81
|
+
# quickly-following chars; if they look like a paste (multiple
|
|
82
|
+
# newlines or a long run), wrap the whole thing as bracketed
|
|
83
|
+
# paste. This fixes the case where pasted text starts with a
|
|
84
|
+
# regular char (e.g. "line1\r\nline2") — previously only
|
|
85
|
+
# newline-first pastes triggered the drain, so the leading
|
|
86
|
+
# "line1" was inserted char-by-char while the rest became a
|
|
87
|
+
# partial [Pasted text] token.
|
|
88
|
+
pasted = self._try_drain_win32_paste(ch)
|
|
89
|
+
if pasted is not None:
|
|
90
|
+
return pasted
|
|
86
91
|
return ch.encode("utf-8")
|
|
87
92
|
|
|
88
93
|
return await asyncio.to_thread(_read)
|
|
89
94
|
|
|
90
95
|
def _try_drain_win32_paste(self, first_char: str, timeout_ms: int = 20) -> bytes | None:
|
|
91
|
-
"""Drain remaining console input
|
|
96
|
+
"""Drain remaining console input to detect a paste.
|
|
92
97
|
|
|
93
|
-
Windows Terminal injects pasted content character-by-character into
|
|
94
|
-
console input buffer.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
Windows Terminal injects pasted content character-by-character into
|
|
99
|
+
the console input buffer. This method reads any quickly-following
|
|
100
|
+
characters after ``first_char`` and, if they look like a paste
|
|
101
|
+
(multiple newlines or a long run), wraps them in a bracketed-paste
|
|
102
|
+
sequence so ``_process_paste`` handles them as a unit.
|
|
103
|
+
|
|
104
|
+
Works with ANY first character, not just newlines — pasted text
|
|
105
|
+
often starts with a regular character (e.g. "line1\\r\\nline2").
|
|
98
106
|
Returns the bracketed-paste bytes, or ``None`` if this is not a paste.
|
|
99
107
|
"""
|
|
100
108
|
import msvcrt
|
|
101
109
|
import time
|
|
102
110
|
|
|
103
|
-
if
|
|
111
|
+
# Quick exit: if nothing follows immediately, this is a single
|
|
112
|
+
# keypress (normal typing), not a paste. Avoids entering the poll
|
|
113
|
+
# loop for every ordinary character.
|
|
114
|
+
if not msvcrt.kbhit():
|
|
104
115
|
return None
|
|
105
116
|
|
|
106
117
|
buffer = first_char
|
|
@@ -121,6 +132,11 @@ class _InputParserMixin:
|
|
|
121
132
|
newline_count = buffer.count("\r") + buffer.count("\n")
|
|
122
133
|
if newline_count >= 2 or len(buffer) > 8:
|
|
123
134
|
return b"\x1b[200~" + buffer.encode("utf-8", errors="replace") + b"\x1b[201~"
|
|
135
|
+
# Not a paste: preserve any extra chars we consumed so they are
|
|
136
|
+
# processed on the next _process_input call instead of being lost.
|
|
137
|
+
extra = buffer[1:]
|
|
138
|
+
if extra:
|
|
139
|
+
self._pending_bytes = extra.encode("utf-8", errors="replace")
|
|
124
140
|
return None
|
|
125
141
|
|
|
126
142
|
async def _read_input_line(self) -> bytes:
|
|
@@ -231,16 +247,38 @@ class _InputParserMixin:
|
|
|
231
247
|
return
|
|
232
248
|
# Normalise line endings: \r\n → \n, \r → \n
|
|
233
249
|
text = text.replace("\r\n", "\n").replace("\r", "\n")
|
|
234
|
-
|
|
250
|
+
# All pasted text collapses to a [Pasted text #N ...] token, regardless
|
|
251
|
+
# of length. This keeps the input box compact and clearly marks pasted
|
|
252
|
+
# content as a distinct unit.
|
|
253
|
+
#
|
|
254
|
+
# Merge with the previous paste if it happened within a short window.
|
|
255
|
+
# Windows Terminal may split a large paste across multiple drain
|
|
256
|
+
# batches (20ms timeout fires between batches), producing two
|
|
257
|
+
# bracketed-paste sequences. Without merging, the user sees two
|
|
258
|
+
# adjacent tokens for what was a single paste action.
|
|
259
|
+
import time
|
|
260
|
+
|
|
261
|
+
now = time.monotonic()
|
|
262
|
+
last = getattr(self, "_last_paste_time", None)
|
|
263
|
+
self._last_paste_time = now
|
|
264
|
+
|
|
265
|
+
if (
|
|
266
|
+
last is not None
|
|
267
|
+
and (now - last) < 0.2
|
|
268
|
+
and self._paste_entries
|
|
269
|
+
and self._paste_entries[-1].get("kind") == "text"
|
|
270
|
+
):
|
|
271
|
+
# Append to the previous paste entry
|
|
272
|
+
entry = self._paste_entries[-1]
|
|
273
|
+
combined = str(entry["expanded"]) + text
|
|
274
|
+
old_display = str(entry["display"])
|
|
275
|
+
new_display = self._compute_text_paste_display(int(entry["id"]), combined)
|
|
276
|
+
entry["expanded"] = combined
|
|
277
|
+
entry["display"] = new_display
|
|
278
|
+
# Replace the old token in the editor with the updated one
|
|
279
|
+
self._replace_token_in_editor(old_display, new_display)
|
|
280
|
+
else:
|
|
235
281
|
self._insert_text_token(self._register_text_paste(text))
|
|
236
|
-
return
|
|
237
|
-
# Split into lines and insert each, creating editor newlines
|
|
238
|
-
lines = text.split("\n")
|
|
239
|
-
for i, line in enumerate(lines):
|
|
240
|
-
if i > 0:
|
|
241
|
-
self._insert_newline()
|
|
242
|
-
if line:
|
|
243
|
-
self._insert_text(line)
|
|
244
282
|
|
|
245
283
|
def _dispatch_key(self, data: bytes, offset: int) -> tuple[int, str | None]:
|
|
246
284
|
"""Parse a key sequence starting at offset. Returns (bytes_consumed, action)."""
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: voidx
|
|
3
|
-
Version: 3.4.
|
|
3
|
+
Version: 3.4.1
|
|
4
4
|
Summary: A coding agent that quantifies everything and solves with tools, not fuzzy prompts.
|
|
5
|
+
Author: chikhamx
|
|
6
|
+
License: MIT
|
|
5
7
|
Requires-Python: >=3.11
|
|
6
8
|
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
7
10
|
Requires-Dist: langgraph>=0.3.0
|
|
8
11
|
Requires-Dist: langchain>=0.3.0
|
|
9
12
|
Requires-Dist: langchain-anthropic>=0.3.0
|
|
@@ -23,6 +26,7 @@ Provides-Extra: dev
|
|
|
23
26
|
Requires-Dist: build>=1.2.0; extra == "dev"
|
|
24
27
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
25
28
|
Requires-Dist: pytest-asyncio>=0.25.0; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
26
30
|
|
|
27
31
|
# voidx
|
|
28
32
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|