From 703eb70354a7deebb10d311f4c6457b41a868f06 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 29 May 2026 13:31:53 +0800 Subject: [PATCH] fix(openclaw): pass prepared artifact scope to chat --- internal/acp/orchestrator.go | 63 ++++++++++++++++++++++++++++++++++++ internal/acp/routing_test.go | 11 +++++++ 2 files changed, 74 insertions(+) diff --git a/internal/acp/orchestrator.go b/internal/acp/orchestrator.go index c4ffbf2..2312ecb 100644 --- a/internal/acp/orchestrator.go +++ b/internal/acp/orchestrator.go @@ -336,6 +336,7 @@ func (o *SessionOrchestrator) runOpenClawGatewayChat( return nil, prepareErr } logOpenClawArtifactSync(gatewayProvider, sessionKey, turnID, "prepare", true, false, false) + applyOpenClawPreparedArtifactToChatParams(chatParams, preparedArtifact, sessionKey, turnID) sendStarted := time.Now() sendResult := o.openClawGatewayRequestWithRetry( gatewayProvider, @@ -617,6 +618,68 @@ func applyOpenClawPreparedArtifactToResult(result map[string]any, prepared *open } } +func applyOpenClawPreparedArtifactToChatParams( + chatParams map[string]any, + prepared *openClawPreparedArtifactScope, + sessionKey string, + runID string, +) { + if chatParams == nil || prepared == nil || strings.TrimSpace(prepared.ArtifactDirectory) == "" { + return + } + receipt := openClawArtifactSystemProvenanceReceipt(prepared, sessionKey, runID) + if receipt == "" { + return + } + existing := strings.TrimSpace(shared.StringArg(chatParams, "systemProvenanceReceipt", "")) + if existing != "" { + chatParams["systemProvenanceReceipt"] = existing + "\n\n" + receipt + return + } + chatParams["systemProvenanceReceipt"] = receipt +} + +func openClawArtifactSystemProvenanceReceipt( + prepared *openClawPreparedArtifactScope, + sessionKey string, + runID string, +) string { + if prepared == nil { + return "" + } + artifactDirectory := strings.TrimSpace(prepared.ArtifactDirectory) + artifactScope := strings.TrimSpace(prepared.ArtifactScope) + if artifactDirectory == "" || artifactScope == "" { + return "" + } + lines := []string{ + "XWorkmate task artifact context:", + "- Treat artifactDirectory as the working directory for all files generated in this turn.", + "- Write final artifacts directly under artifactDirectory using relative paths such as assets/images/... or prompts/....", + "- Do not create a nested task_artifacts/ directory inside artifactDirectory.", + "- Environment contract for shell commands:", + " export XWORKMATE_TASK_ARTIFACT_DIR=" + shellSingleQuote(artifactDirectory), + " export XWORKMATE_ARTIFACT_DIRECTORY=" + shellSingleQuote(artifactDirectory), + " export XWORKMATE_ARTIFACT_SCOPE=" + shellSingleQuote(artifactScope), + " export XWORKMATE_SESSION_KEY=" + shellSingleQuote(strings.TrimSpace(sessionKey)), + " export XWORKMATE_RUN_ID=" + shellSingleQuote(strings.TrimSpace(runID)), + " cd " + shellSingleQuote(artifactDirectory), + "artifactDirectory: " + artifactDirectory, + "artifactScope: " + artifactScope, + } + if relative := strings.TrimSpace(prepared.RelativeArtifactDirectory); relative != "" { + lines = append(lines, "relativeArtifactDirectory: "+relative) + } + if remote := strings.TrimSpace(prepared.RemoteWorkingDirectory); remote != "" { + lines = append(lines, "remoteWorkingDirectory: "+remote) + } + return strings.Join(lines, "\n") +} + +func shellSingleQuote(value string) string { + return "'" + strings.ReplaceAll(value, "'", "'\"'\"'") + "'" +} + func openClawChatSendParams( params map[string]any, turnID string, diff --git a/internal/acp/routing_test.go b/internal/acp/routing_test.go index 735ce88..8eb209b 100644 --- a/internal/acp/routing_test.go +++ b/internal/acp/routing_test.go @@ -513,6 +513,17 @@ func TestExecuteSessionTaskGatewayAutoConnectsLocalOpenClaw(t *testing.T) { t.Fatalf("expected chat.send params to omit bridge artifact/workspace field %q, got %#v", key, chatParams) } } + receipt := strings.TrimSpace(shared.StringArg(chatParams, "systemProvenanceReceipt", "")) + for _, expected := range []string{ + "artifactDirectory: /remote/openclaw/workspace/tasks/thread-openclaw/" + shared.StringArg(chatParams, "idempotencyKey", ""), + "artifactScope: tasks/thread-openclaw/" + shared.StringArg(chatParams, "idempotencyKey", ""), + "export XWORKMATE_TASK_ARTIFACT_DIR='/remote/openclaw/workspace/tasks/thread-openclaw/" + shared.StringArg(chatParams, "idempotencyKey", "") + "'", + "cd '/remote/openclaw/workspace/tasks/thread-openclaw/" + shared.StringArg(chatParams, "idempotencyKey", "") + "'", + } { + if !strings.Contains(receipt, expected) { + t.Fatalf("expected chat.send systemProvenanceReceipt to include %q, got %q", expected, receipt) + } + } if gateway.AgentWaitCount() != 1 { t.Fatalf("expected one OpenClaw agent.wait request, got %d", gateway.AgentWaitCount()) }