superqode 0.1.5__py3-none-any.whl
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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- superqode-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Question Tool - Ask User Clarifying Questions.
|
|
3
|
+
|
|
4
|
+
Allows agents to ask the user questions during task execution.
|
|
5
|
+
This enables interactive workflows where agents can:
|
|
6
|
+
- Clarify ambiguous requirements
|
|
7
|
+
- Get user preferences
|
|
8
|
+
- Confirm risky operations
|
|
9
|
+
- Present choices for implementation
|
|
10
|
+
|
|
11
|
+
Features:
|
|
12
|
+
- Multiple choice questions
|
|
13
|
+
- Free-form text input
|
|
14
|
+
- Confirmation dialogs
|
|
15
|
+
- Rating/ranking questions
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import asyncio
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from enum import Enum
|
|
23
|
+
from typing import Any, Callable, Dict, List, Optional, Awaitable
|
|
24
|
+
|
|
25
|
+
from .base import Tool, ToolResult, ToolContext
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class QuestionType(Enum):
|
|
29
|
+
"""Type of question to ask."""
|
|
30
|
+
|
|
31
|
+
TEXT = "text" # Free-form text input
|
|
32
|
+
CHOICE = "choice" # Single selection from options
|
|
33
|
+
MULTI_CHOICE = "multi_choice" # Multiple selection from options
|
|
34
|
+
CONFIRM = "confirm" # Yes/no confirmation
|
|
35
|
+
RATING = "rating" # Rating on a scale
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class Question:
|
|
40
|
+
"""A question to ask the user."""
|
|
41
|
+
|
|
42
|
+
question: str
|
|
43
|
+
question_type: QuestionType = QuestionType.TEXT
|
|
44
|
+
options: List[str] = field(default_factory=list)
|
|
45
|
+
default: Optional[str] = None
|
|
46
|
+
allow_custom: bool = True
|
|
47
|
+
min_rating: int = 1
|
|
48
|
+
max_rating: int = 5
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class Answer:
|
|
53
|
+
"""An answer from the user."""
|
|
54
|
+
|
|
55
|
+
value: Any
|
|
56
|
+
custom: bool = False
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Global question handler - set by the UI
|
|
60
|
+
_question_handler: Optional[Callable[[Question], Awaitable[Answer]]] = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def set_question_handler(handler: Optional[Callable[[Question], Awaitable[Answer]]]) -> None:
|
|
64
|
+
"""Set the global question handler for UI integration."""
|
|
65
|
+
global _question_handler
|
|
66
|
+
_question_handler = handler
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_question_handler() -> Optional[Callable[[Question], Awaitable[Answer]]]:
|
|
70
|
+
"""Get the current question handler."""
|
|
71
|
+
return _question_handler
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class QuestionTool(Tool):
|
|
75
|
+
"""
|
|
76
|
+
Ask the user a question during execution.
|
|
77
|
+
|
|
78
|
+
Allows agents to:
|
|
79
|
+
- Get clarification on requirements
|
|
80
|
+
- Present implementation choices
|
|
81
|
+
- Confirm risky operations
|
|
82
|
+
- Gather user preferences
|
|
83
|
+
|
|
84
|
+
The question is presented through the UI and execution
|
|
85
|
+
pauses until the user responds.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def name(self) -> str:
|
|
90
|
+
return "ask_user"
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def description(self) -> str:
|
|
94
|
+
return """Ask the user a clarifying question.
|
|
95
|
+
|
|
96
|
+
Use this when you need:
|
|
97
|
+
- Clarification on ambiguous requirements
|
|
98
|
+
- User choice between implementation options
|
|
99
|
+
- Confirmation before risky operations
|
|
100
|
+
- User preferences for configuration
|
|
101
|
+
|
|
102
|
+
The question is shown to the user and execution pauses until they respond.
|
|
103
|
+
Use sparingly - prefer to make reasonable assumptions when possible."""
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def parameters(self) -> Dict[str, Any]:
|
|
107
|
+
return {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"properties": {
|
|
110
|
+
"question": {"type": "string", "description": "The question to ask the user"},
|
|
111
|
+
"type": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"enum": ["text", "choice", "multi_choice", "confirm", "rating"],
|
|
114
|
+
"description": "Question type: text (free input), choice (single), multi_choice (multiple), confirm (yes/no), rating",
|
|
115
|
+
},
|
|
116
|
+
"options": {
|
|
117
|
+
"type": "array",
|
|
118
|
+
"items": {"type": "string"},
|
|
119
|
+
"description": "Options for choice/multi_choice questions",
|
|
120
|
+
},
|
|
121
|
+
"default": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "Default value if user doesn't provide input",
|
|
124
|
+
},
|
|
125
|
+
"allow_custom": {
|
|
126
|
+
"type": "boolean",
|
|
127
|
+
"description": "Allow custom input in addition to options (default: true)",
|
|
128
|
+
},
|
|
129
|
+
"context": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"description": "Additional context to show with the question",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
"required": ["question"],
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async def execute(self, args: Dict[str, Any], ctx: ToolContext) -> ToolResult:
|
|
138
|
+
question_text = args.get("question", "")
|
|
139
|
+
question_type = args.get("type", "text")
|
|
140
|
+
options = args.get("options", [])
|
|
141
|
+
default = args.get("default")
|
|
142
|
+
allow_custom = args.get("allow_custom", True)
|
|
143
|
+
context = args.get("context", "")
|
|
144
|
+
|
|
145
|
+
if not question_text:
|
|
146
|
+
return ToolResult(success=False, output="", error="Question text is required")
|
|
147
|
+
|
|
148
|
+
# Validate question type
|
|
149
|
+
try:
|
|
150
|
+
q_type = QuestionType(question_type)
|
|
151
|
+
except ValueError:
|
|
152
|
+
return ToolResult(
|
|
153
|
+
success=False, output="", error=f"Invalid question type: {question_type}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Validate options for choice questions
|
|
157
|
+
if q_type in (QuestionType.CHOICE, QuestionType.MULTI_CHOICE):
|
|
158
|
+
if not options:
|
|
159
|
+
return ToolResult(
|
|
160
|
+
success=False, output="", error="Options are required for choice questions"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Create question object
|
|
164
|
+
question = Question(
|
|
165
|
+
question=question_text,
|
|
166
|
+
question_type=q_type,
|
|
167
|
+
options=options,
|
|
168
|
+
default=default,
|
|
169
|
+
allow_custom=allow_custom,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Try to get answer through UI handler
|
|
173
|
+
handler = get_question_handler()
|
|
174
|
+
|
|
175
|
+
if handler:
|
|
176
|
+
try:
|
|
177
|
+
answer = await handler(question)
|
|
178
|
+
return self._format_answer(answer, question, context)
|
|
179
|
+
except asyncio.CancelledError:
|
|
180
|
+
return ToolResult(success=False, output="", error="Question was cancelled")
|
|
181
|
+
except Exception as e:
|
|
182
|
+
return ToolResult(success=False, output="", error=f"Error getting answer: {str(e)}")
|
|
183
|
+
|
|
184
|
+
# Fallback: Use context output callback for simple text display
|
|
185
|
+
if ctx.on_output:
|
|
186
|
+
await ctx.emit_output(f"\n[Question] {question_text}\n")
|
|
187
|
+
if options:
|
|
188
|
+
await ctx.emit_output("Options:\n")
|
|
189
|
+
for i, opt in enumerate(options, 1):
|
|
190
|
+
await ctx.emit_output(f" {i}. {opt}\n")
|
|
191
|
+
if default:
|
|
192
|
+
await ctx.emit_output(f"Default: {default}\n")
|
|
193
|
+
await ctx.emit_output("[Waiting for user response...]\n")
|
|
194
|
+
|
|
195
|
+
# Without a UI handler, use default or return pending
|
|
196
|
+
if default:
|
|
197
|
+
return ToolResult(
|
|
198
|
+
success=True,
|
|
199
|
+
output=f"User response: {default}",
|
|
200
|
+
metadata={"question": question_text, "answer": default, "used_default": True},
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return ToolResult(
|
|
204
|
+
success=False,
|
|
205
|
+
output="",
|
|
206
|
+
error="No question handler available and no default value provided. Run in interactive mode to ask questions.",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def _format_answer(self, answer: Answer, question: Question, context: str) -> ToolResult:
|
|
210
|
+
"""Format the answer as a tool result."""
|
|
211
|
+
if question.question_type == QuestionType.CONFIRM:
|
|
212
|
+
response = "Yes" if answer.value else "No"
|
|
213
|
+
elif question.question_type == QuestionType.MULTI_CHOICE:
|
|
214
|
+
if isinstance(answer.value, list):
|
|
215
|
+
response = ", ".join(answer.value)
|
|
216
|
+
else:
|
|
217
|
+
response = str(answer.value)
|
|
218
|
+
elif question.question_type == QuestionType.RATING:
|
|
219
|
+
response = f"{answer.value}/{question.max_rating}"
|
|
220
|
+
else:
|
|
221
|
+
response = str(answer.value)
|
|
222
|
+
|
|
223
|
+
output = f"User response: {response}"
|
|
224
|
+
if answer.custom:
|
|
225
|
+
output += " (custom input)"
|
|
226
|
+
|
|
227
|
+
return ToolResult(
|
|
228
|
+
success=True,
|
|
229
|
+
output=output,
|
|
230
|
+
metadata={
|
|
231
|
+
"question": question.question,
|
|
232
|
+
"type": question.question_type.value,
|
|
233
|
+
"answer": answer.value,
|
|
234
|
+
"custom": answer.custom,
|
|
235
|
+
},
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class ConfirmTool(Tool):
|
|
240
|
+
"""
|
|
241
|
+
Quick confirmation dialog.
|
|
242
|
+
|
|
243
|
+
Simplified version of ask_user for yes/no confirmations.
|
|
244
|
+
Use for confirming risky or irreversible operations.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def name(self) -> str:
|
|
249
|
+
return "confirm"
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def description(self) -> str:
|
|
253
|
+
return """Ask the user for a yes/no confirmation.
|
|
254
|
+
|
|
255
|
+
Use for:
|
|
256
|
+
- Confirming risky or destructive operations
|
|
257
|
+
- Verifying important decisions
|
|
258
|
+
- Getting go-ahead for changes
|
|
259
|
+
|
|
260
|
+
Returns 'confirmed' if user says yes, 'denied' if no."""
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def parameters(self) -> Dict[str, Any]:
|
|
264
|
+
return {
|
|
265
|
+
"type": "object",
|
|
266
|
+
"properties": {
|
|
267
|
+
"message": {"type": "string", "description": "What to confirm"},
|
|
268
|
+
"details": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"description": "Additional details about what will happen",
|
|
271
|
+
},
|
|
272
|
+
"default": {
|
|
273
|
+
"type": "boolean",
|
|
274
|
+
"description": "Default if user doesn't respond (default: false for safety)",
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
"required": ["message"],
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async def execute(self, args: Dict[str, Any], ctx: ToolContext) -> ToolResult:
|
|
281
|
+
message = args.get("message", "")
|
|
282
|
+
details = args.get("details", "")
|
|
283
|
+
default = args.get("default", False)
|
|
284
|
+
|
|
285
|
+
if not message:
|
|
286
|
+
return ToolResult(success=False, output="", error="Confirmation message is required")
|
|
287
|
+
|
|
288
|
+
# Create confirmation question
|
|
289
|
+
question = Question(
|
|
290
|
+
question=message,
|
|
291
|
+
question_type=QuestionType.CONFIRM,
|
|
292
|
+
options=["Yes", "No"],
|
|
293
|
+
default="No" if not default else "Yes",
|
|
294
|
+
allow_custom=False,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
handler = get_question_handler()
|
|
298
|
+
|
|
299
|
+
if handler:
|
|
300
|
+
try:
|
|
301
|
+
answer = await handler(question)
|
|
302
|
+
confirmed = bool(answer.value)
|
|
303
|
+
|
|
304
|
+
return ToolResult(
|
|
305
|
+
success=True,
|
|
306
|
+
output="confirmed" if confirmed else "denied",
|
|
307
|
+
metadata={"message": message, "confirmed": confirmed},
|
|
308
|
+
)
|
|
309
|
+
except Exception as e:
|
|
310
|
+
return ToolResult(success=False, output="", error=f"Confirmation failed: {str(e)}")
|
|
311
|
+
|
|
312
|
+
# Fallback with context output
|
|
313
|
+
if ctx.on_output:
|
|
314
|
+
await ctx.emit_output(f"\n[Confirm] {message}\n")
|
|
315
|
+
if details:
|
|
316
|
+
await ctx.emit_output(f"Details: {details}\n")
|
|
317
|
+
await ctx.emit_output("[Waiting for confirmation...]\n")
|
|
318
|
+
|
|
319
|
+
# Use default for safety
|
|
320
|
+
return ToolResult(
|
|
321
|
+
success=True,
|
|
322
|
+
output="denied" if not default else "confirmed",
|
|
323
|
+
metadata={"message": message, "confirmed": default, "used_default": True},
|
|
324
|
+
)
|