fix: bind OpenClaw artifacts to prepared run scope
This commit is contained in:
parent
836b6bac44
commit
087dd4f354
@ -22,8 +22,9 @@ type SessionOrchestrator struct {
|
||||
}
|
||||
|
||||
const (
|
||||
openClawAgentWaitTimeout = 9 * time.Minute
|
||||
openClawNoDisplayableText = "OpenClaw completed without displayable output."
|
||||
openClawAgentWaitTimeout = 9 * time.Minute
|
||||
openClawNoDisplayableText = "OpenClaw completed without displayable output."
|
||||
openClawArtifactExportAttemptedField = "_openClawArtifactExportAttempted"
|
||||
)
|
||||
|
||||
func NewSessionOrchestrator(server *Server) *SessionOrchestrator {
|
||||
@ -240,13 +241,22 @@ func (o *SessionOrchestrator) runOpenClawGatewayChat(
|
||||
}
|
||||
}
|
||||
sessionKey := openClawSessionKey(params, turnID)
|
||||
artifactRunID := turnID
|
||||
logOpenClawArtifactSync(gatewayProvider, sessionKey, artifactRunID, "intent", false, false, false)
|
||||
chatParams, rpcErr := openClawChatSendParams(params, turnID)
|
||||
if rpcErr != nil {
|
||||
return nil, rpcErr
|
||||
}
|
||||
artifactSinceUnixMs := time.Now().Add(-1 * time.Second).UnixMilli()
|
||||
preparedArtifact, prepareErr := o.openClawArtifactPrepare(
|
||||
gatewayProvider,
|
||||
sessionKey,
|
||||
turnID,
|
||||
notifyWithCollection,
|
||||
)
|
||||
if prepareErr != nil {
|
||||
return nil, prepareErr
|
||||
}
|
||||
applyOpenClawPreparedArtifactToChatParams(chatParams, preparedArtifact)
|
||||
logOpenClawArtifactSync(gatewayProvider, sessionKey, turnID, "prepare", true, false, false)
|
||||
sendStarted := time.Now()
|
||||
sendResult := o.openClawGatewayRequestWithRetry(
|
||||
gatewayProvider,
|
||||
@ -268,6 +278,18 @@ func (o *SessionOrchestrator) runOpenClawGatewayChat(
|
||||
}
|
||||
sendPayload := shared.AsMap(sendResult.Payload)
|
||||
runID := strings.TrimSpace(shared.StringArg(sendPayload, "runId", turnID))
|
||||
if runID != turnID {
|
||||
preparedArtifact, prepareErr = o.openClawArtifactPrepare(
|
||||
gatewayProvider,
|
||||
sessionKey,
|
||||
runID,
|
||||
notifyWithCollection,
|
||||
)
|
||||
if prepareErr != nil {
|
||||
return nil, prepareErr
|
||||
}
|
||||
logOpenClawArtifactSync(gatewayProvider, sessionKey, runID, "prepare", true, false, false)
|
||||
}
|
||||
waitStarted := time.Now()
|
||||
waitResult := o.openClawGatewayRequestWithRetry(
|
||||
gatewayProvider,
|
||||
@ -311,19 +333,20 @@ func (o *SessionOrchestrator) runOpenClawGatewayChat(
|
||||
}
|
||||
mergeOpenClawArtifactPayload(result, waitPayload)
|
||||
mergeOpenClawArtifactPayload(result, collector.artifactPayload())
|
||||
preparedArtifact := openClawPreparedArtifactScopeFromPayload(result)
|
||||
applyOpenClawPreparedArtifactToResult(result, preparedArtifact)
|
||||
artifactPayload := o.openClawArtifactExport(
|
||||
gatewayProvider,
|
||||
chatParams,
|
||||
artifactRunID,
|
||||
runID,
|
||||
artifactSinceUnixMs,
|
||||
preparedArtifact,
|
||||
notifyWithCollection,
|
||||
)
|
||||
mergeOpenClawArtifactPayload(result, artifactPayload)
|
||||
result[openClawArtifactExportAttemptedField] = true
|
||||
exportedCount := openClawArtifactPayloadCount(result)
|
||||
logOpenClawArtifactSync(gatewayProvider, sessionKey, artifactRunID, "export", preparedArtifact != nil, exportedCount > 0, exportedCount == 0)
|
||||
o.server.decorateOpenClawArtifactDownloadURLs(result, shared.StringArg(chatParams, "sessionKey", ""), artifactRunID)
|
||||
logOpenClawArtifactSync(gatewayProvider, sessionKey, runID, "export", preparedArtifact != nil, exportedCount > 0, exportedCount == 0)
|
||||
o.server.decorateOpenClawArtifactDownloadURLs(result, shared.StringArg(chatParams, "sessionKey", ""), runID)
|
||||
stripOpenClawArtifactInlineContent(result)
|
||||
guardOpenClawNoDisplayableResult(result, noDisplayableOutput)
|
||||
if notify != nil {
|
||||
@ -430,9 +453,12 @@ func isSessionTaskMethod(method string) bool {
|
||||
}
|
||||
|
||||
type openClawPreparedArtifactScope struct {
|
||||
ArtifactScope string
|
||||
ArtifactDirectory string
|
||||
ScopeKind string
|
||||
RemoteWorkingDirectory string
|
||||
RemoteWorkspaceRefKind string
|
||||
ArtifactScope string
|
||||
ArtifactDirectory string
|
||||
RelativeArtifactDirectory string
|
||||
ScopeKind string
|
||||
}
|
||||
|
||||
func openClawPreparedArtifactScopeFromPayload(payload map[string]any) *openClawPreparedArtifactScope {
|
||||
@ -440,9 +466,12 @@ func openClawPreparedArtifactScopeFromPayload(payload map[string]any) *openClawP
|
||||
return nil
|
||||
}
|
||||
prepared := &openClawPreparedArtifactScope{
|
||||
ArtifactScope: strings.TrimSpace(shared.StringArg(payload, "artifactScope", "")),
|
||||
ArtifactDirectory: strings.TrimSpace(shared.StringArg(payload, "artifactDirectory", "")),
|
||||
ScopeKind: strings.TrimSpace(shared.StringArg(payload, "scopeKind", "")),
|
||||
RemoteWorkingDirectory: strings.TrimSpace(shared.StringArg(payload, "remoteWorkingDirectory", "")),
|
||||
RemoteWorkspaceRefKind: strings.TrimSpace(shared.StringArg(payload, "remoteWorkspaceRefKind", "")),
|
||||
ArtifactScope: strings.TrimSpace(shared.StringArg(payload, "artifactScope", "")),
|
||||
ArtifactDirectory: strings.TrimSpace(shared.StringArg(payload, "artifactDirectory", "")),
|
||||
RelativeArtifactDirectory: strings.TrimSpace(shared.StringArg(payload, "relativeArtifactDirectory", "")),
|
||||
ScopeKind: strings.TrimSpace(shared.StringArg(payload, "scopeKind", "")),
|
||||
}
|
||||
if prepared.ArtifactScope == "" || prepared.ArtifactDirectory == "" {
|
||||
return nil
|
||||
@ -453,6 +482,83 @@ func openClawPreparedArtifactScopeFromPayload(payload map[string]any) *openClawP
|
||||
return prepared
|
||||
}
|
||||
|
||||
func (o *SessionOrchestrator) openClawArtifactPrepare(
|
||||
gatewayProvider string,
|
||||
sessionKey string,
|
||||
runID string,
|
||||
notify func(map[string]any),
|
||||
) (*openClawPreparedArtifactScope, *shared.RPCError) {
|
||||
sessionKey = strings.TrimSpace(sessionKey)
|
||||
runID = strings.TrimSpace(runID)
|
||||
if sessionKey == "" || runID == "" {
|
||||
return nil, &shared.RPCError{Code: -32602, Message: "openclaw artifact prepare requires sessionKey and runId"}
|
||||
}
|
||||
prepareResult := o.openClawGatewayRequestWithRetry(
|
||||
gatewayProvider,
|
||||
"xworkmate.artifacts.prepare",
|
||||
map[string]any{
|
||||
"sessionKey": sessionKey,
|
||||
"runId": runID,
|
||||
},
|
||||
30*time.Second,
|
||||
notify,
|
||||
)
|
||||
if !prepareResult.OK {
|
||||
return nil, gatewayRPCError(prepareResult.Error, "openclaw artifact prepare failed")
|
||||
}
|
||||
prepared := openClawPreparedArtifactScopeFromPayload(shared.AsMap(prepareResult.Payload))
|
||||
if prepared == nil {
|
||||
return nil, &shared.RPCError{Code: -32002, Message: "openclaw artifact prepare returned no scoped artifact directory"}
|
||||
}
|
||||
return prepared, nil
|
||||
}
|
||||
|
||||
func applyOpenClawPreparedArtifactToChatParams(chatParams map[string]any, prepared *openClawPreparedArtifactScope) {
|
||||
if chatParams == nil || prepared == nil {
|
||||
return
|
||||
}
|
||||
chatParams["artifactScope"] = prepared.ArtifactScope
|
||||
chatParams["artifactDirectory"] = prepared.ArtifactDirectory
|
||||
chatParams["relativeArtifactDirectory"] = prepared.RelativeArtifactDirectory
|
||||
chatParams["artifactScopeKind"] = prepared.ScopeKind
|
||||
if prepared.RemoteWorkingDirectory != "" {
|
||||
chatParams["remoteWorkingDirectory"] = prepared.RemoteWorkingDirectory
|
||||
}
|
||||
if prepared.RemoteWorkspaceRefKind != "" {
|
||||
chatParams["remoteWorkspaceRefKind"] = prepared.RemoteWorkspaceRefKind
|
||||
}
|
||||
chatParams["xworkmateArtifacts"] = map[string]any{
|
||||
"artifactScope": prepared.ArtifactScope,
|
||||
"artifactDirectory": prepared.ArtifactDirectory,
|
||||
"relativeArtifactDirectory": prepared.RelativeArtifactDirectory,
|
||||
"scopeKind": prepared.ScopeKind,
|
||||
}
|
||||
}
|
||||
|
||||
func applyOpenClawPreparedArtifactToResult(result map[string]any, prepared *openClawPreparedArtifactScope) {
|
||||
if result == nil || prepared == nil {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(shared.StringArg(result, "remoteWorkingDirectory", "")) == "" && prepared.RemoteWorkingDirectory != "" {
|
||||
result["remoteWorkingDirectory"] = prepared.RemoteWorkingDirectory
|
||||
}
|
||||
if strings.TrimSpace(shared.StringArg(result, "remoteWorkspaceRefKind", "")) == "" && prepared.RemoteWorkspaceRefKind != "" {
|
||||
result["remoteWorkspaceRefKind"] = prepared.RemoteWorkspaceRefKind
|
||||
}
|
||||
if strings.TrimSpace(shared.StringArg(result, "artifactScope", "")) == "" {
|
||||
result["artifactScope"] = prepared.ArtifactScope
|
||||
}
|
||||
if strings.TrimSpace(shared.StringArg(result, "artifactDirectory", "")) == "" {
|
||||
result["artifactDirectory"] = prepared.ArtifactDirectory
|
||||
}
|
||||
if strings.TrimSpace(shared.StringArg(result, "relativeArtifactDirectory", "")) == "" && prepared.RelativeArtifactDirectory != "" {
|
||||
result["relativeArtifactDirectory"] = prepared.RelativeArtifactDirectory
|
||||
}
|
||||
if strings.TrimSpace(shared.StringArg(result, "scopeKind", "")) == "" {
|
||||
result["scopeKind"] = prepared.ScopeKind
|
||||
}
|
||||
}
|
||||
|
||||
func openClawChatSendParams(
|
||||
params map[string]any,
|
||||
turnID string,
|
||||
@ -952,6 +1058,7 @@ func (o *SessionOrchestrator) normalizeResult(sess *session, result map[string]a
|
||||
if openClawArtifactResponse(result, routing, params) {
|
||||
o.completeOpenClawScopedArtifactExport(result, params, openClawGatewayProviderForArtifacts(result, routing, params), turnID)
|
||||
}
|
||||
delete(result, openClawArtifactExportAttemptedField)
|
||||
|
||||
successValue, hasSuccess := result["success"]
|
||||
success := !hasSuccess || parseBool(successValue)
|
||||
@ -1054,6 +1161,9 @@ func (o *SessionOrchestrator) completeOpenClawScopedArtifactExport(
|
||||
if len(extractArtifactPayloads(result, remoteWorkingDirectory)) > 0 {
|
||||
return
|
||||
}
|
||||
if parseBool(result[openClawArtifactExportAttemptedField]) {
|
||||
return
|
||||
}
|
||||
preparedArtifact := openClawPreparedArtifactScopeFromPayload(result)
|
||||
if preparedArtifact == nil {
|
||||
return
|
||||
|
||||
@ -495,6 +495,16 @@ func TestExecuteSessionTaskGatewayAutoConnectsLocalOpenClaw(t *testing.T) {
|
||||
if gateway.ChatSendCount() != 1 {
|
||||
t.Fatalf("expected one OpenClaw chat.send request, got %d", gateway.ChatSendCount())
|
||||
}
|
||||
if gateway.ArtifactPrepareCount() != 1 {
|
||||
t.Fatalf("expected one OpenClaw artifact prepare request before chat.send, got %d", gateway.ArtifactPrepareCount())
|
||||
}
|
||||
chatParams := gateway.LastChatSendParams()
|
||||
if got := strings.TrimSpace(shared.StringArg(chatParams, "artifactScope", "")); !strings.HasPrefix(got, "tasks/thread-openclaw/turn-") {
|
||||
t.Fatalf("expected chat.send to carry prepared artifact scope, got %#v", chatParams)
|
||||
}
|
||||
if got := strings.TrimSpace(shared.StringArg(chatParams, "artifactDirectory", "")); !strings.Contains(got, "/remote/openclaw/workspace/tasks/thread-openclaw/turn-") {
|
||||
t.Fatalf("expected chat.send to carry prepared artifact directory, got %#v", chatParams)
|
||||
}
|
||||
if gateway.AgentWaitCount() != 1 {
|
||||
t.Fatalf("expected one OpenClaw agent.wait request, got %d", gateway.AgentWaitCount())
|
||||
}
|
||||
@ -512,8 +522,8 @@ func TestExecuteSessionTaskGatewayAutoConnectsLocalOpenClaw(t *testing.T) {
|
||||
if gateway.ArtifactExportCount() != 1 {
|
||||
t.Fatalf("expected one OpenClaw artifact export sync after run, got %d", gateway.ArtifactExportCount())
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
}
|
||||
client := gateway.LastConnectClient()
|
||||
if got := client["id"]; got != "openclaw-macos" {
|
||||
@ -607,8 +617,8 @@ func TestExecuteSessionMessageGatewayUsesOpenClawChatSend(t *testing.T) {
|
||||
if gateway.ArtifactExportCount() != 1 {
|
||||
t.Fatalf("expected one OpenClaw artifact export sync after message run, got %d", gateway.ArtifactExportCount())
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -642,8 +652,8 @@ func TestExecuteSessionTaskGatewaySurfacesOpenClawChatSendError(t *testing.T) {
|
||||
if rpcErr.Code != -32002 || !strings.Contains(rpcErr.Message, "openclaw chat failed") {
|
||||
t.Fatalf("expected surfaced chat.send failure, got %#v", rpcErr)
|
||||
}
|
||||
if got := gateway.Methods(); len(got) != 2 || got[0] != "connect" || got[1] != "chat.send" {
|
||||
t.Fatalf("expected connect then chat.send, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, then chat.send, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -756,8 +766,8 @@ func TestExecuteSessionTaskGatewaySurfacesOpenClawAgentWaitError(t *testing.T) {
|
||||
if rpcErr.Code != -32002 || !strings.Contains(rpcErr.Message, "openclaw wait failed") {
|
||||
t.Fatalf("expected surfaced agent.wait failure, got %#v", rpcErr)
|
||||
}
|
||||
if got := gateway.Methods(); len(got) != 3 || got[0] != "connect" || got[1] != "chat.send" || got[2] != "agent.wait" {
|
||||
t.Fatalf("expected connect, chat.send, then agent.wait, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, chat.send, then agent.wait, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -834,8 +844,8 @@ func TestExecuteSessionTaskGatewayExportsOpenClawArtifacts(t *testing.T) {
|
||||
if got := parsedDownloadURL.Query().Get("relativePath"); got != "reports/final.md" {
|
||||
t.Fatalf("expected artifact relativePath in downloadUrl, got %q", got)
|
||||
}
|
||||
if artifactScope := parsedDownloadURL.Query().Get("artifactScope"); artifactScope != "" {
|
||||
t.Fatalf("expected no bridge-imposed artifact scope in downloadUrl, got %q", artifactScope)
|
||||
if artifactScope := parsedDownloadURL.Query().Get("artifactScope"); artifactScope != "tasks/thread-openclaw-artifact/"+response["runId"].(string) {
|
||||
t.Fatalf("expected prepared artifact scope in downloadUrl, got %q", artifactScope)
|
||||
}
|
||||
if parsedDownloadURL.Query().Get("sig") == "" {
|
||||
t.Fatalf("expected signed downloadUrl, got %q", downloadURL)
|
||||
@ -847,8 +857,8 @@ func TestExecuteSessionTaskGatewayExportsOpenClawArtifacts(t *testing.T) {
|
||||
if got := shared.BoolArg(shared.StringArg(exportParams, "includeContent", ""), true); got {
|
||||
t.Fatalf("expected OpenClaw artifact export to omit content, got %#v", exportParams)
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -886,8 +896,8 @@ func TestExecuteSessionTaskGatewayDoesNotTreatPromptTextAsArtifactContract(t *te
|
||||
t.Fatalf("expected no stale artifacts when gateway exported none, got %#v", response["artifacts"])
|
||||
}
|
||||
exportParams := gateway.LastArtifactExportParams()
|
||||
if got := strings.TrimSpace(shared.StringArg(exportParams, "artifactScope", "")); got != "" {
|
||||
t.Fatalf("expected bridge not to impose scoped artifact export params, got %#v", exportParams)
|
||||
if got := strings.TrimSpace(shared.StringArg(exportParams, "artifactScope", "")); got == "" {
|
||||
t.Fatalf("expected bridge to export the prepared task artifact scope, got %#v", exportParams)
|
||||
}
|
||||
if _, ok := exportParams["latestIfEmpty"]; ok {
|
||||
t.Fatalf("expected no latestIfEmpty fallback export param, got %#v", exportParams)
|
||||
@ -898,8 +908,69 @@ func TestExecuteSessionTaskGatewayDoesNotTreatPromptTextAsArtifactContract(t *te
|
||||
if got := shared.BoolArg(shared.StringArg(exportParams, "includeContent", ""), true); got {
|
||||
t.Fatalf("expected latest workspace export to omit content, got %#v", exportParams)
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteSessionTaskGatewayExportsWithActualOpenClawRunID(t *testing.T) {
|
||||
gateway := newAcpFakeOpenClawGateway(t)
|
||||
gateway.alternateRunID = "openclaw-run-actual"
|
||||
defer gateway.Close()
|
||||
|
||||
t.Setenv("GATEWAY_RPC_URL", gateway.URL())
|
||||
t.Setenv("BRIDGE_AUTH_TOKEN", "bridge-token")
|
||||
|
||||
server := NewServer()
|
||||
response, rpcErr := server.executeSessionTask(task{
|
||||
req: shared.RPCRequest{
|
||||
Method: "session.start",
|
||||
Params: map[string]any{
|
||||
"sessionId": "session-openclaw-actual-run",
|
||||
"threadId": "thread-openclaw-actual-run",
|
||||
"taskPrompt": "make artifact",
|
||||
"workingDirectory": t.TempDir(),
|
||||
"routing": map[string]any{
|
||||
"routingMode": "explicit",
|
||||
"explicitExecutionTarget": "gateway",
|
||||
"preferredGatewayProviderId": "openclaw",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if rpcErr != nil {
|
||||
t.Fatalf("expected gateway response, got rpc error: %#v", rpcErr)
|
||||
}
|
||||
if got := response["runId"]; got != "openclaw-run-actual" {
|
||||
t.Fatalf("expected response to keep actual OpenClaw runId, got %#v", response)
|
||||
}
|
||||
if gateway.ArtifactPrepareCount() != 2 {
|
||||
t.Fatalf("expected bridge to prepare initial turn scope and actual OpenClaw run scope, got %d", gateway.ArtifactPrepareCount())
|
||||
}
|
||||
exportParams := gateway.LastArtifactExportParams()
|
||||
if got := strings.TrimSpace(shared.StringArg(exportParams, "runId", "")); got != "openclaw-run-actual" {
|
||||
t.Fatalf("expected artifact export to use actual OpenClaw runId, got %#v", exportParams)
|
||||
}
|
||||
if got := strings.TrimSpace(shared.StringArg(exportParams, "artifactScope", "")); got != "tasks/thread-openclaw-actual-run/openclaw-run-actual" {
|
||||
t.Fatalf("expected artifact export to use actual OpenClaw run scope, got %#v", exportParams)
|
||||
}
|
||||
artifacts, ok := response["artifacts"].([]map[string]any)
|
||||
if !ok || len(artifacts) != 1 {
|
||||
t.Fatalf("expected actual-run artifact manifest, got %#v", response["artifacts"])
|
||||
}
|
||||
downloadURL := strings.TrimSpace(shared.StringArg(artifacts[0], "downloadUrl", ""))
|
||||
parsedDownloadURL, err := url.Parse(downloadURL)
|
||||
if err != nil {
|
||||
t.Fatalf("parse downloadUrl: %v", err)
|
||||
}
|
||||
if got := parsedDownloadURL.Query().Get("runId"); got != "openclaw-run-actual" {
|
||||
t.Fatalf("expected download URL to use actual OpenClaw runId, got %q from %q", got, downloadURL)
|
||||
}
|
||||
if got := parsedDownloadURL.Query().Get("artifactScope"); got != "tasks/thread-openclaw-actual-run/openclaw-run-actual" {
|
||||
t.Fatalf("expected download URL to use actual OpenClaw artifact scope, got %q", got)
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "xworkmate.artifacts.prepare", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected bridge to reprepare actual OpenClaw run before wait/export, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -939,8 +1010,8 @@ func TestExecuteSessionMessageGatewayDoesNotRewriteClaimedArtifactsWithoutGatewa
|
||||
if gateway.ArtifactExportCount() != 1 {
|
||||
t.Fatalf("expected one post-run artifact export sync, got %d", gateway.ArtifactExportCount())
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -986,11 +1057,11 @@ func TestExecuteSessionMessageGatewayExportsArtifactsWithoutPromptHeuristic(t *t
|
||||
t.Fatalf("expected artifact response success, got %#v", response)
|
||||
}
|
||||
exportParams := gateway.LastArtifactExportParams()
|
||||
if got := strings.TrimSpace(shared.StringArg(exportParams, "artifactScope", "")); got != "" {
|
||||
t.Fatalf("expected bridge not to impose artifact scope from message prompt, got %#v", exportParams)
|
||||
if got := strings.TrimSpace(shared.StringArg(exportParams, "artifactScope", "")); got == "" {
|
||||
t.Fatalf("expected bridge to export the prepared task artifact scope, got %#v", exportParams)
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected connect, artifact prepare, chat.send, agent.wait, then artifact export, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1613,26 +1684,30 @@ func TestExtractArtifactPayloadsRejectsUnsafeDownloadURLArtifactNames(t *testing
|
||||
}
|
||||
|
||||
type acpFakeOpenClawGateway struct {
|
||||
server *http.Server
|
||||
listener net.Listener
|
||||
connectCount atomic.Int32
|
||||
chatSendCount atomic.Int32
|
||||
agentWaitCount atomic.Int32
|
||||
artifactCount atomic.Int32
|
||||
artifactReadCount atomic.Int32
|
||||
artifactReadFailures atomic.Int32
|
||||
closeNextChatSend atomic.Bool
|
||||
alwaysCloseChatSend atomic.Bool
|
||||
agentWaitDelayMs atomic.Int64
|
||||
largeGatewayPayloadBytes atomic.Int64
|
||||
emitAgentDelta atomic.Bool
|
||||
lastConnectClient atomic.Value
|
||||
lastArtifactExportParams atomic.Value
|
||||
lastAgentWaitParams atomic.Value
|
||||
mu sync.Mutex
|
||||
methods []string
|
||||
runMessages map[string]string
|
||||
artifactMode string
|
||||
server *http.Server
|
||||
listener net.Listener
|
||||
connectCount atomic.Int32
|
||||
chatSendCount atomic.Int32
|
||||
agentWaitCount atomic.Int32
|
||||
artifactPrepareCount atomic.Int32
|
||||
artifactCount atomic.Int32
|
||||
artifactReadCount atomic.Int32
|
||||
artifactReadFailures atomic.Int32
|
||||
closeNextChatSend atomic.Bool
|
||||
alwaysCloseChatSend atomic.Bool
|
||||
agentWaitDelayMs atomic.Int64
|
||||
largeGatewayPayloadBytes atomic.Int64
|
||||
emitAgentDelta atomic.Bool
|
||||
lastConnectClient atomic.Value
|
||||
lastChatSendParams atomic.Value
|
||||
lastArtifactPrepareParams atomic.Value
|
||||
lastArtifactExportParams atomic.Value
|
||||
lastAgentWaitParams atomic.Value
|
||||
mu sync.Mutex
|
||||
methods []string
|
||||
runMessages map[string]string
|
||||
artifactMode string
|
||||
alternateRunID string
|
||||
}
|
||||
|
||||
func newAcpFakeOpenClawGateway(t *testing.T) *acpFakeOpenClawGateway {
|
||||
@ -1734,6 +1809,7 @@ func newAcpFakeOpenClawGateway(t *testing.T) *acpFakeOpenClawGateway {
|
||||
return
|
||||
}
|
||||
params := shared.AsMap(frame["params"])
|
||||
fake.lastChatSendParams.Store(params)
|
||||
if strings.TrimSpace(shared.StringArg(params, "message", "")) == "fail" {
|
||||
_ = conn.WriteJSON(map[string]any{
|
||||
"type": "res",
|
||||
@ -1747,6 +1823,9 @@ func newAcpFakeOpenClawGateway(t *testing.T) *acpFakeOpenClawGateway {
|
||||
continue
|
||||
}
|
||||
runID := strings.TrimSpace(shared.StringArg(params, "idempotencyKey", "fake-run"))
|
||||
if strings.TrimSpace(fake.alternateRunID) != "" {
|
||||
runID = strings.TrimSpace(fake.alternateRunID)
|
||||
}
|
||||
fake.recordRunMessage(runID, strings.TrimSpace(shared.StringArg(params, "message", "")))
|
||||
_ = conn.WriteJSON(map[string]any{
|
||||
"type": "res",
|
||||
@ -1758,7 +1837,9 @@ func newAcpFakeOpenClawGateway(t *testing.T) *acpFakeOpenClawGateway {
|
||||
},
|
||||
})
|
||||
case "xworkmate.artifacts.prepare":
|
||||
fake.artifactPrepareCount.Add(1)
|
||||
params := shared.AsMap(frame["params"])
|
||||
fake.lastArtifactPrepareParams.Store(params)
|
||||
runID := strings.TrimSpace(shared.StringArg(params, "runId", "fake-run"))
|
||||
sessionKey := strings.TrimSpace(shared.StringArg(params, "sessionKey", "main"))
|
||||
artifactScope := "tasks/" + sessionKey + "/" + runID
|
||||
@ -2067,6 +2148,11 @@ func (f *acpFakeOpenClawGateway) ChatSendCount() int {
|
||||
return int(f.chatSendCount.Load())
|
||||
}
|
||||
|
||||
func (f *acpFakeOpenClawGateway) LastChatSendParams() map[string]any {
|
||||
params, _ := f.lastChatSendParams.Load().(map[string]any)
|
||||
return params
|
||||
}
|
||||
|
||||
func (f *acpFakeOpenClawGateway) AgentWaitCount() int {
|
||||
return int(f.agentWaitCount.Load())
|
||||
}
|
||||
@ -2076,6 +2162,15 @@ func (f *acpFakeOpenClawGateway) LastAgentWaitParams() map[string]any {
|
||||
return params
|
||||
}
|
||||
|
||||
func (f *acpFakeOpenClawGateway) ArtifactPrepareCount() int {
|
||||
return int(f.artifactPrepareCount.Load())
|
||||
}
|
||||
|
||||
func (f *acpFakeOpenClawGateway) LastArtifactPrepareParams() map[string]any {
|
||||
params, _ := f.lastArtifactPrepareParams.Load().(map[string]any)
|
||||
return params
|
||||
}
|
||||
|
||||
func (f *acpFakeOpenClawGateway) ArtifactExportCount() int {
|
||||
return int(f.artifactCount.Load())
|
||||
}
|
||||
|
||||
@ -561,8 +561,8 @@ func TestHTTPHandlerGatewayOpenClawFiltersRawGatewayEventsAndKeepsFinalResult(t
|
||||
if !sawFinal {
|
||||
t.Fatalf("expected final result envelope, got %q", bodyText)
|
||||
}
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected artifact workflow methods to stay unchanged, got %#v", got)
|
||||
if got := gateway.Methods(); !sameMethods(got, []string{"connect", "xworkmate.artifacts.prepare", "chat.send", "agent.wait", "xworkmate.artifacts.export"}) {
|
||||
t.Fatalf("expected artifact workflow methods to prepare before chat.send, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user