voidx 2.1.1__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.
- {voidx-2.1.1 → voidx-2.2.0}/PKG-INFO +1 -1
- {voidx-2.1.1 → voidx-2.2.0}/pyproject.toml +2 -3
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/__init__.py +1 -1
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/agents.py +11 -11
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/core.py +30 -2
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/subagent.py +6 -24
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/tool_execution.py +46 -3
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/turn_mixin.py +16 -8
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/intent_refinement.py +7 -5
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/runtime_context.py +85 -9
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/catalog.py +7 -1
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/instruction.py +63 -14
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/usage.py +35 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/rules.py +2 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/runtime/intent_classifier.py +3 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/runtime/ui.py +2 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/skills/__init__.py +12 -1
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/writing-design-docs/SKILL.md +1 -1
- voidx-2.2.0/src/voidx/skills/context.py +168 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/skills/policy.py +21 -2
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/skills/registry.py +23 -2
- voidx-2.2.0/src/voidx/skills/runtime.py +252 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/skills/service.py +39 -7
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/base.py +1 -0
- voidx-2.2.0/src/voidx/tools/load_skills.py +203 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/on_intent.py +1 -6
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/registry.py +8 -3
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/repomap.py +3 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/search.py +4 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/websearch.py +4 -1
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/browse.py +38 -24
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/capture.py +13 -4
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/__init__.py +3 -0
- voidx-2.2.0/src/voidx/ui/output/dock/agent_placeholder.py +21 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/app.py +53 -6
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/nodes.py +8 -2
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/stream.py +7 -4
- voidx-2.2.0/src/voidx/ui/output/dock/todo.py +110 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/events/__init__.py +30 -75
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/events/schema.py +10 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/tree.py +22 -0
- voidx-2.2.0/src/voidx/ui/tui/activity.py +64 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/app.py +57 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/panels.py +5 -2
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/parser.py +24 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/renderer.py +360 -39
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/state.py +17 -0
- voidx-2.2.0/src/voidx/ui/tui/terminal_mixin.py +95 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx.egg-info/PKG-INFO +1 -1
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx.egg-info/SOURCES.txt +15 -14
- voidx-2.2.0/tests/test_instruction_cache.py +88 -0
- voidx-2.2.0/tests/test_llm_catalog.py +46 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_llm_usage.py +28 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_npm_package.py +6 -6
- voidx-2.2.0/tests/test_output_browse.py +23 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_pure_tui.py +990 -4
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_runtime_intent_classifier.py +25 -0
- voidx-2.2.0/tests/test_skills.py +890 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_ui_events.py +274 -4
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_ui_gateway.py +58 -0
- voidx-2.1.1/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/api-doc.md +0 -64
- voidx-2.1.1/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/prd.md +0 -117
- voidx-2.1.1/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/readme.md +0 -55
- voidx-2.1.1/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/rfc.md +0 -38
- voidx-2.1.1/src/voidx/skills/bundled/superpowers/writing-design-docs/templates/tech-design.md +0 -68
- voidx-2.1.1/src/voidx/skills/runtime.py +0 -90
- voidx-2.1.1/src/voidx/tools/doc_template.py +0 -96
- voidx-2.1.1/src/voidx/ui/tui/terminal_mixin.py +0 -42
- voidx-2.1.1/tests/test_instruction_cache.py +0 -31
- voidx-2.1.1/tests/test_skills.py +0 -444
- {voidx-2.1.1 → voidx-2.2.0}/README.md +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/setup.cfg +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/attachments.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/compaction.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/contracts.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/convergence.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/permissions.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/run_loop.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/runtime.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/session_mixin.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/streaming.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/title_mixin.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/todo_events.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/topology.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/transcript_mixin.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/graph/wiring.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/message_rows.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/code_ide.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/guide.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/handler.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/init.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/lsp.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/mcp.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/model.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/profile.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/runtime.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/session.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/slash/skills.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/state.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/task_state.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/tool_filters.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/agent/tool_messages.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/cli.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/enums.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/models.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/permissions.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_agent.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_api_keys.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_code_ide.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_custom.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_mcp.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_permissions.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_skills.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_utils.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/config/settings_web.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/data/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/data/intent_classifier.json +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/diffing.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/compaction.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/context.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/message_markers.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/llm/provider.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/client.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/config.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/detector.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/detector_data.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/errors.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/manager.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/schema.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/lsp/service.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/main.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/client/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/client/base.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/client/errors.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/client/http_transport.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/client/sse_transport.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/client/stdio_transport.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/manager.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/schema.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp/tool.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp_servers/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/mcp_servers/web.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/memory/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/memory/context_frames.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/memory/model_profiles.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/memory/runtime_state.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/memory/session.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/memory/store.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/memory/transcript.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/context.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/engine.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/evaluate.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/sandbox.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/schema.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/service.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/permission/wildcard.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/runtime/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/runtime/intent.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/runtime/task_state.py +0 -0
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/brainstorming/SKILL.md +0 -0
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/receiving-code-review/SKILL.md +0 -0
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/requesting-code-review/SKILL.md +0 -0
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/systematic-debugging/SKILL.md +0 -0
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/test-driven-development/SKILL.md +0 -0
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/verification-before-completion/SKILL.md +0 -0
- {voidx-2.1.1/src/voidx/skills/bundled/superpowers → voidx-2.2.0/src/voidx/skills/bundled}/writing-plans/SKILL.md +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/skills/schema.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/agent.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/bash.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/clarify.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/file_ops.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/git.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/lsp.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/plan_checkpoint.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/task_status.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/task_tracker.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/todo.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/web_content.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/web_mcp.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/tools/webfetch.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/commands.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/frontend.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/gateway/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/gateway/bootstrap.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/gateway/server.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/gateway/session.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/agent_display.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/console/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/console/app.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/console/formatting.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/console/streaming.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/diff.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/formatting.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/nodes_permission.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/nodes_startup.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/nodes_status.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/state.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/dock/status.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/output/types.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/protocol/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/protocol/commands.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/protocol/envelope.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/protocol/requests.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/protocol/schema.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/protocol/transcript.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/session.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tools/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tools/attachment_tokens.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tools/clipboard_image.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tools/clipboard_text.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tools/code_ide.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tools/file_picker.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/transcript.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/__init__.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/choice_mixin.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/clipboard_mixin.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/helpers.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/input.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/overlays.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx/ui/tui/text_prompt_mixin.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx.egg-info/dependency_links.txt +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx.egg-info/entry_points.txt +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx.egg-info/requires.txt +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/src/voidx.egg-info/top_level.txt +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_clipboard_image.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_clipboard_text.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_code_ide.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_compaction.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_config.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_intent_classifier_phase_a.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_llm_provider.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_lsp.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_main.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_main_startup.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_mcp.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_runtime_ui.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_scrollback_flush.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_startup.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_tree_smoke.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_ui_diff.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_ui_frontend_protocol.py +0 -0
- {voidx-2.1.1 → voidx-2.2.0}/tests/test_ui_session_changes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "voidx"
|
|
3
|
-
version = "2.
|
|
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,7 @@ where = ["src"]
|
|
|
44
44
|
|
|
45
45
|
[tool.setuptools.package-data]
|
|
46
46
|
"voidx.skills" = [
|
|
47
|
-
"bundled
|
|
48
|
-
"bundled/superpowers/*/templates/*.md",
|
|
47
|
+
"bundled/*/SKILL.md",
|
|
49
48
|
]
|
|
50
49
|
"voidx.data" = [
|
|
51
50
|
"intent_classifier.json",
|
|
@@ -50,12 +50,12 @@ BASE_SYSTEM_PROMPT = """You are voidx, a coding agent that lives in the terminal
|
|
|
50
50
|
|
|
51
51
|
## Workflow Skills
|
|
52
52
|
|
|
53
|
-
- voidx
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-
|
|
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.
|
|
59
59
|
"""
|
|
60
60
|
|
|
61
61
|
|
|
@@ -339,7 +339,7 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
|
|
|
339
339
|
when_to_use="Default agent for all user interactions. Always use first.",
|
|
340
340
|
tools=[
|
|
341
341
|
"on_intent", "clarify", "plan_checkpoint",
|
|
342
|
-
"read", "glob", "grep", "bash", "agent", "task_status", "todo",
|
|
342
|
+
"read", "glob", "grep", "bash", "agent", "task_status", "todo", "load_skills",
|
|
343
343
|
"webfetch", "websearch", "repo_map",
|
|
344
344
|
"lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
|
|
345
345
|
"write", "edit", "apply_patch", "lsp_format",
|
|
@@ -358,7 +358,7 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
|
|
|
358
358
|
"or answer 'how does X work' questions. Specify thoroughness: "
|
|
359
359
|
"'quick' for basic, 'medium' for moderate, 'very thorough' for exhaustive.",
|
|
360
360
|
tools=[
|
|
361
|
-
"read", "glob", "grep", "webfetch", "websearch", "repo_map",
|
|
361
|
+
"read", "glob", "grep", "load_skills", "webfetch", "websearch", "repo_map",
|
|
362
362
|
"lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
|
|
363
363
|
],
|
|
364
364
|
can_write=False,
|
|
@@ -374,7 +374,7 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
|
|
|
374
374
|
when_to_use="Use for design/architecture questions, before complex implementations, "
|
|
375
375
|
"or when the user asks for a plan/approach/solution design.",
|
|
376
376
|
tools=[
|
|
377
|
-
"read", "glob", "grep", "webfetch", "websearch", "repo_map",
|
|
377
|
+
"read", "glob", "grep", "load_skills", "webfetch", "websearch", "repo_map",
|
|
378
378
|
"lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
|
|
379
379
|
],
|
|
380
380
|
can_write=False,
|
|
@@ -388,7 +388,7 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
|
|
|
388
388
|
when_to_use="Use for all code writing, file editing, refactoring, bug fixing, "
|
|
389
389
|
"and bash execution. Give complete, self-contained task descriptions.",
|
|
390
390
|
tools=[
|
|
391
|
-
"read", "write", "edit", "apply_patch", "glob", "grep", "bash", "todo", "repo_map",
|
|
391
|
+
"read", "write", "edit", "apply_patch", "glob", "grep", "bash", "todo", "load_skills", "repo_map",
|
|
392
392
|
"lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
|
|
393
393
|
"lsp_format",
|
|
394
394
|
],
|
|
@@ -404,7 +404,7 @@ BUILTIN_AGENTS: dict[str, AgentDef] = {
|
|
|
404
404
|
when_to_use="ALWAYS invoke after implement finishes non-trivial work. "
|
|
405
405
|
"Use to verify correctness before reporting completion to the user.",
|
|
406
406
|
tools=[
|
|
407
|
-
"read", "glob", "grep", "bash", "webfetch", "websearch", "repo_map",
|
|
407
|
+
"read", "glob", "grep", "bash", "load_skills", "webfetch", "websearch", "repo_map",
|
|
408
408
|
"lsp_diagnostics", "lsp_symbols", "lsp_definition", "lsp_references",
|
|
409
409
|
],
|
|
410
410
|
can_write=False,
|
|
@@ -59,6 +59,7 @@ from voidx.agent.graph.wiring import (
|
|
|
59
59
|
)
|
|
60
60
|
from voidx.agent.state import AgentState
|
|
61
61
|
from voidx.agent.graph.streaming import extract_text, stream_llm as _stream_llm
|
|
62
|
+
from voidx.agent.graph.subagent import _task_intent_for_agent as _subagent_task_intent_for_agent
|
|
62
63
|
from voidx.agent.graph.subagent import run_subagent as _run_subagent
|
|
63
64
|
from voidx.agent.graph.title_mixin import GraphTitleMixin
|
|
64
65
|
from voidx.agent.graph.tool_execution import GraphToolExecutionMixin
|
|
@@ -117,6 +118,20 @@ def _merge_skill_runs(*groups: list[SkillRunState | dict]) -> list[SkillRunState
|
|
|
117
118
|
return list(merged.values())
|
|
118
119
|
|
|
119
120
|
|
|
121
|
+
def _skill_names(group: list[SkillRunState | dict]) -> list[str]:
|
|
122
|
+
names: list[str] = []
|
|
123
|
+
for item in group:
|
|
124
|
+
if isinstance(item, SkillRunState):
|
|
125
|
+
name = item.name
|
|
126
|
+
elif isinstance(item, dict):
|
|
127
|
+
name = item.get("name", "")
|
|
128
|
+
else:
|
|
129
|
+
name = ""
|
|
130
|
+
if isinstance(name, str) and name.strip():
|
|
131
|
+
names.append(name.strip())
|
|
132
|
+
return names
|
|
133
|
+
|
|
134
|
+
|
|
120
135
|
class VoidXGraph(
|
|
121
136
|
GraphTitleMixin,
|
|
122
137
|
GraphRunLoopMixin,
|
|
@@ -147,6 +162,7 @@ class VoidXGraph(
|
|
|
147
162
|
|
|
148
163
|
self._interaction_mode: InteractionMode = InteractionMode.AUTO
|
|
149
164
|
self._debug: bool = False
|
|
165
|
+
self._instruction.set_debug(self._debug)
|
|
150
166
|
ui.set_debug(self._debug)
|
|
151
167
|
|
|
152
168
|
self._file_mtimes: dict[str, float] = {}
|
|
@@ -372,6 +388,16 @@ class VoidXGraph(
|
|
|
372
388
|
self._next_agent_id += 1
|
|
373
389
|
parent_tool_call_id = _current_parent_tool_call_id.get()
|
|
374
390
|
started_at = time.monotonic()
|
|
391
|
+
interaction_mode = InteractionMode.PLAN.value if agent_def.name == "plan" else InteractionMode.AUTO.value
|
|
392
|
+
task_intent = _subagent_task_intent_for_agent(agent_def.name)
|
|
393
|
+
skill_runtime_context = await self._instruction.skill_context_for(
|
|
394
|
+
description,
|
|
395
|
+
agent=agent_def.name,
|
|
396
|
+
task_intent=task_intent,
|
|
397
|
+
interaction_mode=interaction_mode,
|
|
398
|
+
scope=description,
|
|
399
|
+
turn_count=getattr(getattr(self, "_task_run", None), "turn_count", 0),
|
|
400
|
+
)
|
|
375
401
|
|
|
376
402
|
async def authorize(calls, agent_name: str):
|
|
377
403
|
return await self._authorize_tool_calls(
|
|
@@ -403,8 +429,8 @@ class VoidXGraph(
|
|
|
403
429
|
"session_id": session_id if self._session else None,
|
|
404
430
|
"usage_stats": self._usage_stats,
|
|
405
431
|
"lsp_manager": getattr(self, "_lsp_manager", None),
|
|
406
|
-
"skill_selection": self._settings.get_skill_selection() if self._settings else None,
|
|
407
432
|
"parent_tools": self.tools,
|
|
433
|
+
"skill_runtime_context": skill_runtime_context,
|
|
408
434
|
}
|
|
409
435
|
if self._current_tree and self._turn_node:
|
|
410
436
|
kwargs.update({
|
|
@@ -435,6 +461,7 @@ class VoidXGraph(
|
|
|
435
461
|
|
|
436
462
|
def set_debug(self, value: bool) -> None:
|
|
437
463
|
self._debug = value
|
|
464
|
+
self._instruction.set_debug(value)
|
|
438
465
|
ui.set_debug(value)
|
|
439
466
|
|
|
440
467
|
def _apply_max_steps_override(self, agent_def: AgentDef) -> AgentDef:
|
|
@@ -477,6 +504,7 @@ class VoidXGraph(
|
|
|
477
504
|
interaction_mode=interaction_mode,
|
|
478
505
|
scope=pending_approval_scope(state.get("pending_approval")) or state.get("goal") or current_user_text,
|
|
479
506
|
turn_count=state.get("goal_turn_count", 0),
|
|
507
|
+
exclude_names=_skill_names(state.get("skill_runs", []) or []),
|
|
480
508
|
)
|
|
481
509
|
skill_runs = _merge_skill_runs(
|
|
482
510
|
_restored_skill_runs(getattr(self, "_task_run", None)),
|
|
@@ -497,7 +525,7 @@ class VoidXGraph(
|
|
|
497
525
|
agent=agent_name,
|
|
498
526
|
interaction_mode=interaction_mode,
|
|
499
527
|
instructions=instructions,
|
|
500
|
-
|
|
528
|
+
skill_context_content=skill_context.content,
|
|
501
529
|
skill_runs=skill_runs,
|
|
502
530
|
active_skill_summaries=skill_context.active,
|
|
503
531
|
summary=summary,
|
|
@@ -26,6 +26,7 @@ from voidx.agent.tool_messages import sanitize_tool_message_content
|
|
|
26
26
|
from voidx.agent.tool_filters import filter_unavailable_lsp_tools
|
|
27
27
|
from voidx.config import Config
|
|
28
28
|
from voidx.llm.provider import create_chat_model, resolve_protocol
|
|
29
|
+
from voidx.llm.instruction import SkillRuntimeContext
|
|
29
30
|
from voidx.llm.usage import (
|
|
30
31
|
UsageStats,
|
|
31
32
|
estimate_context_tokens,
|
|
@@ -33,9 +34,6 @@ from voidx.llm.usage import (
|
|
|
33
34
|
extract_token_usage,
|
|
34
35
|
)
|
|
35
36
|
from voidx.memory.context_frames import save_context_frame_from_messages
|
|
36
|
-
from voidx.skills.registry import SkillRegistry
|
|
37
|
-
from voidx.skills.runtime import SkillRunState
|
|
38
|
-
from voidx.skills.service import SkillService
|
|
39
37
|
from voidx.tools.base import ToolContext
|
|
40
38
|
from voidx.tools.registry import ToolRegistry
|
|
41
39
|
from voidx.tools.task_tracker import TaskTracker
|
|
@@ -59,8 +57,8 @@ async def run_subagent(
|
|
|
59
57
|
session_id: str | None = None,
|
|
60
58
|
usage_stats: UsageStats | None = None,
|
|
61
59
|
lsp_manager=None,
|
|
62
|
-
skill_selection=None,
|
|
63
60
|
parent_tools: ToolRegistry | None = None,
|
|
61
|
+
skill_runtime_context: SkillRuntimeContext | None = None,
|
|
64
62
|
) -> str:
|
|
65
63
|
"""Run a child agent. Child messages are appended to sub_messages
|
|
66
64
|
(when provided) so the caller can place them after ToolMessages."""
|
|
@@ -140,16 +138,7 @@ async def run_subagent(
|
|
|
140
138
|
interaction_mode = InteractionMode.PLAN.value if agent_def.name == "plan" else InteractionMode.AUTO.value
|
|
141
139
|
mode_prompt = PLAN_MODE_APPEND if InteractionMode.parse(interaction_mode) == InteractionMode.PLAN else ""
|
|
142
140
|
task_intent = _task_intent_for_agent(agent_def.name)
|
|
143
|
-
|
|
144
|
-
SkillRegistry(config.workspace),
|
|
145
|
-
selection=skill_selection,
|
|
146
|
-
)
|
|
147
|
-
skill_matches = skill_service.select(
|
|
148
|
-
task_description,
|
|
149
|
-
agent=agent_def.name,
|
|
150
|
-
task_intent=task_intent,
|
|
151
|
-
interaction_mode=interaction_mode,
|
|
152
|
-
)
|
|
141
|
+
skills = skill_runtime_context or SkillRuntimeContext(instructions=[], active=[], content="", runs=[])
|
|
153
142
|
context_cache = ContextCompilerCache()
|
|
154
143
|
context, context_cache = RuntimeContextBuilder(
|
|
155
144
|
config=context_config,
|
|
@@ -160,16 +149,9 @@ async def run_subagent(
|
|
|
160
149
|
tool_contract=agent_def.tool_contract,
|
|
161
150
|
agent=agent_def.name,
|
|
162
151
|
interaction_mode=interaction_mode,
|
|
163
|
-
|
|
164
|
-
skill_runs=
|
|
165
|
-
|
|
166
|
-
match,
|
|
167
|
-
phase="design" if interaction_mode == InteractionMode.PLAN.value else task_intent,
|
|
168
|
-
scope=task_description,
|
|
169
|
-
)
|
|
170
|
-
for match in skill_matches
|
|
171
|
-
],
|
|
172
|
-
active_skill_summaries=[f"{match.name} ({match.reason})" for match in skill_matches],
|
|
152
|
+
skill_context_content=skills.content,
|
|
153
|
+
skill_runs=skills.runs,
|
|
154
|
+
active_skill_summaries=skills.active,
|
|
173
155
|
current_user_text=task_description,
|
|
174
156
|
task_intent=task_intent,
|
|
175
157
|
session_date=datetime.now().astimezone().strftime("%Y-%m-%d %Z"),
|
|
@@ -14,6 +14,7 @@ from voidx.agent.graph.runtime import current_parent_tool_call_id, ui
|
|
|
14
14
|
from voidx.agent.graph.todo_events import todo_updated_event
|
|
15
15
|
from voidx.agent.task_state import ToolStatePatch
|
|
16
16
|
from voidx.agent.tool_messages import sanitize_tool_message_content
|
|
17
|
+
from voidx.skills.runtime import SkillRunState
|
|
17
18
|
from voidx.tools.base import ToolContext, UserInteraction, UserResponse
|
|
18
19
|
from voidx.runtime.ui import (
|
|
19
20
|
FileChangeAppended,
|
|
@@ -69,6 +70,7 @@ class GraphToolExecutionMixin:
|
|
|
69
70
|
pending_approval=_dump_pending_approval(state.get("pending_approval")),
|
|
70
71
|
goal=state.get("goal", ""),
|
|
71
72
|
goal_turn_count=state.get("goal_turn_count", 0),
|
|
73
|
+
active_skill_names=_active_skill_names(state.get("skill_runs", []) or []),
|
|
72
74
|
file_mtimes=self._file_mtimes,
|
|
73
75
|
mcp_manager=getattr(self, "_mcp_manager", None),
|
|
74
76
|
lsp_manager=getattr(self, "_lsp_manager", None),
|
|
@@ -287,7 +289,10 @@ class GraphToolExecutionMixin:
|
|
|
287
289
|
]
|
|
288
290
|
deferred_msgs = [_deferred_message(tc, ctx.workspace) for tc in deferred_for_barrier]
|
|
289
291
|
|
|
290
|
-
state_update = _state_update_from_executed_tools(
|
|
292
|
+
state_update = _state_update_from_executed_tools(
|
|
293
|
+
executed,
|
|
294
|
+
current_skill_runs=state.get("skill_runs", []) or [],
|
|
295
|
+
)
|
|
291
296
|
return {
|
|
292
297
|
"messages": [item.message for item in executed] + extra + denied_msgs + deferred_msgs,
|
|
293
298
|
**state_update,
|
|
@@ -306,8 +311,14 @@ class GraphToolExecutionMixin:
|
|
|
306
311
|
return True
|
|
307
312
|
|
|
308
313
|
|
|
309
|
-
def _state_update_from_executed_tools(
|
|
314
|
+
def _state_update_from_executed_tools(
|
|
315
|
+
executed: list[_ExecutedTool],
|
|
316
|
+
*,
|
|
317
|
+
current_skill_runs: object = (),
|
|
318
|
+
) -> dict:
|
|
310
319
|
update: dict = {}
|
|
320
|
+
merged_skill_runs = _merge_skill_runs_for_state(current_skill_runs)
|
|
321
|
+
skill_runs_changed = False
|
|
311
322
|
for item in executed:
|
|
312
323
|
metadata = getattr(item.result, "metadata", {}) or {}
|
|
313
324
|
raw = metadata.get("state_patch")
|
|
@@ -325,12 +336,31 @@ def _state_update_from_executed_tools(executed: list[_ExecutedTool]) -> dict:
|
|
|
325
336
|
elif field == "pending_approval":
|
|
326
337
|
update["pending_approval"] = data.get(field)
|
|
327
338
|
elif field == "skill_runs":
|
|
328
|
-
|
|
339
|
+
merged_skill_runs = _merge_skill_runs_for_state(
|
|
340
|
+
merged_skill_runs,
|
|
341
|
+
patch.skill_runs,
|
|
342
|
+
)
|
|
343
|
+
skill_runs_changed = True
|
|
329
344
|
else:
|
|
330
345
|
update[field] = data.get(field)
|
|
346
|
+
if skill_runs_changed:
|
|
347
|
+
update["skill_runs"] = merged_skill_runs
|
|
331
348
|
return update
|
|
332
349
|
|
|
333
350
|
|
|
351
|
+
def _merge_skill_runs_for_state(*groups: object) -> list[SkillRunState]:
|
|
352
|
+
merged: dict[str, SkillRunState] = {}
|
|
353
|
+
for group in groups:
|
|
354
|
+
items = group.values() if isinstance(group, dict) else group or []
|
|
355
|
+
for item in items:
|
|
356
|
+
try:
|
|
357
|
+
run = item if isinstance(item, SkillRunState) else SkillRunState.model_validate(item)
|
|
358
|
+
except (TypeError, ValueError):
|
|
359
|
+
continue
|
|
360
|
+
merged[run.name] = run
|
|
361
|
+
return list(merged.values())
|
|
362
|
+
|
|
363
|
+
|
|
334
364
|
def _parallel_subagent_limit(config) -> int:
|
|
335
365
|
parallel = getattr(config, "parallel_subagents", None)
|
|
336
366
|
if not bool(getattr(parallel, "enabled", False)):
|
|
@@ -430,3 +460,16 @@ def _dump_pending_approval(value: object | None) -> dict | None:
|
|
|
430
460
|
if hasattr(value, "model_dump"):
|
|
431
461
|
return value.model_dump(mode="json")
|
|
432
462
|
return None
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def _active_skill_names(value: object) -> list[str]:
|
|
466
|
+
names: list[str] = []
|
|
467
|
+
items = value.values() if isinstance(value, dict) else value or []
|
|
468
|
+
for item in items:
|
|
469
|
+
if isinstance(item, dict):
|
|
470
|
+
name = item.get("name")
|
|
471
|
+
else:
|
|
472
|
+
name = getattr(item, "name", None)
|
|
473
|
+
if isinstance(name, str) and name.strip():
|
|
474
|
+
names.append(name.strip())
|
|
475
|
+
return names
|
|
@@ -31,6 +31,8 @@ from voidx.runtime.ui import (
|
|
|
31
31
|
InputSet,
|
|
32
32
|
StatusFinished,
|
|
33
33
|
StatusUpdated,
|
|
34
|
+
TodoCleared,
|
|
35
|
+
TodoCommitted,
|
|
34
36
|
TurnStarted,
|
|
35
37
|
WarningAppended,
|
|
36
38
|
dock,
|
|
@@ -73,9 +75,7 @@ class GraphTurnMixin:
|
|
|
73
75
|
display_text: str | None = None,
|
|
74
76
|
) -> None:
|
|
75
77
|
t_turn_start = time.monotonic()
|
|
76
|
-
|
|
77
|
-
t_turn_in_start = self._usage_stats.total_input_tokens
|
|
78
|
-
t_turn_out_start = self._usage_stats.total_output_tokens
|
|
78
|
+
self._usage_stats.begin_turn()
|
|
79
79
|
user_message_id: int | None = None
|
|
80
80
|
try:
|
|
81
81
|
session_tracker.begin_turn(self._workspace)
|
|
@@ -339,13 +339,11 @@ class GraphTurnMixin:
|
|
|
339
339
|
scheduler = getattr(self, "_schedule_session_title_generation", None)
|
|
340
340
|
if callable(scheduler):
|
|
341
341
|
scheduler(self._session.id, title_source, title)
|
|
342
|
-
await self._persist_transcript_snapshot()
|
|
343
|
-
|
|
344
342
|
elapsed = time.monotonic() - t_turn_start
|
|
345
343
|
stats = self._usage_stats
|
|
346
|
-
turn_calls = stats.
|
|
347
|
-
turn_in = stats.
|
|
348
|
-
turn_out = stats.
|
|
344
|
+
turn_calls = stats.turn_calls
|
|
345
|
+
turn_in = stats.turn_input_tokens
|
|
346
|
+
turn_out = stats.turn_output_tokens
|
|
349
347
|
from voidx.llm.usage import format_token_count
|
|
350
348
|
dock.append_message(
|
|
351
349
|
f"[dim]✻ {elapsed:.0f}s[/dim]"
|
|
@@ -361,6 +359,13 @@ class GraphTurnMixin:
|
|
|
361
359
|
"\n".join(change_lines),
|
|
362
360
|
markup=True,
|
|
363
361
|
)
|
|
362
|
+
if via_events():
|
|
363
|
+
await ui_events.emit(TodoCommitted())
|
|
364
|
+
await ui_events.drain()
|
|
365
|
+
else:
|
|
366
|
+
dock.commit_todo_state()
|
|
367
|
+
if self._session:
|
|
368
|
+
await self._persist_transcript_snapshot()
|
|
364
369
|
except (KeyboardInterrupt, asyncio.CancelledError):
|
|
365
370
|
if self._session is not None and user_message_id is not None:
|
|
366
371
|
await delete_messages_from(self._session.id, user_message_id)
|
|
@@ -378,6 +383,7 @@ class GraphTurnMixin:
|
|
|
378
383
|
}
|
|
379
384
|
raise
|
|
380
385
|
finally:
|
|
386
|
+
self._usage_stats.end_turn()
|
|
381
387
|
pending_guidance = getattr(self, "_pending_guidance", None)
|
|
382
388
|
if pending_guidance is not None:
|
|
383
389
|
if pending_guidance:
|
|
@@ -392,9 +398,11 @@ class GraphTurnMixin:
|
|
|
392
398
|
await ui_events.emit(StatusFinished(status_id="turn:analyzing"))
|
|
393
399
|
await ui_events.emit(StatusFinished(status_id="agent:-1:progress"))
|
|
394
400
|
await ui_events.emit(StatusFinished(status_id="compaction"))
|
|
401
|
+
await ui_events.emit(TodoCleared())
|
|
395
402
|
await ui_events.emit(InputSet(text="", hints=[]))
|
|
396
403
|
await ui_events.drain()
|
|
397
404
|
else:
|
|
405
|
+
dock.clear_todo_state()
|
|
398
406
|
dock.set_input("", [])
|
|
399
407
|
|
|
400
408
|
|
|
@@ -72,7 +72,6 @@ def refine_intent(
|
|
|
72
72
|
intent_refined=True,
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
-
service = _skill_service(config, settings)
|
|
76
75
|
return OnIntentResult(
|
|
77
76
|
confirmed_intent=confirmed,
|
|
78
77
|
confidence=inp.confidence,
|
|
@@ -82,7 +81,6 @@ def refine_intent(
|
|
|
82
81
|
available_tool_ids=available_tool_ids,
|
|
83
82
|
needs_user_confirmation=needs_confirmation,
|
|
84
83
|
state_patch=patch,
|
|
85
|
-
skill_instructions=[service.render_instruction(match.skill) for match in matches],
|
|
86
84
|
)
|
|
87
85
|
|
|
88
86
|
|
|
@@ -136,13 +134,14 @@ def _available_tools_for_intent(
|
|
|
136
134
|
"lsp_definition",
|
|
137
135
|
"lsp_references",
|
|
138
136
|
"task_status",
|
|
137
|
+
"load_skills",
|
|
139
138
|
}
|
|
140
139
|
planning_tools = read_tools | {"agent", "todo", "bash"}
|
|
141
140
|
implementation_tools = set(agent_tools)
|
|
142
141
|
review_tools = read_tools | {"agent", "todo", "bash"}
|
|
143
142
|
|
|
144
143
|
if intent in {TaskIntent.CHAT, TaskIntent.AMBIGUOUS}:
|
|
145
|
-
desired =
|
|
144
|
+
desired = {"load_skills"}
|
|
146
145
|
elif intent == TaskIntent.INSPECT:
|
|
147
146
|
desired = read_tools
|
|
148
147
|
elif intent == TaskIntent.DESIGN:
|
|
@@ -205,14 +204,17 @@ def _skill_matches(
|
|
|
205
204
|
agent=ctx.agent,
|
|
206
205
|
task_intent=intent.value,
|
|
207
206
|
interaction_mode=ctx.interaction_mode,
|
|
207
|
+
scopes=("bundled",),
|
|
208
|
+
exclude_names=ctx.active_skill_names,
|
|
208
209
|
)
|
|
209
210
|
seen = {normalize_skill_name(match.name) for match in matches}
|
|
211
|
+
excluded = {normalize_skill_name(name) for name in ctx.active_skill_names}
|
|
210
212
|
for name in inp.suggested_skills:
|
|
211
213
|
normalized = normalize_skill_name(name)
|
|
212
|
-
if normalized in seen:
|
|
214
|
+
if normalized in seen or normalized in excluded:
|
|
213
215
|
continue
|
|
214
216
|
skill = service.get(normalized)
|
|
215
|
-
if skill is None or not service.is_enabled(skill):
|
|
217
|
+
if skill is None or skill.meta.scope != "bundled" or not service.is_enabled(skill):
|
|
216
218
|
continue
|
|
217
219
|
matches.append(SkillMatch(skill=skill, reason="suggested"))
|
|
218
220
|
seen.add(normalized)
|
|
@@ -9,12 +9,18 @@ import json
|
|
|
9
9
|
import platform
|
|
10
10
|
from typing import Any, Iterable
|
|
11
11
|
|
|
12
|
-
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
|
|
12
|
+
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage, ToolMessage
|
|
13
13
|
from pydantic import BaseModel, ConfigDict, Field
|
|
14
14
|
|
|
15
15
|
from voidx.agent.message_rows import RowMessageCacheEntry
|
|
16
16
|
from voidx.config import ApprovalReviewer, Config, UserProfile
|
|
17
17
|
from voidx.runtime.intent import InteractionMode, TaskIntent, infer_task_intent
|
|
18
|
+
from voidx.skills.context import (
|
|
19
|
+
has_skill_tool_context,
|
|
20
|
+
is_skill_context_content,
|
|
21
|
+
skill_context_cache_key,
|
|
22
|
+
strip_skill_tool_context,
|
|
23
|
+
)
|
|
18
24
|
from voidx.skills.runtime import SkillRunState
|
|
19
25
|
|
|
20
26
|
_CONTEXT_MARKER = "VOIDX_RUNTIME_CONTEXT"
|
|
@@ -26,6 +32,9 @@ class ContextCompilerCache:
|
|
|
26
32
|
stable_prefix_key: str = ""
|
|
27
33
|
stable_system_content: str = ""
|
|
28
34
|
stable_system_message: SystemMessage | None = None
|
|
35
|
+
skill_context_key: str = ""
|
|
36
|
+
skill_context_content: str = ""
|
|
37
|
+
skill_context_message: HumanMessage | None = None
|
|
29
38
|
row_messages: dict[int, RowMessageCacheEntry] = dataclass_field(default_factory=dict)
|
|
30
39
|
|
|
31
40
|
|
|
@@ -68,11 +77,15 @@ class RuntimeContext(BaseModel):
|
|
|
68
77
|
|
|
69
78
|
sections: list[ContextSection]
|
|
70
79
|
task_sections: list[ContextSection] = Field(default_factory=list)
|
|
80
|
+
skill_context_content: str = ""
|
|
71
81
|
system_content: str | None = None
|
|
72
82
|
system_message: SystemMessage | None = Field(default=None, exclude=True)
|
|
83
|
+
skill_context_message: HumanMessage | None = Field(default=None, exclude=True)
|
|
73
84
|
|
|
74
85
|
def section_names(self) -> list[str]:
|
|
75
86
|
names = [section.name for section in self.sections]
|
|
87
|
+
if self.skill_context_content:
|
|
88
|
+
names.append("Skill Context")
|
|
76
89
|
names.extend(section.name for section in self.task_sections)
|
|
77
90
|
return names
|
|
78
91
|
|
|
@@ -97,6 +110,10 @@ class ContextCompiler:
|
|
|
97
110
|
def compile_messages(self, messages: list[BaseMessage]) -> list[BaseMessage]:
|
|
98
111
|
semantic_messages = raw_semantic_messages(messages)
|
|
99
112
|
current_user_index = _last_user_index(semantic_messages)
|
|
113
|
+
semantic_messages = _strip_historical_tool_skill_context(
|
|
114
|
+
semantic_messages,
|
|
115
|
+
current_user_index,
|
|
116
|
+
)
|
|
100
117
|
|
|
101
118
|
system_content = self.context.render_system()
|
|
102
119
|
cached_system = self.context.system_message
|
|
@@ -113,11 +130,23 @@ class ContextCompiler:
|
|
|
113
130
|
current = semantic_messages[current_user_index]
|
|
114
131
|
semantic_messages[current_user_index] = _prepend_task_context(current, task_context)
|
|
115
132
|
|
|
116
|
-
|
|
133
|
+
skill_context_message = self._skill_context_message()
|
|
134
|
+
if skill_context_message is None:
|
|
135
|
+
return [prefix, *semantic_messages]
|
|
136
|
+
return [prefix, skill_context_message, *semantic_messages]
|
|
117
137
|
|
|
118
138
|
def apply_to_messages(self, messages: list[BaseMessage]) -> None:
|
|
119
139
|
messages[:] = self.compile_messages(messages)
|
|
120
140
|
|
|
141
|
+
def _skill_context_message(self) -> HumanMessage | None:
|
|
142
|
+
content = self.context.skill_context_content.strip()
|
|
143
|
+
if not content:
|
|
144
|
+
return None
|
|
145
|
+
cached = self.context.skill_context_message
|
|
146
|
+
if cached is not None and cached.content == content:
|
|
147
|
+
return cached
|
|
148
|
+
return HumanMessage(content=content)
|
|
149
|
+
|
|
121
150
|
|
|
122
151
|
class RuntimeContextBuilder:
|
|
123
152
|
def __init__(
|
|
@@ -133,7 +162,7 @@ class RuntimeContextBuilder:
|
|
|
133
162
|
agent: str,
|
|
134
163
|
interaction_mode: str | InteractionMode,
|
|
135
164
|
instructions: Iterable[str] = (),
|
|
136
|
-
|
|
165
|
+
skill_context_content: str = "",
|
|
137
166
|
skill_runs: Iterable[SkillRunState] = (),
|
|
138
167
|
active_skill_summaries: Iterable[str] = (),
|
|
139
168
|
summary: str | None = None,
|
|
@@ -162,7 +191,7 @@ class RuntimeContextBuilder:
|
|
|
162
191
|
self.agent = agent
|
|
163
192
|
self.interaction_mode = InteractionMode.parse(interaction_mode)
|
|
164
193
|
self.instructions = [item for item in instructions if item.strip()]
|
|
165
|
-
self.
|
|
194
|
+
self.skill_context_content = skill_context_content.strip()
|
|
166
195
|
self.skill_runs = list(skill_runs)
|
|
167
196
|
self.active_skill_summaries = [item for item in active_skill_summaries if item.strip()]
|
|
168
197
|
self.summary = summary.strip() if summary else ""
|
|
@@ -192,6 +221,12 @@ class RuntimeContextBuilder:
|
|
|
192
221
|
return RuntimeContext(
|
|
193
222
|
sections=self._build_stable_sections(),
|
|
194
223
|
task_sections=self._build_task_sections(),
|
|
224
|
+
skill_context_content=self.skill_context_content,
|
|
225
|
+
skill_context_message=(
|
|
226
|
+
HumanMessage(content=self.skill_context_content)
|
|
227
|
+
if self.skill_context_content
|
|
228
|
+
else None
|
|
229
|
+
),
|
|
195
230
|
)
|
|
196
231
|
|
|
197
232
|
def build_incremental(
|
|
@@ -213,13 +248,38 @@ class RuntimeContextBuilder:
|
|
|
213
248
|
cache.stable_system_content = system_content
|
|
214
249
|
cache.stable_system_message = system_message
|
|
215
250
|
|
|
251
|
+
skill_context_content, skill_context_message = self._incremental_skill_context(cache)
|
|
252
|
+
|
|
216
253
|
return RuntimeContext(
|
|
217
254
|
sections=sections,
|
|
218
255
|
task_sections=self._build_task_sections(),
|
|
256
|
+
skill_context_content=skill_context_content,
|
|
219
257
|
system_content=system_content,
|
|
220
258
|
system_message=system_message,
|
|
259
|
+
skill_context_message=skill_context_message,
|
|
221
260
|
), cache
|
|
222
261
|
|
|
262
|
+
def _incremental_skill_context(
|
|
263
|
+
self,
|
|
264
|
+
cache: ContextCompilerCache,
|
|
265
|
+
) -> tuple[str, HumanMessage | None]:
|
|
266
|
+
content = self.skill_context_content.strip()
|
|
267
|
+
if not content:
|
|
268
|
+
cache.skill_context_key = ""
|
|
269
|
+
cache.skill_context_content = ""
|
|
270
|
+
cache.skill_context_message = None
|
|
271
|
+
return "", None
|
|
272
|
+
|
|
273
|
+
key = skill_context_cache_key(content)
|
|
274
|
+
if cache.skill_context_key == key and cache.skill_context_content:
|
|
275
|
+
return cache.skill_context_content, cache.skill_context_message
|
|
276
|
+
|
|
277
|
+
message = HumanMessage(content=content)
|
|
278
|
+
cache.skill_context_key = key
|
|
279
|
+
cache.skill_context_content = content
|
|
280
|
+
cache.skill_context_message = message
|
|
281
|
+
return content, message
|
|
282
|
+
|
|
223
283
|
def _build_stable_sections(self) -> list[ContextSection]:
|
|
224
284
|
sections = [
|
|
225
285
|
ContextSection(name="Base System", content=self.base_system_prompt),
|
|
@@ -266,11 +326,6 @@ class RuntimeContextBuilder:
|
|
|
266
326
|
ContextSection(name="Runtime State", content=_render_envelope(envelope)),
|
|
267
327
|
ContextSection(name="Current DateTime", content=self.current_datetime),
|
|
268
328
|
]
|
|
269
|
-
if self.skill_instructions:
|
|
270
|
-
task_sections.append(ContextSection(
|
|
271
|
-
name="Active Skills",
|
|
272
|
-
content="\n\n".join(self.skill_instructions),
|
|
273
|
-
))
|
|
274
329
|
task_sections.append(ContextSection(
|
|
275
330
|
name="Current Task State",
|
|
276
331
|
content=self._current_task_state(),
|
|
@@ -352,12 +407,33 @@ def raw_semantic_messages(messages: list[BaseMessage]) -> list[BaseMessage]:
|
|
|
352
407
|
if isinstance(message, SystemMessage):
|
|
353
408
|
continue
|
|
354
409
|
if isinstance(message, HumanMessage):
|
|
410
|
+
if is_skill_context_content(message.content):
|
|
411
|
+
continue
|
|
355
412
|
raw.append(_strip_turn_overlay(message))
|
|
356
413
|
else:
|
|
357
414
|
raw.append(message)
|
|
358
415
|
return raw
|
|
359
416
|
|
|
360
417
|
|
|
418
|
+
def _strip_historical_tool_skill_context(
|
|
419
|
+
messages: list[BaseMessage],
|
|
420
|
+
current_user_index: int | None,
|
|
421
|
+
) -> list[BaseMessage]:
|
|
422
|
+
cutoff = current_user_index if current_user_index is not None else len(messages)
|
|
423
|
+
stripped: list[BaseMessage] = []
|
|
424
|
+
for index, message in enumerate(messages):
|
|
425
|
+
if index < cutoff and isinstance(message, ToolMessage):
|
|
426
|
+
if not has_skill_tool_context(message.content):
|
|
427
|
+
stripped.append(message)
|
|
428
|
+
continue
|
|
429
|
+
content = strip_skill_tool_context(message.content)
|
|
430
|
+
if content != message.content:
|
|
431
|
+
stripped.append(message.model_copy(update={"content": content}))
|
|
432
|
+
continue
|
|
433
|
+
stripped.append(message)
|
|
434
|
+
return stripped
|
|
435
|
+
|
|
436
|
+
|
|
361
437
|
def _strip_turn_overlay(message: HumanMessage) -> HumanMessage:
|
|
362
438
|
content = message.content
|
|
363
439
|
if isinstance(content, str):
|