feat(perm): 添加会话权限模式管理功能
实现会话权限模式管理功能,包括: 1. 在 pty_process 中定义三种权限模式标志 2. 添加 /perm 命令用于修改会话权限模式 3. 新增 run_command 工具用于执行 bot 控制命令 4. 在会话管理中支持权限模式设置 5. 添加完整的测试用例和文档说明
This commit is contained in:
+33
-16
@@ -40,28 +40,45 @@ Pass these names directly to `create_conversation` — the tool resolves them au
|
||||
|
||||
{active_session_line}
|
||||
|
||||
Your responsibilities:
|
||||
## Tools — two distinct categories
|
||||
|
||||
### Bot control commands (use `run_command`)
|
||||
`run_command` executes PhoneWork slash commands. Use it when the user asks to:
|
||||
- Change permission mode: "切换到只读模式" → run_command("/perm plan")
|
||||
- Close/switch sessions: "关掉第一个" → run_command("/close 1")
|
||||
- Change routing mode: "切换到直连模式" → run_command("/direct")
|
||||
- Set a reminder: "10分钟后提醒我" → run_command("/remind 10m 提醒我")
|
||||
- Check status: "看看现在有哪些 session" → run_command("/status")
|
||||
- Any other /command the user would type manually
|
||||
|
||||
Available bot commands (pass verbatim to run_command):
|
||||
/new <dir> [msg] [--perm bypass|accept|plan] — create session
|
||||
/close [n|conv_id] — close session
|
||||
/switch <n> — switch active session
|
||||
/perm <mode> [conv_id] — permission mode: bypass (default), accept, plan
|
||||
/direct — direct mode (bypass LLM for CC messages)
|
||||
/smart — smart mode (LLM routing, default)
|
||||
/status — list sessions
|
||||
/remind <Ns|Nm|Nh> <msg> — one-shot reminder
|
||||
/tasks — list background tasks
|
||||
|
||||
### Host shell commands (use `run_shell`)
|
||||
`run_shell` executes shell commands on the host machine (git, ls, cat, pip, etc.).
|
||||
NEVER use `run_shell` for bot control. NEVER use `run_command` for shell commands.
|
||||
|
||||
## Session responsibilities
|
||||
1. NEW session: call `create_conversation` with the project name/path. \
|
||||
If the user's message also contains a task, pass it as `initial_message` too.
|
||||
2. Follow-up to ACTIVE session: call `send_to_conversation` with the active conv_id shown above.
|
||||
3. List sessions: call `list_conversations`.
|
||||
4. Close session: call `close_conversation`.
|
||||
5. GENERAL QUESTIONS: If the user asks a general question (not about a specific project or file), \
|
||||
answer directly using your own knowledge. Do NOT create a session for simple Q&A.
|
||||
6. WEB / SEARCH: Use the `web` tool when the user needs current information. \
|
||||
Call it ONCE (or at most twice with a refined query). Then synthesize and reply — \
|
||||
do NOT keep searching in a loop. If the first search returns results, use them.
|
||||
7. BACKGROUND TASKS: When `create_conversation` or `send_to_conversation` returns a \
|
||||
"Task #... started" message, the task is running in the background. \
|
||||
Immediately reply to the user that the task has started and they will be notified. \
|
||||
Do NOT call `task_status` in a loop waiting for it — the system sends a notification when done.
|
||||
3. BOT CONTROL: call `run_command` with the appropriate slash command.
|
||||
4. GENERAL QUESTIONS: answer directly — do NOT create a session for simple Q&A.
|
||||
5. WEB / SEARCH: use `web` at most twice, then synthesize and reply.
|
||||
6. BACKGROUND TASKS: when a task starts, reply immediately — do NOT poll `task_status`.
|
||||
|
||||
Guidelines:
|
||||
- Relay Claude Code's output verbatim.
|
||||
- If no active session and the user sends a task without naming a directory, ask them which project.
|
||||
- For general knowledge questions (e.g., "what is a Python generator?", "explain async/await"), \
|
||||
answer directly without creating a session.
|
||||
- After using any tool, always produce a final text reply to the user. Never end a turn on a tool call.
|
||||
- If no active session and the user sends a task without naming a directory, ask which project.
|
||||
- After using any tool, always produce a final text reply. Never end a turn on a tool call.
|
||||
- Keep your own words brief — let Claude Code's output speak.
|
||||
- Reply in the same language the user uses (Chinese or English).
|
||||
"""
|
||||
|
||||
@@ -716,12 +716,49 @@ class TaskStatusTool(BaseTool):
|
||||
}, ensure_ascii=False)
|
||||
|
||||
|
||||
class RunCommandInput(BaseModel):
|
||||
command: str = Field(
|
||||
...,
|
||||
description=(
|
||||
"A bot slash command to execute (e.g. '/perm accept', '/close 1', '/switch 2'). "
|
||||
"This runs bot control commands — NOT shell commands on the host machine. "
|
||||
"Use run_shell for host shell commands (git, ls, etc.)."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class RunCommandTool(BaseTool):
|
||||
name: str = "run_command"
|
||||
description: str = (
|
||||
"Execute a PhoneWork bot slash command on behalf of the user. "
|
||||
"Use this to control sessions, switch modes, change permissions, etc. "
|
||||
"Examples: '/perm accept', '/close 1', '/switch 2', '/direct', '/smart', '/status'. "
|
||||
"Do NOT use this for shell commands — use run_shell for those."
|
||||
)
|
||||
args_schema: Type[BaseModel] = RunCommandInput
|
||||
|
||||
def _run(self, command: str) -> str:
|
||||
raise NotImplementedError("Use async version")
|
||||
|
||||
async def _arun(self, command: str) -> str:
|
||||
from bot.commands import handle_command
|
||||
from orchestrator.tools import get_current_user
|
||||
user_id = get_current_user()
|
||||
if not user_id:
|
||||
return "Error: no user context"
|
||||
result = await handle_command(user_id, command.strip())
|
||||
if result is None:
|
||||
return f"Unknown command: {command!r}. Use /help to see available commands."
|
||||
return result
|
||||
|
||||
|
||||
# Module-level tool list for easy import
|
||||
TOOLS = [
|
||||
CreateConversationTool(),
|
||||
SendToConversationTool(),
|
||||
ListConversationsTool(),
|
||||
CloseConversationTool(),
|
||||
RunCommandTool(),
|
||||
ShellTool(),
|
||||
FileReadTool(),
|
||||
FileWriteTool(),
|
||||
|
||||
Reference in New Issue
Block a user