From ef849d731726e442a2ecfe226f1fd276400a069c Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sat, 27 Jun 2026 12:15:27 +0800 Subject: [PATCH] fix(prompt): simplify gateway workspace context to avoid conflicting paths (S5) Every gateway turn's prompt prefix injected three near-duplicate absolute paths: currentTaskWorkspace + localWorkspace + remoteWorkspaceHint. localWorkspace is the App's LOCAL thread dir (~/.xworkmate/threads/...) which the gateway agent cannot access, and remoteWorkspaceHint duplicates currentTaskWorkspace. The conflicting paths leave the agent unsure where to work and can block conversation continuation. For gateway turns the prompt now carries only currentTaskWorkspace (the plugin owns the artifact scope); localWorkspace is kept only for non-gateway (local agent runs there); remoteWorkspaceHint is dropped when equal to currentTaskWorkspace. sessionKey is kept (short, not a path). UI is unaffected (chat bubble shows the raw user message; the prompt-debug parser only special-cases Execution context / Preferred skills / Attached files). Tests updated; assistant_execution_target_test green (74). Co-Authored-By: Claude Opus 4.8 --- .../06-gateway-turn-stability-and-robustness.md | 5 +++++ .../app_controller_desktop_thread_actions.dart | 17 ++++++++++++----- .../assistant_execution_target_test.dart | 5 ++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/docs/cases/06-gateway-turn-stability-and-robustness.md b/docs/cases/06-gateway-turn-stability-and-robustness.md index 1d3d4ce5..a42e7361 100644 --- a/docs/cases/06-gateway-turn-stability-and-robustness.md +++ b/docs/cases/06-gateway-turn-stability-and-robustness.md @@ -331,6 +331,11 @@ curl -sS -X POST http://127.0.0.1:8787/acp/rpc \ - **S4 运行态可观测** — 沿用 §5 T11/T12:bridge `/api/ping.commit`、网关 `N plugins` 列表、`openclaw plugins inspect` 三处纳入健康检查;`runId` 贯穿 App→bridge→插件→gateway 日志,便于定位断点落在四层中的哪一层。 +- **S5 工作区上下文精简(2026-06-27 已实现)— 避免多个冲突路径阻断对话续跑** ✅ + 现象:每个 gateway turn 的 prompt 前缀 `TaskThread workspace context` 同时塞 `currentTaskWorkspace` + `localWorkspace` + `remoteWorkspaceHint` 三个近似重复的绝对路径,其中 **`localWorkspace` 是 App 本机线程目录(`~/.xworkmate/threads/…`),网关 agent 的文件系统根本访问不到**;`remoteWorkspaceHint` 又与 `currentTaskWorkspace` 重复。多个相互冲突的路径让 agent 不知该在哪工作 → 对话任务可能无法继续执行。 + 改进(`app_controller_desktop_thread_actions.dart taskWorkspaceContextPromptInternal`):网关任务**只给 `currentTaskWorkspace` 一个工作目录**(artifact scope 由插件托管);`localWorkspace` 仅在非网关(本地 agent 实际运行于此)保留;`remoteWorkspaceHint` 与 `currentTaskWorkspace` 相同则去重。`sessionKey` 保留(短、非路径、不致冲突)。 + 不破坏 UI:聊天气泡显示原始用户消息,prompt 调试视图只特判 `Execution context`/`Preferred skills`/`Attached files` 块,工作区块按正文渲染。验收:`assistant_execution_target_test.dart` 全绿(网关 prompt 断言 `isNot(localWorkspace)` / `isNot(remoteWorkspaceHint)`,保留 `currentTaskWorkspace`/`sessionKey`)。 + --- ## 8. 2026-06-27 Cases 00–05 全面验收执行日志(进行中) diff --git a/lib/app/app_controller_desktop_thread_actions.dart b/lib/app/app_controller_desktop_thread_actions.dart index 82642279..eae81a95 100644 --- a/lib/app/app_controller_desktop_thread_actions.dart +++ b/lib/app/app_controller_desktop_thread_actions.dart @@ -1017,17 +1017,24 @@ extension AppControllerDesktopThreadActions on AppController { : (target.isGateway ? r'$XWORKMATE_ARTIFACT_DIRECTORY' : workingDirectory.trim()); + // 精简工作区上下文:只给 agent 一个明确的工作目录。 + // 此前同时塞 sessionKey + currentTaskWorkspace + localWorkspace + remoteWorkspaceHint + // 三个近似重复的绝对路径——其中 localWorkspace 是 App 本机线程目录(`~/.xworkmate/threads/…`), + // 与网关 agent 的文件系统无关;remoteWorkspaceHint 又与 currentTaskWorkspace 重复。 + // 多个相互冲突的路径会让 agent 不知该在哪工作,导致对话任务无法继续执行。 final buffer = StringBuffer() ..writeln('TaskThread workspace context:') ..writeln('- sessionKey: $sessionKey') ..writeln('- currentTaskWorkspace: $currentTaskWorkspace'); - if (workingDirectory.trim().isNotEmpty) { + // 网关任务由插件托管 artifact scope,agent 只需认 currentTaskWorkspace; + // 本机线程目录对网关 agent 无意义,仅在非网关(本地 agent 实际运行于此)时给出。 + if (!target.isGateway && workingDirectory.trim().isNotEmpty) { buffer.writeln('- localWorkspace: ${workingDirectory.trim()}'); } - if (remoteWorkingDirectoryHint.trim().isNotEmpty) { - buffer.writeln( - '- remoteWorkspaceHint: ${remoteWorkingDirectoryHint.trim()}', - ); + // remoteWorkspaceHint 仅在与 currentTaskWorkspace 不同时才输出(去重)。 + final remoteHint = remoteWorkingDirectoryHint.trim(); + if (remoteHint.isNotEmpty && remoteHint != currentTaskWorkspace) { + buffer.writeln('- remoteWorkspaceHint: $remoteHint'); } final visibleTaskInputAttachments = taskInputAttachments .where((item) => item.name.trim().isNotEmpty && item.key.isNotEmpty) diff --git a/test/runtime/assistant_execution_target_test.dart b/test/runtime/assistant_execution_target_test.dart index bf4be715..972a7309 100644 --- a/test/runtime/assistant_execution_target_test.dart +++ b/test/runtime/assistant_execution_target_test.dart @@ -1588,7 +1588,10 @@ void main() { expect(request.workingDirectory, startsWith('/owners/')); expect(request.workingDirectory, isNot(localWorkspace)); expect(request.remoteWorkingDirectoryHint, request.workingDirectory); - expect(request.prompt, contains('- localWorkspace: $localWorkspace')); + // 精简:网关任务不再注入 App 本机 localWorkspace(agent 无法访问、徒增混淆), + // 且 remoteWorkspaceHint 与 currentTaskWorkspace 相同被去重。agent 只认 currentTaskWorkspace。 + expect(request.prompt, isNot(contains('- localWorkspace:'))); + expect(request.prompt, isNot(contains('- remoteWorkspaceHint:'))); expect( request.prompt, contains('- currentTaskWorkspace: ${request.workingDirectory}'),