diff --git a/pipeline_core/agent_config.py b/pipeline_core/agent_config.py index 66151cf..a5d216f 100644 --- a/pipeline_core/agent_config.py +++ b/pipeline_core/agent_config.py @@ -389,8 +389,10 @@ async def load_agent_config(pipeline_id: str = None, project_id: str = None, gen # 的 schema 里会出现 switch_project/project_info/list_my_projects 等, # LLM 看到就能查看/切换/操作任何产线的项目(含他人产线的)。 # 执行层在 agent_loop_v2._dispatch_sdlc_tool 还有一道拦截兜底。 + # 同时剔除 shell 类工具(run_command):shell 无法圈禁在工作目录内, + # 通用会话拿到就能遍历整个工作空间枚举各产线项目(第 6 层泄漏)。 if generic: - tools = [t for t in tools if t.category != "project"] + tools = [t for t in tools if t.category not in ("project", "shell")] prompt = base.system_prompt if ability: tools = _merge_tools(ability.tools, tools) diff --git a/pipeline_core/upload_tools.py b/pipeline_core/upload_tools.py index 4fe39da..4b32620 100644 --- a/pipeline_core/upload_tools.py +++ b/pipeline_core/upload_tools.py @@ -34,12 +34,16 @@ def extract_text(path, name): return '', 0, False -async def resolve_upload_dir(sor, uid, session_id=''): +async def resolve_upload_dir(sor, uid, session_id='', generic: bool = False): """上传文件落盘目录:有当前项目 → 项目根目录;无项目 → workspace 会话目录。 两个位置都在会话 agent 的 read_file 根内(项目根 / WORKSPACE_BASE), 保证「上传放的位置 = agent 读的位置」。 返回 (dir, rel_prefix):rel_prefix 是相对 read_file 根的路径前缀(项目根为 '')。 + + generic=True(纯通用会话,2026-09-05 产线隔离):无项目时落用户专属 + 通用工作目录 _general/{uid}——与 AgentExecutor 的文件根一致 + (_resolve_ws_path 越界保护只认该目录),rel_prefix 为 ''。 """ try: from pipeline_service.workspace import ( @@ -54,6 +58,9 @@ async def resolve_upload_dir(sor, uid, session_id=''): return pdir, '' except Exception: pass + if generic: + from pipeline_service.workspace import generic_workspace_dir + return generic_workspace_dir(uid), '' sess = (session_id or uid or 'default').replace('/', '_').replace('..', '_') return os.path.join(WORKSPACE_BASE, '_uploads', sess), '_uploads/' + sess + '/' diff --git a/wwwroot/api/agent_chat_generic.dspy b/wwwroot/api/agent_chat_generic.dspy index e2f67b3..3960120 100644 --- a/wwwroot/api/agent_chat_generic.dspy +++ b/wwwroot/api/agent_chat_generic.dspy @@ -50,7 +50,7 @@ if action == 'send_message': try: _dbname = get_module_dbname('pipeline_core') async with DBPools().sqlorContext(_dbname) as sor: - _udir, _rel = await resolve_upload_dir(sor, uid, '') + _udir, _rel = await resolve_upload_dir(sor, uid, '', generic=True) _saved = save_uploads(_udir, _uploads) _byname = {os.path.basename(src): n for (src, _n), (n, _p) in zip(_uploads, _saved)} _items = []