voidx 2.3.0__tar.gz → 3.0.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.3.0 → voidx-3.0.0}/PKG-INFO +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/pyproject.toml +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/__init__.py +1 -1
- voidx-3.0.0/src/voidx/agent/agents.py +266 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/attachments.py +2 -1
- voidx-3.0.0/src/voidx/agent/goal_resolver.py +228 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/compaction_coordinator.py +68 -44
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/contracts.py +12 -7
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/convergence.py +2 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/core.py +297 -151
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/permissions.py +53 -28
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/run_loop.py +31 -13
- voidx-3.0.0/src/voidx/agent/graph/runtime_guards.py +465 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/session_runtime.py +20 -65
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/streaming.py +21 -7
- voidx-3.0.0/src/voidx/agent/graph/subagent.py +448 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/title_mixin.py +2 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/todo_events.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/tool_execution.py +0 -1
- voidx-3.0.0/src/voidx/agent/graph/tool_executor.py +1226 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/topology.py +11 -18
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/turn_mixin.py +0 -4
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/turn_runner.py +97 -119
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/wiring.py +2 -9
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/message_rows.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/runtime_context.py +204 -142
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/code_ide.py +1 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/handler.py +13 -18
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/host.py +9 -15
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/init.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/model.py +7 -7
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/session.py +119 -5
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/skills.py +1 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/state.py +3 -16
- voidx-3.0.0/src/voidx/agent/task_state.py +33 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/todo_state.py +81 -18
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/tool_filters.py +2 -2
- voidx-3.0.0/src/voidx/agent/tool_result_storage.py +73 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/__init__.py +0 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/enums.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/models.py +1 -39
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings.py +152 -37
- voidx-3.0.0/src/voidx/config/settings_agent.py +21 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_api_keys.py +3 -11
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_code_ide.py +2 -4
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_custom.py +3 -21
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_mcp.py +23 -19
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_permissions.py +7 -7
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_skills.py +4 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_update.py +3 -7
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_web.py +15 -10
- voidx-3.0.0/src/voidx/data/intent_classifier.json +1 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/compaction.py +14 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/instruction.py +27 -33
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/provider.py +27 -24
- voidx-3.0.0/src/voidx/llm/service.py +32 -0
- voidx-3.0.0/src/voidx/logging/__init__.py +1 -0
- voidx-3.0.0/src/voidx/logging/request_log.py +123 -0
- voidx-3.0.0/src/voidx/memory/cleanup.py +122 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/memory/context_frames.py +80 -20
- voidx-3.0.0/src/voidx/memory/jsonl_store.py +186 -0
- voidx-3.0.0/src/voidx/memory/runtime_state.py +366 -0
- voidx-3.0.0/src/voidx/memory/service.py +99 -0
- voidx-3.0.0/src/voidx/memory/session.py +403 -0
- voidx-3.0.0/src/voidx/memory/store.py +242 -0
- voidx-3.0.0/src/voidx/memory/subagents.py +21 -0
- voidx-3.0.0/src/voidx/memory/transcript.py +495 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/evaluate.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/rules.py +33 -22
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/service.py +13 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/runtime/__init__.py +17 -11
- voidx-3.0.0/src/voidx/runtime/attachments.py +7 -0
- voidx-3.0.0/src/voidx/runtime/intent.py +163 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/runtime/intent_classifier.py +8 -11
- voidx-3.0.0/src/voidx/runtime/reference_tokens.py +9 -0
- voidx-3.0.0/src/voidx/runtime/task_state.py +338 -0
- voidx-3.0.0/src/voidx/runtime/todo.py +9 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/runtime/ui.py +3 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/policy.py +3 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/runtime.py +2 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/schema.py +1 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/service.py +13 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/advance_workflow.py +59 -12
- voidx-3.0.0/src/voidx/tools/agent.py +187 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/base.py +7 -6
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/bash.py +4 -4
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/clarify.py +24 -15
- voidx-3.0.0/src/voidx/tools/compact_context.py +53 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/file_ops.py +32 -8
- voidx-3.0.0/src/voidx/tools/file_state.py +134 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/git.py +1 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/load_doc_template.py +1 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/load_skills.py +1 -0
- voidx-3.0.0/src/voidx/tools/lsp.py +131 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/plan_checkpoint.py +8 -15
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/registry.py +5 -12
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/repomap.py +1 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/search.py +2 -0
- voidx-3.0.0/src/voidx/tools/service.py +32 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/task_status.py +3 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/task_tracker.py +3 -3
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/todo.py +2 -5
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/web_mcp.py +1 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/webfetch.py +1 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/websearch.py +1 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/commands.py +5 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/agent_display.py +2 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/capture.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/console/app.py +40 -8
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/console/formatting.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/console/streaming.py +6 -3
- voidx-3.0.0/src/voidx/ui/output/display_policy.py +133 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/app.py +12 -6
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/formatting.py +38 -17
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/nodes.py +30 -16
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/stream.py +15 -58
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/todo.py +1 -5
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/events/consumers.py +36 -18
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/events/schema.py +11 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/tree.py +186 -18
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/types.py +2 -3
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/session.py +2 -8
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tools/clipboard_image.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tools/skill_picker.py +8 -4
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/transcript.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/app.py +14 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/helpers.py +1 -4
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/panels.py +1 -2
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/render_frame.py +161 -30
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/render_status.py +54 -11
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/state.py +22 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/workflow/__init__.py +10 -4
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/workflow/auto_advance.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/workflow/context.py +7 -9
- voidx-3.0.0/src/voidx/workflow/dag.py +40 -0
- voidx-3.0.0/src/voidx/workflow/nodes.py +417 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/workflow/policy.py +81 -23
- voidx-3.0.0/src/voidx/workflow/reconcile.py +251 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/workflow/render.py +42 -28
- voidx-3.0.0/src/voidx/workflow/route.py +49 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/workflow/runtime.py +142 -135
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/workflow/schema.py +49 -31
- voidx-3.0.0/src/voidx/workflow/service.py +391 -0
- voidx-3.0.0/src/voidx/workflow/types.py +100 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx.egg-info/PKG-INFO +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx.egg-info/SOURCES.txt +26 -4
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_auto_advance.py +3 -3
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_compaction.py +106 -7
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_config.py +245 -28
- voidx-3.0.0/tests/test_dock_formatting.py +48 -0
- voidx-3.0.0/tests/test_goal_resolution_refactor.py +128 -0
- voidx-3.0.0/tests/test_install_sh.py +162 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_intent_classifier_phase_a.py +13 -14
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_lsp.py +37 -5
- voidx-3.0.0/tests/test_module_boundaries.py +242 -0
- voidx-3.0.0/tests/test_runtime_intent_classifier.py +131 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_scrollback_flush.py +2 -2
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_skills.py +315 -59
- voidx-3.0.0/tests/test_streaming_sanitize.py +103 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_tree_smoke.py +30 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_tui_frame_rendering.py +83 -0
- voidx-3.0.0/tests/test_tui_output_tree.py +750 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_tui_paste_handling.py +1 -1
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_tui_status_activity.py +69 -14
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_tui_terminal_panels.py +71 -5
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_ui_events.py +41 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_ui_session_changes.py +0 -49
- voidx-3.0.0/tests/test_workflow_reconcile.py +309 -0
- voidx-2.3.0/src/voidx/agent/agents.py +0 -492
- voidx-2.3.0/src/voidx/agent/graph/subagent.py +0 -284
- voidx-2.3.0/src/voidx/agent/graph/tool_executor.py +0 -640
- voidx-2.3.0/src/voidx/agent/intent_refinement.py +0 -299
- voidx-2.3.0/src/voidx/agent/task_state.py +0 -27
- voidx-2.3.0/src/voidx/config/settings_agent.py +0 -34
- voidx-2.3.0/src/voidx/data/intent_classifier.json +0 -1
- voidx-2.3.0/src/voidx/memory/runtime_state.py +0 -391
- voidx-2.3.0/src/voidx/memory/session.py +0 -298
- voidx-2.3.0/src/voidx/memory/store.py +0 -273
- voidx-2.3.0/src/voidx/memory/transcript.py +0 -132
- voidx-2.3.0/src/voidx/runtime/intent.py +0 -96
- voidx-2.3.0/src/voidx/runtime/task_state.py +0 -376
- voidx-2.3.0/src/voidx/tools/agent.py +0 -110
- voidx-2.3.0/src/voidx/tools/apply_patch.py +0 -423
- voidx-2.3.0/src/voidx/tools/file_state.py +0 -28
- voidx-2.3.0/src/voidx/tools/lsp.py +0 -155
- voidx-2.3.0/src/voidx/tools/on_intent.py +0 -116
- voidx-2.3.0/src/voidx/workflow/dag.py +0 -34
- voidx-2.3.0/src/voidx/workflow/nodes.py +0 -322
- voidx-2.3.0/src/voidx/workflow/service.py +0 -197
- voidx-2.3.0/tests/test_runtime_intent_classifier.py +0 -191
- voidx-2.3.0/tests/test_tui_output_tree.py +0 -301
- {voidx-2.3.0 → voidx-3.0.0}/README.md +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/setup.cfg +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/compaction.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/runtime.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/session_mixin.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/graph/transcript_mixin.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/guide.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/lsp.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/mcp.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/profile.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/runtime.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/slash/upgrade.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/agent/tool_messages.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/cli.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/permissions.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/config/settings_utils.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/data/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/data/templates/api-doc.md +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/data/templates/prd.md +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/data/templates/readme.md +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/data/templates/rfc.md +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/data/templates/tech-design.md +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/diffing.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/catalog.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/context.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/message_markers.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/llm/usage.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/client.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/config.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/detector.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/detector_data.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/errors.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/manager.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/schema.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/lsp/service.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/main.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/client/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/client/base.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/client/errors.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/client/http_transport.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/client/sse_transport.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/client/stdio_transport.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/manager.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/schema.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp/tool.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp_servers/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/mcp_servers/web.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/memory/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/memory/model_profiles.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/context.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/engine.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/sandbox.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/schema.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/permission/wildcard.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/runtime/ui_port.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/selfupdate.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/context.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/references.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/skills/registry.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/tools/web_content.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/frontend.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/gateway/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/gateway/bootstrap.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/gateway/server.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/gateway/session.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/browse.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/console/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/diff.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/agent_placeholder.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/nodes_permission.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/nodes_startup.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/nodes_status.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/state.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/dock/status.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/events/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/output/events/bus.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/protocol/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/protocol/commands.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/protocol/envelope.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/protocol/requests.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/protocol/schema.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/protocol/transcript.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tools/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tools/attachment_tokens.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tools/clipboard_text.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tools/code_ide.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tools/file_picker.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/__init__.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/activity.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/choice_mixin.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/clipboard_mixin.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/input.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/overlays.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/parser.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/render_activity.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/render_input.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/render_todo.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/renderer.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/terminal_mixin.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx/ui/tui/text_prompt_mixin.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx.egg-info/dependency_links.txt +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx.egg-info/entry_points.txt +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx.egg-info/requires.txt +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/src/voidx.egg-info/top_level.txt +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_clipboard_image.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_clipboard_text.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_code_ide.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_instruction_cache.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_llm_catalog.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_llm_provider.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_llm_usage.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_main.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_main_startup.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_mcp.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_npm_package.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_output_browse.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_runtime_ui.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_selfupdate.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_startup.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_tui_input_handling.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_ui_diff.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_ui_frontend_protocol.py +0 -0
- {voidx-2.3.0 → voidx-3.0.0}/tests/test_ui_gateway.py +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Agent definitions — typed config, whenToUse descriptions, prompts.
|
|
2
|
+
|
|
3
|
+
voidx uses one agent identity:
|
|
4
|
+
voidx — primary identity, also used for isolated child runs
|
|
5
|
+
|
|
6
|
+
Runtime personas (coordinate/explore/plan/implement/review) are thinking-mode
|
|
7
|
+
labels, not AgentDef ids.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ── prompts ───────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
BASE_SYSTEM_PROMPT = """You are voidx, a coding agent that lives in the terminal.
|
|
19
|
+
|
|
20
|
+
## Communication Style
|
|
21
|
+
|
|
22
|
+
- **Natural and warm.** Write like a skilled colleague, not a robot.
|
|
23
|
+
Use contractions, vary sentence length, show personality.
|
|
24
|
+
- **Match the user's language.** If the user writes in Chinese, respond in Chinese.
|
|
25
|
+
If they write in English, respond in English. Mirror their tone.
|
|
26
|
+
- **Be concise.** One good sentence beats three mediocre ones. The user can ask
|
|
27
|
+
follow-ups if they want more detail.
|
|
28
|
+
- **Don't explain your internals.** The user doesn't need to know about agents,
|
|
29
|
+
personas, explore/plan/implement/review, or your architecture. Just help them.
|
|
30
|
+
If asked "who are you", say "I'm voidx, a coding assistant" — one sentence max.
|
|
31
|
+
- **Say what you're about to do.** Brief heads-up before searching or editing:
|
|
32
|
+
"Let me check the auth module." — not "I will now delegate to the explore agent."
|
|
33
|
+
- **Summarize results, not process.** After completing work, tell the user what
|
|
34
|
+
changed and where. Don't narrate which agents you used or how many steps it took.
|
|
35
|
+
- **Acknowledge uncertainty.** If you're not sure, say so. "I think it's auth.py:42,
|
|
36
|
+
but let me verify" — not "I have medium confidence in this assessment."
|
|
37
|
+
- **Show progress via todo.** Update the todo list so progress is visible.
|
|
38
|
+
But don't narrate todo updates in your text.
|
|
39
|
+
|
|
40
|
+
## Global Rules
|
|
41
|
+
|
|
42
|
+
- Use tools for facts about the workspace; do not guess file contents.
|
|
43
|
+
- Read before editing. Make minimal, precise changes.
|
|
44
|
+
- Keep user-facing responses concise and focused on outcomes.
|
|
45
|
+
- Do not expose internal persona names unless the user asks about architecture.
|
|
46
|
+
- Never claim work is complete until it has been verified.
|
|
47
|
+
- When Current Task State lists an active workflow gate, that workflow gate takes precedence
|
|
48
|
+
over persona prompts, delegation rules, and the decision flow below.
|
|
49
|
+
|
|
50
|
+
## Workflow Runtime
|
|
51
|
+
|
|
52
|
+
- voidx has a structured workflow runtime.
|
|
53
|
+
- Current Task State is the activation source for this turn's workflow nodes.
|
|
54
|
+
- Workflow Context messages contain structured workflow node definitions as a
|
|
55
|
+
stable reference library. Follow ONLY nodes listed as active in Current Task
|
|
56
|
+
State, unless the user explicitly references another node by name.
|
|
57
|
+
- When a node is not listed as active, its definition is reference only. Do not
|
|
58
|
+
follow its gate, internal workflow steps, or transition instructions.
|
|
59
|
+
- load_skills can return project/global skill bodies for the current turn.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
VOIDX_PROMPT = """## Coordination
|
|
64
|
+
|
|
65
|
+
- Assess before acting.
|
|
66
|
+
- Stay aligned with the user's actual goal.
|
|
67
|
+
- Delegate only when you need to run multiple independent tasks in parallel, or the user explicitly asks for a child agent. Do not delegate single-file reads, simple searches, or straightforward tasks you can do directly.
|
|
68
|
+
- Coordinate the work without exposing internal persona names to the user.
|
|
69
|
+
|
|
70
|
+
## Persona Model
|
|
71
|
+
|
|
72
|
+
voidx has five thinking modes (personas). The active persona is shown in Current Task State.
|
|
73
|
+
Switch persona automatically when entering a workflow node.
|
|
74
|
+
|
|
75
|
+
- **coordinate**: Default. Assess, plan next steps, coordinate work, delegate when parallel speedup is needed.
|
|
76
|
+
- **explore**: Evidence gathering and codebase search. Search broadly, report with concrete paths and lines.
|
|
77
|
+
- **plan**: Design and architecture. Study existing patterns, output structured implementable plans.
|
|
78
|
+
- **implement**: Build and execute. Write minimal precise edits, run tests to verify.
|
|
79
|
+
- **review**: Verify and critique. Check correctness, completeness, style, security. Produce PASS/FAIL verdicts.
|
|
80
|
+
|
|
81
|
+
## Responsibilities
|
|
82
|
+
|
|
83
|
+
- Before acting, assess what's already known and what's still needed.
|
|
84
|
+
- Pick the smallest next action that makes progress toward the goal.
|
|
85
|
+
- Only delegate to a child agent when you have multiple independent tasks or the user asks.
|
|
86
|
+
- Only declare work done after running verification (tests, reads, diagnostics).
|
|
87
|
+
|
|
88
|
+
## Rules
|
|
89
|
+
|
|
90
|
+
- Subagents do not interact with the user.
|
|
91
|
+
- Runtime workflow gates take precedence over persona prompts and delegation rules.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
CHILD_RUN_CONSTRAINTS = """- Follow the runtime persona shown in Current Task State.
|
|
95
|
+
- Execute only the delegated task in this isolated child run.
|
|
96
|
+
- Do not interact with the user directly.
|
|
97
|
+
- Do not start another child agent.
|
|
98
|
+
- If a tool call fails, report the error clearly and attempt an alternative approach if one exists.
|
|
99
|
+
- Structure your final output with clear sections: what you found, what you did, and what remains uncertain.
|
|
100
|
+
- Runtime workflow gates take precedence over persona prompts and delegation rules."""
|
|
101
|
+
|
|
102
|
+
# Plan mode prompt — injected when plan_mode=True
|
|
103
|
+
PLAN_MODE_APPEND = """
|
|
104
|
+
## PLAN MODE ACTIVE
|
|
105
|
+
You are in plan mode. Write/edit tools are BLOCKED at the permission level.
|
|
106
|
+
- You CAN: read, glob, grep, bash (non-destructive commands only; no file writes, installs, or system changes), agent(plan/explore/review)
|
|
107
|
+
- You CANNOT: write, edit, agent(implement), bash (destructive)
|
|
108
|
+
- Focus on analysis, design, and creating structured plans.
|
|
109
|
+
- When ready to implement, tell the user to exit plan mode.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
# ── agent definitions ─────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
class AgentDef(BaseModel):
|
|
115
|
+
"""An agent's complete definition — typed, no loose config."""
|
|
116
|
+
name: str
|
|
117
|
+
description: str
|
|
118
|
+
when_to_use: str
|
|
119
|
+
tools: list[str] # tool IDs this agent can use
|
|
120
|
+
can_write: bool
|
|
121
|
+
can_delegate: bool # can it start child agents via the agent tool?
|
|
122
|
+
hidden: bool = False # hidden from user-facing lists?
|
|
123
|
+
model: str | None = None # None = inherit from parent
|
|
124
|
+
mcp_tools: bool = False # can see registered MCP tools
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def persona_prompt(self) -> str:
|
|
128
|
+
try:
|
|
129
|
+
return PERSONA_PROMPTS[self.name]
|
|
130
|
+
except KeyError as exc:
|
|
131
|
+
raise ValueError(f"No persona prompt registered for agent: {self.name}") from exc
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def tool_contract(self) -> str:
|
|
135
|
+
lines = [
|
|
136
|
+
f"- Agent identity: {self.name}",
|
|
137
|
+
f"- Can write files: {str(self.can_write).lower()}",
|
|
138
|
+
f"- Can start child agents: {str(self.can_delegate).lower()}",
|
|
139
|
+
]
|
|
140
|
+
if self.tools:
|
|
141
|
+
lines.append(f"- Available tools: {', '.join(self.tools)}")
|
|
142
|
+
else:
|
|
143
|
+
lines.append("- Available tools: none")
|
|
144
|
+
if self.mcp_tools:
|
|
145
|
+
lines.append("- MCP tools: available when configured; each call is permission-gated")
|
|
146
|
+
if not self.can_delegate:
|
|
147
|
+
lines.append("- Constraint: this agent must not start another child agent.")
|
|
148
|
+
return "\n".join(lines)
|
|
149
|
+
|
|
150
|
+
def persona_prompt_for_llm(agent: AgentDef, *, parallel_subagents_enabled: bool = False) -> str:
|
|
151
|
+
"""Return the persona prompt with runtime-gated child-agent scheduling rules."""
|
|
152
|
+
|
|
153
|
+
prompt = agent.persona_prompt
|
|
154
|
+
if agent.name != "voidx":
|
|
155
|
+
return prompt
|
|
156
|
+
child_agent_prompt = _parallel_subagents_prompt(
|
|
157
|
+
enabled=parallel_subagents_enabled,
|
|
158
|
+
)
|
|
159
|
+
if not prompt:
|
|
160
|
+
return child_agent_prompt
|
|
161
|
+
return f"{prompt.rstrip()}\n\n{child_agent_prompt}"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
_SCHEDULING_COMMON = """- Each child-agent brief must be complete and self-contained.
|
|
165
|
+
- Keep dependent child-agent work sequential: wait for the result before
|
|
166
|
+
delegating follow-up work that depends on it.
|
|
167
|
+
- Batch independent read/search tools when useful; keep dependent tool work
|
|
168
|
+
sequential."""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _parallel_subagents_prompt(*, enabled: bool) -> str:
|
|
172
|
+
if enabled:
|
|
173
|
+
return (
|
|
174
|
+
"## Child-Agent Scheduling\n\n"
|
|
175
|
+
"- Only start a child agent when you have multiple independent tasks to run in\n"
|
|
176
|
+
" parallel, or the user explicitly requests it. Do not delegate work you can do\n"
|
|
177
|
+
" directly.\n"
|
|
178
|
+
"- For independent child-agent tasks, you may issue multiple `agent` tool calls\n"
|
|
179
|
+
" in one response. They will run concurrently up to the configured limit.\n"
|
|
180
|
+
+ _SCHEDULING_COMMON
|
|
181
|
+
)
|
|
182
|
+
return (
|
|
183
|
+
"## Child-Agent Scheduling\n\n"
|
|
184
|
+
"- Only start a child agent when you have multiple independent tasks to run in\n"
|
|
185
|
+
" parallel, or the user explicitly requests it. Do not delegate work you can do\n"
|
|
186
|
+
" directly.\n"
|
|
187
|
+
"- Delegate at most one child agent in a response. Wait for that result before\n"
|
|
188
|
+
" deciding whether another child agent is needed.\n"
|
|
189
|
+
+ _SCHEDULING_COMMON
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ── built-in agents ────────────────────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
BUILTIN_AGENTS: dict[str, AgentDef] = {
|
|
196
|
+
"voidx": AgentDef(
|
|
197
|
+
name="voidx",
|
|
198
|
+
description="Primary agent. Understands intent, edits small scoped changes directly, "
|
|
199
|
+
"delegates broad work to specialists, reviews results.",
|
|
200
|
+
when_to_use="Default agent for all user interactions. Always use first.",
|
|
201
|
+
tools=[
|
|
202
|
+
"clarify", "plan_checkpoint", "advance_workflow", "compact_context",
|
|
203
|
+
"read", "glob", "grep", "bash", "agent", "task_status", "todo", "load_skills",
|
|
204
|
+
"load_doc_template",
|
|
205
|
+
"webfetch", "websearch", "repo_map",
|
|
206
|
+
"lsp",
|
|
207
|
+
"write", "edit",
|
|
208
|
+
],
|
|
209
|
+
can_write=True,
|
|
210
|
+
can_delegate=True,
|
|
211
|
+
hidden=False,
|
|
212
|
+
mcp_tools=True,
|
|
213
|
+
),
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
PERSONA_PROMPTS = {
|
|
218
|
+
"voidx": VOIDX_PROMPT,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
CHILD_RUN_TOOLS = [
|
|
223
|
+
"read", "write", "edit", "glob", "grep", "bash", "todo", "load_skills", "repo_map",
|
|
224
|
+
"lsp",
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_agent(name: str) -> AgentDef | None:
|
|
229
|
+
return BUILTIN_AGENTS.get(name)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def get_visible_agents() -> list[AgentDef]:
|
|
233
|
+
return [a for a in BUILTIN_AGENTS.values() if not a.hidden]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def get_subagents() -> list[AgentDef]:
|
|
237
|
+
"""Child-run identities voidx can delegate to."""
|
|
238
|
+
agent = get_agent("voidx")
|
|
239
|
+
return [child_run_agent_def(agent)] if agent is not None else []
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def child_run_agent_def(agent: AgentDef) -> AgentDef:
|
|
243
|
+
"""Return the child-run view of the public voidx identity."""
|
|
244
|
+
tools = CHILD_RUN_TOOLS if agent.name == "voidx" else agent.tools
|
|
245
|
+
return agent.model_copy(update={
|
|
246
|
+
"name": "voidx",
|
|
247
|
+
"description": "Isolated child run of voidx with a requested runtime persona.",
|
|
248
|
+
"when_to_use": "Use for delegated child work that benefits from isolated context.",
|
|
249
|
+
"tools": tools,
|
|
250
|
+
"can_delegate": False,
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def child_agent_descriptions_for_llm() -> str:
|
|
255
|
+
"""Generate child-agent descriptions for the agent tool."""
|
|
256
|
+
lines = ["Available child agents and the tools they have access to:"]
|
|
257
|
+
for agent in get_subagents():
|
|
258
|
+
tools_str = ", ".join(agent.tools)
|
|
259
|
+
if agent.mcp_tools:
|
|
260
|
+
tools_str = f"{tools_str}, MCP tools" if tools_str else "MCP tools"
|
|
261
|
+
lines.append(
|
|
262
|
+
f"- {agent.name}: {agent.description}\n"
|
|
263
|
+
f" Tools: {tools_str}\n"
|
|
264
|
+
f" Write access: {agent.can_write}"
|
|
265
|
+
)
|
|
266
|
+
return "\n".join(lines)
|
|
@@ -10,9 +10,10 @@ from dataclasses import dataclass, field
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
+
from voidx.runtime.attachments import MAX_IMAGE_ATTACHMENT_BYTES
|
|
14
|
+
|
|
13
15
|
IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".webp"}
|
|
14
16
|
MAX_TEXT_ATTACHMENT_BYTES = 200_000
|
|
15
|
-
MAX_IMAGE_ATTACHMENT_BYTES = 5_000_000
|
|
16
17
|
MAX_DIR_LISTING_ITEMS = 500
|
|
17
18
|
_DIR_TREE_SKIP = {"__pycache__", ".git", ".hg", ".svn", "node_modules", ".venv", "venv", "dist", "build", ".pytest_cache", ".mypy_cache"}
|
|
18
19
|
_ATTACHMENT_RE = re.compile(r'(?<!\S)(?:@(?:"([^"]+)"|(\S+))|\[image-([^\]]+)\])')
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Runtime-owned structured goal resolution for top-level turns."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
|
10
|
+
|
|
11
|
+
from voidx.runtime.intent import InteractionMode, TaskIntent
|
|
12
|
+
from voidx.runtime.task_state import (
|
|
13
|
+
GoalResolution,
|
|
14
|
+
GoalSpec,
|
|
15
|
+
GoalType,
|
|
16
|
+
IntentResolution,
|
|
17
|
+
PlanResolution,
|
|
18
|
+
TaskState,
|
|
19
|
+
_default_join_for_goal_type,
|
|
20
|
+
_default_leave_for_goal_type,
|
|
21
|
+
)
|
|
22
|
+
from voidx.workflow.dag import DEFAULT_WORKFLOW_DAG
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
GOAL_RESOLVER_TIMEOUT_SECONDS = 20
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def resolve_goal_for_turn(
|
|
29
|
+
*,
|
|
30
|
+
model: Any | None,
|
|
31
|
+
user_text: str,
|
|
32
|
+
interaction_mode: str | InteractionMode | None,
|
|
33
|
+
task_state: TaskState,
|
|
34
|
+
workspace: str,
|
|
35
|
+
session_time: str,
|
|
36
|
+
) -> GoalResolution:
|
|
37
|
+
fallback = GoalResolution(
|
|
38
|
+
intent=IntentResolution(type=TaskIntent.GENERAL, desc=""),
|
|
39
|
+
goal=None,
|
|
40
|
+
plan=None,
|
|
41
|
+
)
|
|
42
|
+
if model is None:
|
|
43
|
+
return _normalize_resolution(fallback, user_text, interaction_mode, task_state)
|
|
44
|
+
|
|
45
|
+
structured = getattr(model, "with_structured_output", None)
|
|
46
|
+
if not callable(structured):
|
|
47
|
+
return _normalize_resolution(fallback, user_text, interaction_mode, task_state)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
runnable = structured(GoalResolution)
|
|
51
|
+
raw = await asyncio.wait_for(
|
|
52
|
+
runnable.ainvoke(_resolver_messages(
|
|
53
|
+
user_text,
|
|
54
|
+
interaction_mode,
|
|
55
|
+
task_state,
|
|
56
|
+
workspace,
|
|
57
|
+
session_time,
|
|
58
|
+
)),
|
|
59
|
+
timeout=GOAL_RESOLVER_TIMEOUT_SECONDS,
|
|
60
|
+
)
|
|
61
|
+
resolution = _coerce_resolution(raw)
|
|
62
|
+
except Exception:
|
|
63
|
+
resolution = None
|
|
64
|
+
|
|
65
|
+
if resolution is None:
|
|
66
|
+
return _normalize_resolution(fallback, user_text, interaction_mode, task_state)
|
|
67
|
+
return _normalize_resolution(resolution, user_text, interaction_mode, task_state)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _resolver_messages(
|
|
71
|
+
user_text: str,
|
|
72
|
+
interaction_mode: str | InteractionMode | None,
|
|
73
|
+
task_state: TaskState,
|
|
74
|
+
workspace: str,
|
|
75
|
+
session_time: str,
|
|
76
|
+
) -> list:
|
|
77
|
+
schema = json.dumps(GoalResolution.model_json_schema(), ensure_ascii=False)
|
|
78
|
+
current_goal = (
|
|
79
|
+
task_state.current_goal.model_dump(mode="json")
|
|
80
|
+
if task_state.current_goal is not None
|
|
81
|
+
else None
|
|
82
|
+
)
|
|
83
|
+
context = {
|
|
84
|
+
"workspace": workspace,
|
|
85
|
+
"session_time": session_time,
|
|
86
|
+
"interaction_mode": InteractionMode.parse(interaction_mode).value,
|
|
87
|
+
"current_intent": task_state.current_intent.value,
|
|
88
|
+
"current_goal": current_goal,
|
|
89
|
+
|
|
90
|
+
"recent_user_texts": task_state.recent_user_texts[-2:],
|
|
91
|
+
"latest_user_text": user_text,
|
|
92
|
+
}
|
|
93
|
+
available_joins = ", ".join(sorted(_ALLOWED_JOIN_NODES))
|
|
94
|
+
system = (
|
|
95
|
+
"You are voidx resolving the current user's goal before normal work begins.\n"
|
|
96
|
+
"Return only structured data matching the GoalResolution schema.\n\n"
|
|
97
|
+
"Rules:\n"
|
|
98
|
+
"- intent.type=general only for non-code, non-workspace conversation.\n"
|
|
99
|
+
"- intent.type=coding for codebase inspection, design, docs, review, debugging, or edits.\n"
|
|
100
|
+
"- Pick exactly one goal.type when intent is coding and a concrete workspace goal exists.\n"
|
|
101
|
+
"- plan.join is the workflow node the agent should enter. Required when goal is set; null when goal is null.\n"
|
|
102
|
+
"- plan.leave is the workflow node after which automatic progression stops. Optional.\n"
|
|
103
|
+
f"- Available join values: {available_joins}.\n"
|
|
104
|
+
"- Choose join based on the user's primary intent:\n"
|
|
105
|
+
" - debug: user reports a bug, error, crash, or unexpected behavior to investigate.\n"
|
|
106
|
+
" - brainstorm: user wants to explore requirements, design a feature, or discuss approach before coding.\n"
|
|
107
|
+
" - design-doc: user asks to write or revise a design/spec/PRD/RFC/API doc.\n"
|
|
108
|
+
" - plan: user asks to turn a spec or requirements into an implementation plan.\n"
|
|
109
|
+
" - tdd: user explicitly asks to implement an already detailed spec or continue an approved implementation.\n"
|
|
110
|
+
" - review: user asks for code review or pre-merge review.\n"
|
|
111
|
+
" - feedback: user provides review feedback or reviewer comments to act on.\n"
|
|
112
|
+
"- If the user's intent does not clearly match any join value, set goal to null and plan to null. The agent will work without workflow constraints.\n"
|
|
113
|
+
"- Do not choose brainstorm when the request already contains an approved or sufficiently detailed spec.\n"
|
|
114
|
+
"- Do not set join or leave based on vague or ambiguous approval.\n"
|
|
115
|
+
"- goal and plan are bound: if goal is set, plan must be set with join; if goal is null, plan must be null.\n"
|
|
116
|
+
f"GoalResolution JSON schema:\n{schema}"
|
|
117
|
+
)
|
|
118
|
+
return [
|
|
119
|
+
SystemMessage(content=system),
|
|
120
|
+
HumanMessage(content=json.dumps(context, ensure_ascii=False, indent=2)),
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
_ALLOWED_JOIN_NODES = {"debug", "brainstorm", "design-doc", "plan", "tdd", "review", "feedback"}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _coerce_resolution(value: object) -> GoalResolution | None:
|
|
128
|
+
if isinstance(value, GoalResolution):
|
|
129
|
+
return value
|
|
130
|
+
if isinstance(value, AIMessage):
|
|
131
|
+
value = value.content
|
|
132
|
+
if isinstance(value, str):
|
|
133
|
+
try:
|
|
134
|
+
value = json.loads(value)
|
|
135
|
+
except json.JSONDecodeError:
|
|
136
|
+
return None
|
|
137
|
+
if isinstance(value, dict) and "parsed" in value:
|
|
138
|
+
return _coerce_resolution(value.get("parsed"))
|
|
139
|
+
if isinstance(value, dict):
|
|
140
|
+
try:
|
|
141
|
+
return GoalResolution.model_validate(value)
|
|
142
|
+
except ValueError:
|
|
143
|
+
return None
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _normalize_resolution(
|
|
148
|
+
resolution: GoalResolution,
|
|
149
|
+
user_text: str,
|
|
150
|
+
interaction_mode: str | InteractionMode | None,
|
|
151
|
+
task_state: TaskState,
|
|
152
|
+
) -> GoalResolution:
|
|
153
|
+
mode = InteractionMode.parse(interaction_mode)
|
|
154
|
+
|
|
155
|
+
# Validate join against resolver entry points, and leave against DAG nodes.
|
|
156
|
+
plan = resolution.plan
|
|
157
|
+
if plan is not None:
|
|
158
|
+
if plan.join and plan.join not in _ALLOWED_JOIN_NODES:
|
|
159
|
+
plan = None
|
|
160
|
+
elif plan.leave and plan.leave not in DEFAULT_WORKFLOW_DAG.nodes:
|
|
161
|
+
plan = PlanResolution(join=plan.join, leave=None)
|
|
162
|
+
|
|
163
|
+
# plan mode: force design goal + brainstorm
|
|
164
|
+
if mode == InteractionMode.PLAN:
|
|
165
|
+
desc = (
|
|
166
|
+
resolution.goal.desc
|
|
167
|
+
if resolution.goal is not None and resolution.goal.desc.strip()
|
|
168
|
+
else user_text
|
|
169
|
+
)
|
|
170
|
+
return GoalResolution(
|
|
171
|
+
intent=IntentResolution(type=TaskIntent.CODING, desc="plan mode forces design goal"),
|
|
172
|
+
goal=GoalSpec(type=GoalType.DESIGN, desc=desc),
|
|
173
|
+
plan=PlanResolution(
|
|
174
|
+
join="brainstorm",
|
|
175
|
+
leave=plan.leave if plan is not None else None,
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# goal mode: keep current_goal unchanged
|
|
180
|
+
if mode == InteractionMode.GOAL and task_state.current_goal is not None:
|
|
181
|
+
current = task_state.current_goal
|
|
182
|
+
goal = GoalSpec(type=current.type, desc=current.desc)
|
|
183
|
+
if plan is None:
|
|
184
|
+
plan = PlanResolution(
|
|
185
|
+
join=_default_join_for_goal_type(current.type),
|
|
186
|
+
leave=_default_leave_for_goal_type(current.type),
|
|
187
|
+
)
|
|
188
|
+
return GoalResolution(
|
|
189
|
+
intent=IntentResolution(
|
|
190
|
+
type=TaskIntent.CODING,
|
|
191
|
+
desc="goal mode keeps the turn scoped to the current goal",
|
|
192
|
+
),
|
|
193
|
+
goal=goal,
|
|
194
|
+
plan=plan,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# general intent: no goal, no plan
|
|
198
|
+
if resolution.intent.type == TaskIntent.GENERAL:
|
|
199
|
+
return GoalResolution(
|
|
200
|
+
intent=resolution.intent,
|
|
201
|
+
goal=None,
|
|
202
|
+
plan=None,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# coding intent: fill default join/leave when needed
|
|
206
|
+
goal = resolution.goal
|
|
207
|
+
if goal is not None:
|
|
208
|
+
if plan is None:
|
|
209
|
+
plan = PlanResolution(
|
|
210
|
+
join=_default_join_for_goal_type(goal.type),
|
|
211
|
+
leave=_default_leave_for_goal_type(goal.type),
|
|
212
|
+
)
|
|
213
|
+
elif not plan.join:
|
|
214
|
+
plan = PlanResolution(
|
|
215
|
+
join=_default_join_for_goal_type(goal.type),
|
|
216
|
+
leave=plan.leave or _default_leave_for_goal_type(goal.type),
|
|
217
|
+
)
|
|
218
|
+
elif not plan.leave:
|
|
219
|
+
plan = PlanResolution(
|
|
220
|
+
join=plan.join,
|
|
221
|
+
leave=_default_leave_for_goal_type(goal.type),
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return GoalResolution(
|
|
225
|
+
intent=resolution.intent,
|
|
226
|
+
goal=goal,
|
|
227
|
+
plan=plan,
|
|
228
|
+
)
|