illusion-code 0.1.0__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.
- illusion/__init__.py +24 -0
- illusion/__main__.py +15 -0
- illusion/_frontend/dist/index.mjs +39208 -0
- illusion/_frontend/package.json +27 -0
- illusion/_frontend/src/App.tsx +624 -0
- illusion/_frontend/src/components/CommandPicker.tsx +98 -0
- illusion/_frontend/src/components/Composer.tsx +55 -0
- illusion/_frontend/src/components/ComposerController.tsx +128 -0
- illusion/_frontend/src/components/ConversationView.tsx +750 -0
- illusion/_frontend/src/components/Footer.tsx +25 -0
- illusion/_frontend/src/components/MarkdownContent.tsx +537 -0
- illusion/_frontend/src/components/MarkdownTable.tsx +245 -0
- illusion/_frontend/src/components/ModalHost.tsx +425 -0
- illusion/_frontend/src/components/MultilineTextInput.tsx +250 -0
- illusion/_frontend/src/components/PromptInput.tsx +64 -0
- illusion/_frontend/src/components/SelectModal.tsx +78 -0
- illusion/_frontend/src/components/SidePanel.tsx +175 -0
- illusion/_frontend/src/components/Spinner.tsx +77 -0
- illusion/_frontend/src/components/StatusBar.tsx +142 -0
- illusion/_frontend/src/components/SwarmPanel.tsx +141 -0
- illusion/_frontend/src/components/TodoPanel.tsx +126 -0
- illusion/_frontend/src/components/ToolCallDisplay.tsx +202 -0
- illusion/_frontend/src/components/TranscriptPane.tsx +79 -0
- illusion/_frontend/src/components/WelcomeBanner.tsx +37 -0
- illusion/_frontend/src/hooks/useBackendSession.ts +468 -0
- illusion/_frontend/src/hooks/useTerminalSize.ts +9 -0
- illusion/_frontend/src/i18n.ts +78 -0
- illusion/_frontend/src/index.tsx +42 -0
- illusion/_frontend/src/theme/ThemeContext.tsx +19 -0
- illusion/_frontend/src/theme/builtinThemes.ts +89 -0
- illusion/_frontend/src/types.ts +110 -0
- illusion/_frontend/src/utils/markdown.ts +33 -0
- illusion/_frontend/src/utils/thinking.ts +191 -0
- illusion/_frontend/tsconfig.json +13 -0
- illusion/_web_dist/assets/index-BseIw-ik.css +10 -0
- illusion/_web_dist/assets/index-C_0ZWMuW.js +82 -0
- illusion/_web_dist/index.html +16 -0
- illusion/api/__init__.py +36 -0
- illusion/api/client.py +568 -0
- illusion/api/codex_client.py +563 -0
- illusion/api/compat.py +138 -0
- illusion/api/effort.py +128 -0
- illusion/api/errors.py +57 -0
- illusion/api/openai_client.py +819 -0
- illusion/api/provider.py +148 -0
- illusion/api/registry.py +479 -0
- illusion/api/usage.py +45 -0
- illusion/auth/__init__.py +50 -0
- illusion/auth/copilot.py +419 -0
- illusion/auth/external.py +612 -0
- illusion/auth/flows.py +58 -0
- illusion/auth/manager.py +214 -0
- illusion/auth/storage.py +372 -0
- illusion/bridge/__init__.py +38 -0
- illusion/bridge/manager.py +190 -0
- illusion/bridge/session_runner.py +84 -0
- illusion/bridge/types.py +113 -0
- illusion/bridge/work_secret.py +131 -0
- illusion/cli.py +1228 -0
- illusion/commands/__init__.py +32 -0
- illusion/commands/registry.py +1934 -0
- illusion/config/__init__.py +39 -0
- illusion/config/i18n.py +522 -0
- illusion/config/paths.py +259 -0
- illusion/config/settings.py +564 -0
- illusion/coordinator/__init__.py +41 -0
- illusion/coordinator/agent_definitions.py +1093 -0
- illusion/coordinator/coordinator_mode.py +127 -0
- illusion/engine/__init__.py +95 -0
- illusion/engine/cost_tracker.py +55 -0
- illusion/engine/messages.py +369 -0
- illusion/engine/query.py +632 -0
- illusion/engine/query_engine.py +343 -0
- illusion/engine/stream_events.py +169 -0
- illusion/hooks/__init__.py +67 -0
- illusion/hooks/events.py +43 -0
- illusion/hooks/executor.py +397 -0
- illusion/hooks/hot_reload.py +74 -0
- illusion/hooks/loader.py +133 -0
- illusion/hooks/schemas.py +121 -0
- illusion/hooks/types.py +86 -0
- illusion/mcp/__init__.py +104 -0
- illusion/mcp/client.py +377 -0
- illusion/mcp/config.py +140 -0
- illusion/mcp/types.py +175 -0
- illusion/memory/__init__.py +36 -0
- illusion/memory/manager.py +94 -0
- illusion/memory/memdir.py +58 -0
- illusion/memory/paths.py +57 -0
- illusion/memory/scan.py +120 -0
- illusion/memory/search.py +83 -0
- illusion/memory/types.py +43 -0
- illusion/output_styles/__init__.py +15 -0
- illusion/output_styles/loader.py +64 -0
- illusion/permissions/__init__.py +39 -0
- illusion/permissions/checker.py +174 -0
- illusion/permissions/modes.py +38 -0
- illusion/platforms.py +148 -0
- illusion/plugins/__init__.py +71 -0
- illusion/plugins/bundled/__init__.py +0 -0
- illusion/plugins/installer.py +59 -0
- illusion/plugins/loader.py +301 -0
- illusion/plugins/schemas.py +51 -0
- illusion/plugins/types.py +56 -0
- illusion/prompts/__init__.py +29 -0
- illusion/prompts/claudemd.py +74 -0
- illusion/prompts/context.py +187 -0
- illusion/prompts/environment.py +189 -0
- illusion/prompts/system_prompt.py +155 -0
- illusion/py.typed +0 -0
- illusion/sandbox/__init__.py +29 -0
- illusion/sandbox/adapter.py +174 -0
- illusion/services/__init__.py +59 -0
- illusion/services/compact/__init__.py +1015 -0
- illusion/services/cron.py +338 -0
- illusion/services/cron_scheduler.py +715 -0
- illusion/services/file_history.py +258 -0
- illusion/services/lsp/__init__.py +455 -0
- illusion/services/session_storage.py +237 -0
- illusion/services/token_estimation.py +72 -0
- illusion/skills/__init__.py +60 -0
- illusion/skills/bundled/__init__.py +110 -0
- illusion/skills/bundled/content/batch.md +86 -0
- illusion/skills/bundled/content/coding-guidelines.md +70 -0
- illusion/skills/bundled/content/debug.md +38 -0
- illusion/skills/bundled/content/loop.md +82 -0
- illusion/skills/bundled/content/remember.md +105 -0
- illusion/skills/bundled/content/simplify.md +53 -0
- illusion/skills/bundled/content/skillify.md +113 -0
- illusion/skills/bundled/content/stuck.md +54 -0
- illusion/skills/bundled/content/update-config.md +329 -0
- illusion/skills/bundled/content/verify.md +74 -0
- illusion/skills/loader.py +219 -0
- illusion/skills/registry.py +40 -0
- illusion/skills/types.py +24 -0
- illusion/state/__init__.py +18 -0
- illusion/state/app_state.py +67 -0
- illusion/state/store.py +93 -0
- illusion/swarm/__init__.py +71 -0
- illusion/swarm/agent_executor.py +857 -0
- illusion/swarm/in_process.py +259 -0
- illusion/swarm/subprocess_backend.py +136 -0
- illusion/swarm/team_helpers.py +123 -0
- illusion/swarm/types.py +159 -0
- illusion/swarm/worktree.py +347 -0
- illusion/tasks/__init__.py +33 -0
- illusion/tasks/local_agent_task.py +42 -0
- illusion/tasks/local_shell_task.py +27 -0
- illusion/tasks/manager.py +377 -0
- illusion/tasks/stop_task.py +21 -0
- illusion/tasks/types.py +88 -0
- illusion/tools/__init__.py +126 -0
- illusion/tools/agent_tool.py +388 -0
- illusion/tools/ask_user_question_tool.py +186 -0
- illusion/tools/base.py +149 -0
- illusion/tools/bash_tool.py +413 -0
- illusion/tools/config_tool.py +90 -0
- illusion/tools/cron_tool.py +473 -0
- illusion/tools/enter_plan_mode_tool.py +147 -0
- illusion/tools/enter_worktree_tool.py +188 -0
- illusion/tools/exit_plan_mode_tool.py +69 -0
- illusion/tools/exit_worktree_tool.py +225 -0
- illusion/tools/file_edit_tool.py +283 -0
- illusion/tools/file_read_tool.py +294 -0
- illusion/tools/file_write_tool.py +184 -0
- illusion/tools/glob_tool.py +165 -0
- illusion/tools/grep_tool.py +190 -0
- illusion/tools/list_mcp_resources_tool.py +80 -0
- illusion/tools/lsp_tool.py +333 -0
- illusion/tools/mcp_auth_tool.py +100 -0
- illusion/tools/mcp_tool.py +75 -0
- illusion/tools/notebook_edit_tool.py +242 -0
- illusion/tools/powershell_tool.py +334 -0
- illusion/tools/read_mcp_resource_tool.py +63 -0
- illusion/tools/repl_tool.py +100 -0
- illusion/tools/send_message_tool.py +112 -0
- illusion/tools/shell_common.py +187 -0
- illusion/tools/skill_tool.py +86 -0
- illusion/tools/sleep_tool.py +62 -0
- illusion/tools/structured_output_tool.py +58 -0
- illusion/tools/task_create_tool.py +98 -0
- illusion/tools/task_get_tool.py +94 -0
- illusion/tools/task_list_tool.py +94 -0
- illusion/tools/task_output_tool.py +55 -0
- illusion/tools/task_stop_tool.py +52 -0
- illusion/tools/task_update_tool.py +224 -0
- illusion/tools/team_create_tool.py +236 -0
- illusion/tools/team_delete_tool.py +104 -0
- illusion/tools/todo_write_tool.py +198 -0
- illusion/tools/tool_search_tool.py +156 -0
- illusion/tools/web_fetch_tool.py +264 -0
- illusion/tools/web_search_tool.py +186 -0
- illusion/ui/__init__.py +23 -0
- illusion/ui/app.py +258 -0
- illusion/ui/backend_host.py +1180 -0
- illusion/ui/input.py +86 -0
- illusion/ui/output.py +363 -0
- illusion/ui/permission_dialog.py +47 -0
- illusion/ui/permission_store.py +99 -0
- illusion/ui/protocol.py +384 -0
- illusion/ui/react_launcher.py +280 -0
- illusion/ui/runtime.py +787 -0
- illusion/ui/textual_app.py +603 -0
- illusion/ui/web/__init__.py +10 -0
- illusion/ui/web/server.py +87 -0
- illusion/ui/web/ws_host.py +1197 -0
- illusion/utils/__init__.py +0 -0
- illusion/utils/ripgrep.py +299 -0
- illusion/utils/shell.py +248 -0
- illusion_code-0.1.0.dist-info/METADATA +1159 -0
- illusion_code-0.1.0.dist-info/RECORD +214 -0
- illusion_code-0.1.0.dist-info/WHEEL +4 -0
- illusion_code-0.1.0.dist-info/entry_points.txt +2 -0
- illusion_code-0.1.0.dist-info/licenses/LICENSE +21 -0
illusion/tools/base.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""
|
|
2
|
+
工具抽象模块
|
|
3
|
+
============
|
|
4
|
+
|
|
5
|
+
本模块提供 IllusionCode 工具系统的抽象基类和注册表。
|
|
6
|
+
|
|
7
|
+
主要组件:
|
|
8
|
+
- BaseTool: 所有工具的抽象基类
|
|
9
|
+
- ToolExecutionContext: 工具执行的共享上下文
|
|
10
|
+
- ToolResult: 标准化的工具执行结果
|
|
11
|
+
- ToolRegistry: 工具名称到实现的映射
|
|
12
|
+
|
|
13
|
+
使用示例:
|
|
14
|
+
>>> from illusion.tools.base import BaseTool, ToolRegistry, ToolResult
|
|
15
|
+
>>> registry = ToolRegistry()
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from abc import ABC, abstractmethod
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from pydantic import BaseModel
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ToolExecutionContext:
|
|
30
|
+
"""工具调用的共享执行上下文
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
cwd: 当前工作目录
|
|
34
|
+
metadata: 元数据字典
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
cwd: Path
|
|
38
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class ToolResult:
|
|
43
|
+
"""标准化的工具执行结果
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
output: 输出内容
|
|
47
|
+
is_error: 是否为错误
|
|
48
|
+
metadata: 元数据字典
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
output: str
|
|
52
|
+
is_error: bool = False
|
|
53
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class BaseTool(ABC):
|
|
57
|
+
"""所有 IllusionCode 工具的基类
|
|
58
|
+
|
|
59
|
+
Attributes:
|
|
60
|
+
name: 工具名称
|
|
61
|
+
description: 工具描述
|
|
62
|
+
input_model: 输入模型类型
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
name: str
|
|
66
|
+
description: str
|
|
67
|
+
input_model: type[BaseModel]
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
async def execute(self, arguments: BaseModel, context: ToolExecutionContext) -> ToolResult:
|
|
71
|
+
"""执行工具
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
arguments: 输入参数模型
|
|
75
|
+
context: 执行上下文
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
ToolResult: 工具执行结果
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def is_read_only(self, arguments: BaseModel) -> bool:
|
|
82
|
+
"""返回调用是否为只读
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
arguments: 输入参数模型
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
bool: 是否只读
|
|
89
|
+
"""
|
|
90
|
+
del arguments
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
def to_api_schema(self) -> dict[str, Any]:
|
|
94
|
+
"""返回 Anthropic Messages API 期望的工具模式
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
dict[str, Any]: API 工具模式
|
|
98
|
+
"""
|
|
99
|
+
return {
|
|
100
|
+
"name": self.name,
|
|
101
|
+
"description": self.description,
|
|
102
|
+
"input_schema": self.input_model.model_json_schema(),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class ToolRegistry:
|
|
107
|
+
"""工具名称到实现的映射
|
|
108
|
+
|
|
109
|
+
Attributes:
|
|
110
|
+
_tools: 工具字典
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self) -> None:
|
|
114
|
+
self._tools: dict[str, BaseTool] = {}
|
|
115
|
+
|
|
116
|
+
def register(self, tool: BaseTool) -> None:
|
|
117
|
+
"""注册工具实例
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
tool: 工具实例
|
|
121
|
+
"""
|
|
122
|
+
self._tools[tool.name] = tool
|
|
123
|
+
|
|
124
|
+
def get(self, name: str) -> BaseTool | None:
|
|
125
|
+
"""按名称返回已注册的工具
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
name: 工具名称
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
BaseTool | None: 工具或 None
|
|
132
|
+
"""
|
|
133
|
+
return self._tools.get(name)
|
|
134
|
+
|
|
135
|
+
def list_tools(self) -> list[BaseTool]:
|
|
136
|
+
"""返回所有已注册的工具
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
list[BaseTool]: 工具列表
|
|
140
|
+
"""
|
|
141
|
+
return list(self._tools.values())
|
|
142
|
+
|
|
143
|
+
def to_api_schema(self) -> list[dict[str, Any]]:
|
|
144
|
+
"""以 API 格式返回所有工具模式
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
list[dict[str, Any]]: API 工具模式列表
|
|
148
|
+
"""
|
|
149
|
+
return [tool.to_api_schema() for tool in self._tools.values()]
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bash 命令执行工具
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
本模块提供执行 shell 命令并捕获标准输出/错误的功能。
|
|
6
|
+
|
|
7
|
+
主要组件:
|
|
8
|
+
- BashTool: 执行 bash 命令的工具
|
|
9
|
+
|
|
10
|
+
使用示例:
|
|
11
|
+
>>> from illusion.tools import BashTool
|
|
12
|
+
>>> tool = BashTool()
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import os
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel, Field
|
|
22
|
+
|
|
23
|
+
from illusion.platforms import get_platform
|
|
24
|
+
from illusion.sandbox import SandboxUnavailableError
|
|
25
|
+
from illusion.tools.base import BaseTool, ToolExecutionContext, ToolResult
|
|
26
|
+
from illusion.tools.shell_common import CommandExecutor
|
|
27
|
+
from illusion.utils.shell import _resolve_windows_bash, create_shell_subprocess
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class BashToolInput(BaseModel):
|
|
31
|
+
"""Bash 工具参数。
|
|
32
|
+
|
|
33
|
+
属性:
|
|
34
|
+
command: 要执行的 shell 命令
|
|
35
|
+
cwd: 可选的工作目录覆盖
|
|
36
|
+
timeout_ms: 超时毫秒数(1000-600000)
|
|
37
|
+
run_in_background: 是否在后台运行
|
|
38
|
+
dangerouslyDisableSandbox: 是否绕过沙箱
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
command: str = Field(description="Shell command to execute")
|
|
42
|
+
cwd: str | None = Field(default=None, description="Working directory override")
|
|
43
|
+
timeout_ms: int = Field(default=120000, ge=1000, le=600000)
|
|
44
|
+
run_in_background: bool = Field(
|
|
45
|
+
default=False,
|
|
46
|
+
description="Set to true to run this command in the background",
|
|
47
|
+
)
|
|
48
|
+
dangerouslyDisableSandbox: bool = Field(
|
|
49
|
+
default=False,
|
|
50
|
+
description="Set to true to bypass sandbox restrictions for this command",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# 提示词生成(从 claude-code-sourcemap BashTool/prompt.ts 移植)
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
_DEFAULT_TIMEOUT_MS = 120_000 # 默认超时 2 分钟
|
|
59
|
+
_MAX_TIMEOUT_MS = 600_000 # 最大超时 10 分钟
|
|
60
|
+
_MAX_OUTPUT_LENGTH = 30_000 # 最大输出长度
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _get_background_usage_note() -> str | None:
|
|
64
|
+
if os.environ.get("ILLUSION_DISABLE_BACKGROUND_TASKS", "").lower() in ("1", "true"):
|
|
65
|
+
return None
|
|
66
|
+
return (
|
|
67
|
+
"You can use the `run_in_background` parameter to run the command in the background. "
|
|
68
|
+
"Only use this if you don't need the result immediately and are OK being notified when "
|
|
69
|
+
"the command completes later. You do not need to check the output right away - you'll be "
|
|
70
|
+
"notified when it finishes. You do not need to use '&' at the end of the command when "
|
|
71
|
+
"using this parameter."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _get_sleep_guidance() -> str | None:
|
|
76
|
+
if os.environ.get("ILLUSION_DISABLE_BACKGROUND_TASKS", "").lower() in ("1", "true"):
|
|
77
|
+
return None
|
|
78
|
+
return (
|
|
79
|
+
" - Avoid unnecessary `sleep` commands:\n"
|
|
80
|
+
" - Do not sleep between commands that can run immediately — just run them.\n"
|
|
81
|
+
" - If your command is long running and you would like to be notified when it finishes — "
|
|
82
|
+
"use `run_in_background`. No sleep needed.\n"
|
|
83
|
+
" - Do not retry failing commands in a sleep loop — diagnose the root cause.\n"
|
|
84
|
+
" - If waiting for a background task you started with `run_in_background`, you will be "
|
|
85
|
+
"notified when it completes — do not poll.\n"
|
|
86
|
+
" - If you must poll an external process, use a check command (e.g. `gh run view`) "
|
|
87
|
+
"rather than sleeping first.\n"
|
|
88
|
+
" - If you must sleep, keep the duration short (1-5 seconds) to avoid blocking the user."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _get_sandbox_section() -> str:
|
|
93
|
+
"""生成沙箱提示词段(包含实际沙箱配置)。"""
|
|
94
|
+
sandbox_config_text = _build_sandbox_config_text()
|
|
95
|
+
|
|
96
|
+
return f"""\
|
|
97
|
+
## Command sandbox
|
|
98
|
+
|
|
99
|
+
By default, your command will be run in a sandbox. This sandbox controls which directories and network hosts commands may access or modify without an explicit override.
|
|
100
|
+
|
|
101
|
+
{sandbox_config_text}
|
|
102
|
+
- You should always default to running commands within the sandbox. Do NOT attempt to set `dangerouslyDisableSandbox: true` unless:
|
|
103
|
+
- The user *explicitly* asks you to bypass sandbox
|
|
104
|
+
- A specific command just failed and you see evidence of sandbox restrictions causing the failure.
|
|
105
|
+
- Evidence of sandbox-caused failures includes:
|
|
106
|
+
- "Operation not permitted" errors for file/network operations
|
|
107
|
+
- Access denied to specific paths outside allowed directories
|
|
108
|
+
- Network connection failures to non-whitelisted hosts
|
|
109
|
+
- Unix socket connection errors
|
|
110
|
+
- When you see evidence of sandbox-caused failure:
|
|
111
|
+
- Immediately retry with `dangerouslyDisableSandbox: true` (don't ask, just do it)
|
|
112
|
+
- Briefly explain what sandbox restriction likely caused the failure.
|
|
113
|
+
- This will prompt the user for permission
|
|
114
|
+
- Treat each command you execute with `dangerouslyDisableSandbox: true` individually.
|
|
115
|
+
- Do not suggest adding sensitive paths like ~/.bashrc, ~/.zshrc, ~/.ssh/*, or credential files to the sandbox allowlist.
|
|
116
|
+
- For temporary files, always use the `$TMPDIR` environment variable. TMPDIR is automatically set to the correct sandbox-writable directory in sandbox mode. Do NOT use `/tmp` directly - use `$TMPDIR` instead."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _build_sandbox_config_text() -> str:
|
|
120
|
+
"""构建沙箱配置的描述文本,用于提示词中展示。"""
|
|
121
|
+
try:
|
|
122
|
+
from illusion.config import load_settings
|
|
123
|
+
from illusion.sandbox import build_sandbox_runtime_config, get_sandbox_availability
|
|
124
|
+
|
|
125
|
+
settings = load_settings()
|
|
126
|
+
availability = get_sandbox_availability(settings)
|
|
127
|
+
|
|
128
|
+
if not availability.active:
|
|
129
|
+
if settings.sandbox.enabled:
|
|
130
|
+
return (
|
|
131
|
+
f"Sandbox is enabled but not currently available: {availability.reason}\n"
|
|
132
|
+
"All commands will run WITHOUT sandbox restrictions.\n"
|
|
133
|
+
)
|
|
134
|
+
return "Sandbox is disabled. All commands will run WITHOUT sandbox restrictions.\n"
|
|
135
|
+
|
|
136
|
+
config = build_sandbox_runtime_config(settings)
|
|
137
|
+
fs = config.get("filesystem", {})
|
|
138
|
+
net = config.get("network", {})
|
|
139
|
+
parts = ["The sandbox has the following restrictions:"]
|
|
140
|
+
if fs:
|
|
141
|
+
parts.append(f" Filesystem: {_format_sandbox_dict(fs)}")
|
|
142
|
+
if net:
|
|
143
|
+
parts.append(f" Network: {_format_sandbox_dict(net)}")
|
|
144
|
+
return "\n".join(parts) + "\n"
|
|
145
|
+
except Exception:
|
|
146
|
+
return "Sandbox configuration is not available.\n"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _format_sandbox_dict(d: dict) -> str:
|
|
150
|
+
"""格式化沙箱配置字典为简洁文本。"""
|
|
151
|
+
items = []
|
|
152
|
+
for key, values in d.items():
|
|
153
|
+
if isinstance(values, list) and values:
|
|
154
|
+
items.append(f"{key}: {values}")
|
|
155
|
+
return ", ".join(items) if items else "no restrictions"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _get_commit_and_pr_instructions() -> str:
|
|
159
|
+
return """\
|
|
160
|
+
# Committing changes with git
|
|
161
|
+
|
|
162
|
+
Only create commits when requested by the user. If unclear, ask first. When the user asks you to create a new git commit, follow these steps carefully:
|
|
163
|
+
|
|
164
|
+
You can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. The numbered steps below indicate which commands should be batched in parallel.
|
|
165
|
+
|
|
166
|
+
Git Safety Protocol:
|
|
167
|
+
- NEVER update the git config
|
|
168
|
+
- NEVER run destructive git commands (push --force, reset --hard, checkout ., restore ., clean -f, branch -D) unless the user explicitly requests these actions. Taking unauthorized destructive actions is unhelpful and can result in lost work, so it's best to ONLY run these commands when given direct instructions
|
|
169
|
+
- NEVER skip hooks (--no-verify, --no-gpg-sign, etc) unless the user explicitly requests it
|
|
170
|
+
- NEVER run force push to main/master, warn the user if they request it
|
|
171
|
+
- CRITICAL: Always create NEW commits rather than amending, unless the user explicitly requests a git amend. When a pre-commit hook fails, the commit did NOT happen — so --amend would modify the PREVIOUS commit, which may result in destroying work or losing previous changes. Instead, after hook failure, fix the issue, re-stage, and create a NEW commit
|
|
172
|
+
- When staging files, prefer adding specific files by name rather than using "git add -A" or "git add .", which can accidentally include sensitive files (.env, credentials) or large binaries
|
|
173
|
+
- NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive
|
|
174
|
+
|
|
175
|
+
1. Run the following bash commands in parallel, each using the Bash tool:
|
|
176
|
+
- Run a git status command to see all untracked files. IMPORTANT: Never use the -uall flag as it can cause memory issues on large repos.
|
|
177
|
+
- Run a git diff command to see both staged and unstaged changes that will be committed.
|
|
178
|
+
- Run a git log command to see recent commit messages, so that you can follow this repository's commit message style.
|
|
179
|
+
2. Analyze all staged changes (both previously staged and newly added) and draft a commit message:
|
|
180
|
+
- Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.). Ensure the message accurately reflects the changes and their purpose (i.e. "add" means a wholly new feature, "update" means an enhancement to an existing feature, "fix" means a bug fix, etc.).
|
|
181
|
+
- Do not commit files that likely contain secrets (.env, credentials.json, etc). Warn the user if they specifically request to commit those files
|
|
182
|
+
- Draft a concise (1-2 sentences) commit message that focuses on the "why" rather than the "what"
|
|
183
|
+
- Ensure it accurately reflects the changes and their purpose
|
|
184
|
+
3. Run the following commands in parallel:
|
|
185
|
+
- Add relevant untracked files to the staging area.
|
|
186
|
+
- Create the commit.
|
|
187
|
+
- Run git status after the commit completes to verify success.
|
|
188
|
+
Note: git status depends on the commit completing, so run it sequentially after the commit.
|
|
189
|
+
4. If the commit fails due to pre-commit hook: fix the issue and create a NEW commit
|
|
190
|
+
|
|
191
|
+
Important notes:
|
|
192
|
+
- NEVER run additional commands to read or explore code, besides git bash commands
|
|
193
|
+
- NEVER use the TodoWrite or Agent tools
|
|
194
|
+
- DO NOT push to the remote repository unless the user explicitly asks you to do so
|
|
195
|
+
- IMPORTANT: Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported.
|
|
196
|
+
- IMPORTANT: Do not use --no-edit with git rebase commands, as the --no-edit flag is not a valid option for git rebase.
|
|
197
|
+
- If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit
|
|
198
|
+
- In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:
|
|
199
|
+
<example>
|
|
200
|
+
git commit -m "$(cat <<'EOF'
|
|
201
|
+
Commit message here.
|
|
202
|
+
EOF
|
|
203
|
+
)"
|
|
204
|
+
</example>
|
|
205
|
+
|
|
206
|
+
# Creating pull requests
|
|
207
|
+
Use the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed.
|
|
208
|
+
|
|
209
|
+
IMPORTANT: When the user asks you to create a pull request, follow these steps carefully:
|
|
210
|
+
|
|
211
|
+
1. Run the following bash commands in parallel using the Bash tool, in order to understand the current state of the branch since it diverged from the main branch:
|
|
212
|
+
- Run a git status command to see all untracked files (never use -uall flag)
|
|
213
|
+
- Run a git diff command to see both staged and unstaged changes that will be committed
|
|
214
|
+
- Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote
|
|
215
|
+
- Run a git log command and `git diff [base-branch]...HEAD` to understand the full commit history for the current branch (from the time it diverged from the base branch)
|
|
216
|
+
2. Analyze all changes that will be included in the pull request, making sure to look at all relevant commits (NOT just the latest commit, but ALL commits that will be included in the pull request!!!), and draft a pull request title and summary:
|
|
217
|
+
- Keep the PR title short (under 70 characters)
|
|
218
|
+
- Use the description/body for details, not the title
|
|
219
|
+
3. Run the following commands in parallel:
|
|
220
|
+
- Create new branch if needed
|
|
221
|
+
- Push to remote with -u flag if needed
|
|
222
|
+
- Create PR using gh pr create with the format below. Use a HEREDOC to pass the body to ensure correct formatting.
|
|
223
|
+
<example>
|
|
224
|
+
gh pr create --title "the pr title" --body "$(cat <<'EOF'
|
|
225
|
+
## Summary
|
|
226
|
+
<1-3 bullet points>
|
|
227
|
+
|
|
228
|
+
## Test plan
|
|
229
|
+
[Bulleted markdown checklist of TODOs for testing the pull request...]
|
|
230
|
+
EOF
|
|
231
|
+
)"
|
|
232
|
+
</example>
|
|
233
|
+
|
|
234
|
+
Important:
|
|
235
|
+
- DO NOT use the TodoWrite or Agent tools
|
|
236
|
+
- Return the PR URL when you're done, so the user can see it
|
|
237
|
+
|
|
238
|
+
# Other common operations
|
|
239
|
+
- View comments on a Github PR: gh api repos/foo/bar/pulls/123/comments"""
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _build_bash_description() -> str:
|
|
243
|
+
background_note = _get_background_usage_note()
|
|
244
|
+
|
|
245
|
+
tool_preference_items = [
|
|
246
|
+
"File search: Use Glob (NOT find or ls)",
|
|
247
|
+
"Content search: Use Grep (NOT grep or rg)",
|
|
248
|
+
"Read files: Use Read (NOT cat/head/tail)",
|
|
249
|
+
"Edit files: Use Edit (NOT sed/awk)",
|
|
250
|
+
"Write files: Use Write (NOT echo >/cat <<EOF)",
|
|
251
|
+
"Communication: Output text directly (NOT echo/printf)",
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
avoid_commands = "`find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo`"
|
|
255
|
+
|
|
256
|
+
multiple_commands_subitems = (
|
|
257
|
+
'If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. '
|
|
258
|
+
'Example: if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.\n'
|
|
259
|
+
"If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together.\n"
|
|
260
|
+
"Use ';' only when you need to run commands sequentially but don't care if earlier commands fail.\n"
|
|
261
|
+
"DO NOT use newlines to separate commands (newlines are ok in quoted strings)."
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
git_subitems = (
|
|
265
|
+
"Prefer to create a new commit rather than amending an existing commit.\n"
|
|
266
|
+
"Before running destructive operations (e.g., git reset --hard, git push --force, git checkout --), "
|
|
267
|
+
"consider whether there is a safer alternative that achieves the same goal. Only use destructive operations "
|
|
268
|
+
"when they are truly the best approach.\n"
|
|
269
|
+
"Never skip hooks (--no-verify) or bypass signing (--no-gpg-sign, -c commit.gpgsign=false) unless the "
|
|
270
|
+
"user has explicitly asked for it. If a hook fails, investigate and fix the underlying issue."
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
sleep_subitems = (
|
|
274
|
+
"Do not sleep between commands that can run immediately — just run them.\n"
|
|
275
|
+
"If your command is long running and you would like to be notified when it finishes — "
|
|
276
|
+
"use `run_in_background`. No sleep needed.\n"
|
|
277
|
+
"Do not retry failing commands in a sleep loop — diagnose the root cause.\n"
|
|
278
|
+
"If waiting for a background task you started with `run_in_background`, you will be notified "
|
|
279
|
+
"when it completes — do not poll.\n"
|
|
280
|
+
"If you must poll an external process, use a check command (e.g. `gh run view`) rather than "
|
|
281
|
+
"sleeping first.\n"
|
|
282
|
+
"If you must sleep, keep the duration short (1-5 seconds) to avoid blocking the user."
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
instruction_items = [
|
|
286
|
+
"If your command will create new directories or files, first use this tool to run `ls` to verify the parent directory exists and is the correct location.",
|
|
287
|
+
'Always quote file paths that contain spaces with double quotes in your command (e.g., cd "path with spaces/file.txt")',
|
|
288
|
+
"Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly requests it.",
|
|
289
|
+
f"You may specify an optional timeout in milliseconds (up to {_MAX_TIMEOUT_MS}ms / {_MAX_TIMEOUT_MS // 60000} minutes). "
|
|
290
|
+
f"By default, your command will timeout after {_DEFAULT_TIMEOUT_MS}ms ({_DEFAULT_TIMEOUT_MS // 60000} minutes).",
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
if background_note is not None:
|
|
294
|
+
instruction_items.append(background_note)
|
|
295
|
+
|
|
296
|
+
instruction_items.extend([
|
|
297
|
+
"When issuing multiple commands:",
|
|
298
|
+
multiple_commands_subitems,
|
|
299
|
+
"For git commands:",
|
|
300
|
+
git_subitems,
|
|
301
|
+
"Avoid unnecessary `sleep` commands:",
|
|
302
|
+
sleep_subitems,
|
|
303
|
+
])
|
|
304
|
+
|
|
305
|
+
# Build tool preference bullets
|
|
306
|
+
preference_bullets = "\n".join(f" - {item}" for item in tool_preference_items)
|
|
307
|
+
|
|
308
|
+
# Build instruction bullets
|
|
309
|
+
instruction_lines: list[str] = []
|
|
310
|
+
for item in instruction_items:
|
|
311
|
+
if "\n" in item:
|
|
312
|
+
# Multi-line sub-items get their own indented block
|
|
313
|
+
for line in item.split("\n"):
|
|
314
|
+
instruction_lines.append(f" - {line}" if not line.startswith(" ") else f" {line}")
|
|
315
|
+
else:
|
|
316
|
+
instruction_lines.append(f" - {item}")
|
|
317
|
+
|
|
318
|
+
sections = [
|
|
319
|
+
"Executes a given bash command and returns its output.",
|
|
320
|
+
"",
|
|
321
|
+
"The working directory persists between commands, but shell state does not. "
|
|
322
|
+
"The shell environment is initialized from the user's profile (bash or zsh).",
|
|
323
|
+
"",
|
|
324
|
+
f"IMPORTANT: Avoid using this tool to run {avoid_commands} commands, unless explicitly "
|
|
325
|
+
"instructed or after you have verified that a dedicated tool cannot accomplish your task. "
|
|
326
|
+
"Instead, use the appropriate dedicated tool as this will provide a much better experience for the user:",
|
|
327
|
+
"",
|
|
328
|
+
preference_bullets,
|
|
329
|
+
"While the Bash tool can do similar things, it's better to use the built-in tools as they "
|
|
330
|
+
"provide a better user experience and make it easier to review tool calls and give permission.",
|
|
331
|
+
"",
|
|
332
|
+
"# Instructions",
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
for line in instruction_lines:
|
|
336
|
+
sections.append(line)
|
|
337
|
+
|
|
338
|
+
sections.append(_get_sandbox_section())
|
|
339
|
+
sections.append("")
|
|
340
|
+
sections.append(_get_commit_and_pr_instructions())
|
|
341
|
+
|
|
342
|
+
return "\n".join(sections)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class BashTool(BaseTool):
|
|
346
|
+
"""执行 shell 命令并捕获标准输出/错误。
|
|
347
|
+
|
|
348
|
+
用于执行终端操作,如 git、npm、docker 等命令。
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
name = "bash"
|
|
352
|
+
description = _build_bash_description()
|
|
353
|
+
input_model = BashToolInput
|
|
354
|
+
|
|
355
|
+
async def execute(self, arguments: BashToolInput, context: ToolExecutionContext) -> ToolResult:
|
|
356
|
+
# 检查 Windows 平台上的 bash 可用性
|
|
357
|
+
if get_platform() == "windows":
|
|
358
|
+
bash_path = _resolve_windows_bash()
|
|
359
|
+
if not bash_path:
|
|
360
|
+
return ToolResult(
|
|
361
|
+
output=(
|
|
362
|
+
"Bash is not available on this Windows machine. "
|
|
363
|
+
"Install Git for Windows or set ILLUSION_CODE_GIT_BASH_PATH, "
|
|
364
|
+
"or use the powershell tool for command execution."
|
|
365
|
+
),
|
|
366
|
+
is_error=True,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# 解析工作目录
|
|
370
|
+
cwd = Path(arguments.cwd).expanduser() if arguments.cwd else context.cwd
|
|
371
|
+
try:
|
|
372
|
+
# 创建 shell 子进程
|
|
373
|
+
process = await create_shell_subprocess(
|
|
374
|
+
arguments.command,
|
|
375
|
+
cwd=cwd,
|
|
376
|
+
disable_sandbox=arguments.dangerouslyDisableSandbox,
|
|
377
|
+
stdin=asyncio.subprocess.DEVNULL, # 防止 Windows 上的句柄继承死锁
|
|
378
|
+
stdout=asyncio.subprocess.PIPE,
|
|
379
|
+
stderr=asyncio.subprocess.PIPE,
|
|
380
|
+
)
|
|
381
|
+
except SandboxUnavailableError as exc:
|
|
382
|
+
return ToolResult(output=str(exc), is_error=True)
|
|
383
|
+
|
|
384
|
+
# 后台运行模式
|
|
385
|
+
if arguments.run_in_background:
|
|
386
|
+
async def _background_wait():
|
|
387
|
+
try:
|
|
388
|
+
# 必须消费 stdout/stderr,避免管道缓冲区满导致进程挂起
|
|
389
|
+
stdout_task = asyncio.create_task(process.stdout.read())
|
|
390
|
+
stderr_task = asyncio.create_task(process.stderr.read())
|
|
391
|
+
await process.wait()
|
|
392
|
+
stdout_task.cancel()
|
|
393
|
+
stderr_task.cancel()
|
|
394
|
+
except Exception:
|
|
395
|
+
pass
|
|
396
|
+
|
|
397
|
+
asyncio.create_task(_background_wait(), name=f"bash-bg-{process.pid}")
|
|
398
|
+
return ToolResult(
|
|
399
|
+
output=f"Command launched in background (pid={process.pid})",
|
|
400
|
+
is_error=False,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# 执行命令并归一化结果
|
|
404
|
+
timeout_seconds = arguments.timeout_ms // 1000
|
|
405
|
+
result = await CommandExecutor.run_and_normalize(
|
|
406
|
+
process,
|
|
407
|
+
timeout=timeout_seconds,
|
|
408
|
+
)
|
|
409
|
+
return ToolResult(
|
|
410
|
+
output=result.output,
|
|
411
|
+
is_error=result.is_error,
|
|
412
|
+
metadata=dict(result.metadata),
|
|
413
|
+
)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
配置工具
|
|
3
|
+
========
|
|
4
|
+
|
|
5
|
+
本模块提供读取和更新 IllusionCode 配置设置的功能。
|
|
6
|
+
|
|
7
|
+
主要组件:
|
|
8
|
+
- ConfigTool: 读取或更新配置的工具,使用示例:
|
|
9
|
+
>>> from illusion.tools import ConfigTool
|
|
10
|
+
>>> tool = ConfigTool()
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
|
|
17
|
+
from illusion.config.settings import load_settings, save_settings
|
|
18
|
+
from illusion.tools.base import BaseTool, ToolExecutionContext, ToolResult
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ConfigToolInput(BaseModel):
|
|
22
|
+
"""配置访问参数。
|
|
23
|
+
|
|
24
|
+
属性:
|
|
25
|
+
action: 操作类型,"show" 显示全部配置,"set" 设置指定键
|
|
26
|
+
key: 配置键名(仅 action="set" 时有效,仅支持顶层平坦字段)
|
|
27
|
+
value: 配置值(仅 action="set" 时有效,字符串类型)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
action: str = Field(default="show", description="show or set")
|
|
31
|
+
key: str | None = Field(default=None)
|
|
32
|
+
value: str | None = Field(default=None)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ConfigTool(BaseTool):
|
|
36
|
+
"""读取或更新 IllusionCode 配置设置。
|
|
37
|
+
|
|
38
|
+
用于查看或更改 IllusionCode 设置。当用户请求配置更改、询问当前设置时使用此工具。
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
name = "config"
|
|
42
|
+
description = """Get or set Illusion Code configuration settings.
|
|
43
|
+
|
|
44
|
+
View or change Illusion Code settings (stored in ~/.illusion/settings.json). Use when the user requests configuration changes or asks about current settings.
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
- **Show all settings:** action="show" — dumps the entire config as JSON
|
|
48
|
+
- **Set a value:** action="set" with key and value
|
|
49
|
+
|
|
50
|
+
Only top-level flat fields can be set. Nested objects (permission, sandbox, memory, hooks, mcp_servers, etc.) must be edited directly in the config file.
|
|
51
|
+
|
|
52
|
+
## Available flat settings
|
|
53
|
+
- model: Active model reference in "env_N.model_N" format (e.g., "env_1.model_1")
|
|
54
|
+
- verbose: true/false — Show detailed output
|
|
55
|
+
- ui_language: UI language (e.g., "zh-CN")
|
|
56
|
+
- output_style: Output style ("default" or custom)
|
|
57
|
+
- show_thinking: true/false — Show thinking process
|
|
58
|
+
- fast_mode: true/false — Fast mode
|
|
59
|
+
- effort: Effort level ("low", "medium", "high")
|
|
60
|
+
- passes: Number of passes (integer)
|
|
61
|
+
- max_tokens: Maximum tokens per response (integer)
|
|
62
|
+
- max_turns: Maximum conversation turns (integer)
|
|
63
|
+
- context_window: Context window size (integer)
|
|
64
|
+
- system_prompt: Custom system prompt or null
|
|
65
|
+
|
|
66
|
+
## Examples
|
|
67
|
+
- Show all settings: { "action": "show" }
|
|
68
|
+
- Enable verbose: { "action": "set", "key": "verbose", "value": "true" }
|
|
69
|
+
- Set UI language: { "action": "set", "key": "ui_language", "value": "zh-CN" }
|
|
70
|
+
- Set effort level: { "action": "set", "key": "effort", "value": "high" }"""
|
|
71
|
+
input_model = ConfigToolInput
|
|
72
|
+
|
|
73
|
+
async def execute(self, arguments: ConfigToolInput, context: ToolExecutionContext) -> ToolResult:
|
|
74
|
+
del context
|
|
75
|
+
# 加载当前设置
|
|
76
|
+
settings = load_settings()
|
|
77
|
+
# 显示当前所有配置
|
|
78
|
+
if arguments.action == "show":
|
|
79
|
+
return ToolResult(output=settings.model_dump_json(indent=2))
|
|
80
|
+
# 设置配置值
|
|
81
|
+
if arguments.action == "set" and arguments.key and arguments.value is not None:
|
|
82
|
+
# 检查配置键是否存在
|
|
83
|
+
if not hasattr(settings, arguments.key):
|
|
84
|
+
return ToolResult(output=f"Unknown config key: {arguments.key}", is_error=True)
|
|
85
|
+
# 更新配置值
|
|
86
|
+
setattr(settings, arguments.key, arguments.value)
|
|
87
|
+
# 保存设置
|
|
88
|
+
save_settings(settings)
|
|
89
|
+
return ToolResult(output=f"Updated {arguments.key}")
|
|
90
|
+
return ToolResult(output="Usage: action=show or action=set with key/value", is_error=True)
|