fix(server,static): 修复执行环境/权限模式切换对当前对话不生效
对话级 terminal 隔离后,模式切换请求未携带 conversation_id, 落在工作区级服务 terminal 上;而任务实际运行在对话级 terminal, 且 load_conversation 每次都会从对话 metadata 恢复创建时冻结的模式, 导致旧对话模式永远无法改变、新对话继承服务 terminal 的瞬时模式。 - 前端 permission.ts:权限/执行环境/网络权限的 POST 与 GET 均携带当前 conversation_id,路由到对话级 terminal 并持久化到正确对话 - 后端 permission.py:切换时同步工作区级服务 terminal 的内存态 (persist=False),保证新建对话继承最新模式而非过期模式 - 附带修复:运行期间切换的 pending 队列此前排在错误的 terminal 上, 任务工具循环永远消费不到
This commit is contained in:
parent
8628954f30
commit
bd06f96d07
@ -46,7 +46,7 @@ from core.web_terminal import WebTerminal
|
||||
from config.model_profiles import get_model_context_window
|
||||
|
||||
from server.auth_helpers import api_login_required, resolve_admin_policy, get_current_user_record, get_current_username
|
||||
from server.context import with_terminal, get_gui_manager, get_upload_guard, build_upload_error_response, ensure_conversation_loaded, get_or_create_usage_tracker
|
||||
from server.context import with_terminal, get_gui_manager, get_upload_guard, build_upload_error_response, ensure_conversation_loaded, get_or_create_usage_tracker, get_user_resources
|
||||
from server.security import rate_limited, prune_socket_tokens
|
||||
from server.utils_common import debug_log
|
||||
from server.state import PROJECT_MAX_STORAGE_MB, THINKING_FAILURE_KEYWORDS, pending_socket_tokens, SOCKET_TOKEN_TTL_SECONDS
|
||||
@ -60,6 +60,32 @@ import re
|
||||
UPLOAD_FOLDER_NAME = ".astrion/user_upload"
|
||||
|
||||
|
||||
def _sync_workspace_terminal_mode(username: str, workspace, kind: str, mode: str) -> None:
|
||||
"""把模式切换同步到工作区级服务 terminal(仅内存态,不持久化)。
|
||||
|
||||
对话级隔离后,携带 conversation_id 的切换请求落在对话级 terminal 上,
|
||||
工作区级 terminal 仍保持旧模式;而新建对话会以工作区 terminal 的当前
|
||||
模式冻结进 metadata(create_new_conversation),导致新对话继承过期模式。
|
||||
这里同步内存态即可:persist=False,避免误写工作区 terminal 上的陈旧对话。
|
||||
"""
|
||||
try:
|
||||
ws_terminal, _ws = get_user_resources(
|
||||
username,
|
||||
workspace_id=getattr(workspace, "workspace_id", None),
|
||||
conversation_id=None,
|
||||
)
|
||||
if not ws_terminal:
|
||||
return
|
||||
if kind == "permission_mode":
|
||||
ws_terminal.set_permission_mode(mode, persist=False)
|
||||
elif kind == "execution_mode":
|
||||
ws_terminal.set_execution_mode(mode)
|
||||
elif kind == "network_permission":
|
||||
ws_terminal.set_network_permission(mode)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _path_conflicts_with_deny_list(path: str) -> Optional[str]:
|
||||
"""检查用户授权路径是否与内置 deny 列表冲突,返回错误信息或 None。"""
|
||||
if not path:
|
||||
@ -125,6 +151,7 @@ def update_permission_mode(terminal: WebTerminal, workspace: UserWorkspace, user
|
||||
# 避免「A→B→A」来回切换时中间值被覆盖后仍插入多余消息。
|
||||
try:
|
||||
terminal.queue_permission_mode_change(target_mode)
|
||||
_sync_workspace_terminal_mode(username, workspace, "permission_mode", target_mode)
|
||||
except Exception as exc:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
@ -153,6 +180,7 @@ def update_permission_mode(terminal: WebTerminal, workspace: UserWorkspace, user
|
||||
"permission_mode": applied_mode,
|
||||
"pending_permission_mode": None,
|
||||
})
|
||||
_sync_workspace_terminal_mode(username, workspace, "permission_mode", applied_mode)
|
||||
except Exception as exc:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
@ -228,6 +256,7 @@ def update_execution_mode(terminal: WebTerminal, workspace: UserWorkspace, usern
|
||||
# 避免「A→B→A」来回切换时中间值被覆盖后仍插入多余消息。
|
||||
try:
|
||||
terminal.queue_execution_mode_change(target_mode)
|
||||
_sync_workspace_terminal_mode(username, workspace, "execution_mode", target_mode)
|
||||
except Exception as exc:
|
||||
return jsonify({"success": False, "error": str(exc), "message": "更新执行环境失败"}), 500
|
||||
status = terminal.get_status()
|
||||
@ -254,6 +283,7 @@ def update_execution_mode(terminal: WebTerminal, workspace: UserWorkspace, usern
|
||||
"execution_mode": state.get("mode", target_mode),
|
||||
"pending_execution_mode": None,
|
||||
})
|
||||
_sync_workspace_terminal_mode(username, workspace, "execution_mode", state.get("mode", target_mode))
|
||||
except Exception as exc:
|
||||
return jsonify({"success": False, "error": str(exc), "message": "更新执行环境失败"}), 500
|
||||
status = terminal.get_status()
|
||||
@ -315,6 +345,7 @@ def update_network_permission(terminal: WebTerminal, workspace: UserWorkspace, u
|
||||
if is_running:
|
||||
try:
|
||||
terminal.queue_network_permission_change(target_mode)
|
||||
_sync_workspace_terminal_mode(username, workspace, "network_permission", target_mode)
|
||||
except Exception as exc:
|
||||
return jsonify({"success": False, "error": str(exc), "message": "更新网络权限失败"}), 500
|
||||
status = terminal.get_status()
|
||||
@ -337,6 +368,7 @@ def update_network_permission(terminal: WebTerminal, workspace: UserWorkspace, u
|
||||
"network_permission": applied,
|
||||
"pending_network_permission": None,
|
||||
})
|
||||
_sync_workspace_terminal_mode(username, workspace, "network_permission", applied)
|
||||
except Exception as exc:
|
||||
return jsonify({"success": False, "error": str(exc), "message": "更新网络权限失败"}), 500
|
||||
status = terminal.get_status()
|
||||
|
||||
@ -70,7 +70,13 @@ export const permissionMethods = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ mode: target })
|
||||
body: JSON.stringify({
|
||||
mode: target,
|
||||
// 对话级隔离:携带当前对话 ID,让后端把模式设置到对话级 terminal
|
||||
// (任务实际运行的实例),并持久化到当前对话 metadata;
|
||||
// /new 页面无对话时回退到工作区级 terminal(新对话创建时继承)。
|
||||
...(this.currentConversationId ? { conversation_id: this.currentConversationId } : {})
|
||||
})
|
||||
});
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !payload?.success) {
|
||||
@ -106,7 +112,13 @@ export const permissionMethods = {
|
||||
const response = await fetch('/api/execution-mode', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mode: target })
|
||||
body: JSON.stringify({
|
||||
mode: target,
|
||||
// 对话级隔离:携带当前对话 ID,让后端把模式设置到对话级 terminal
|
||||
// (任务实际运行的实例),并持久化到当前对话 metadata;
|
||||
// /new 页面无对话时回退到工作区级 terminal(新对话创建时继承)。
|
||||
...(this.currentConversationId ? { conversation_id: this.currentConversationId } : {})
|
||||
})
|
||||
});
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !payload?.success) {
|
||||
@ -151,7 +163,13 @@ export const permissionMethods = {
|
||||
const response = await fetch('/api/network-permission', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mode: target })
|
||||
body: JSON.stringify({
|
||||
mode: target,
|
||||
// 对话级隔离:携带当前对话 ID,让后端把模式设置到对话级 terminal
|
||||
// (任务实际运行的实例),并持久化到当前对话 metadata;
|
||||
// /new 页面无对话时回退到工作区级 terminal(新对话创建时继承)。
|
||||
...(this.currentConversationId ? { conversation_id: this.currentConversationId } : {})
|
||||
})
|
||||
});
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !payload?.success) {
|
||||
@ -179,7 +197,10 @@ export const permissionMethods = {
|
||||
},
|
||||
async fetchNetworkPermission() {
|
||||
try {
|
||||
const response = await fetch('/api/network-permission');
|
||||
const query = this.currentConversationId
|
||||
? `?conversation_id=${encodeURIComponent(this.currentConversationId)}`
|
||||
: '';
|
||||
const response = await fetch(`/api/network-permission${query}`);
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !payload?.success) {
|
||||
return;
|
||||
@ -195,7 +216,10 @@ export const permissionMethods = {
|
||||
},
|
||||
async fetchPermissionMode() {
|
||||
try {
|
||||
const response = await fetch('/api/permission-mode');
|
||||
const query = this.currentConversationId
|
||||
? `?conversation_id=${encodeURIComponent(this.currentConversationId)}`
|
||||
: '';
|
||||
const response = await fetch(`/api/permission-mode${query}`);
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !payload?.success) {
|
||||
return;
|
||||
@ -212,7 +236,10 @@ export const permissionMethods = {
|
||||
try {
|
||||
const prevExecutionMode = this.currentExecutionMode;
|
||||
const prevDirectUntil = this.executionModeDirectUntil;
|
||||
const response = await fetch('/api/execution-mode');
|
||||
const query = this.currentConversationId
|
||||
? `?conversation_id=${encodeURIComponent(this.currentConversationId)}`
|
||||
: '';
|
||||
const response = await fetch(`/api/execution-mode${query}`);
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !payload?.success) {
|
||||
return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user