diff --git a/docs/ai-context/module-boundary.md b/docs/ai-context/module-boundary.md index 8c647d5d..f8c00695 100644 --- a/docs/ai-context/module-boundary.md +++ b/docs/ai-context/module-boundary.md @@ -227,7 +227,7 @@ App → Bridge → Gateway: device.pair.approve / device.pair.reject | `xworkmate.artifacts.collect-and-snapshot` | `{sessionKey, runId, artifactScope?, sinceUnixMs?, expectedArtifactDirs?}` | `{artifacts[], warnings[]}` | | `xworkmate.artifacts.list` | `{sessionKey, runId, ...}` | `{artifacts[] (不含内容), manifestMarkdown}` | | `xworkmate.artifacts.read` | `{sessionKey, runId, artifactScope?, relativePath?, artifactRef?}` | `{artifacts[0], manifestMarkdown}` | -| `xworkmate.tasks.get` | `{sessionKey, runId}` | `{taskStatus, status, artifacts[], warnings[]}` | +| `xworkmate.tasks.get` | `{appThreadKey, openclawSessionKey, runId/taskId}` | `{taskStatus, status, artifacts[], warnings[]}` | `expectedArtifactDirs` has a single upstream source: `session.start.metadata.xworkmateTaskArtifactContract.expectedArtifactDirs`. diff --git a/docs/architecture/chain-map-task-execution.md b/docs/architecture/chain-map-task-execution.md index 48018d42..8a9e6949 100644 --- a/docs/architecture/chain-map-task-execution.md +++ b/docs/architecture/chain-map-task-execution.md @@ -43,6 +43,7 @@ xworkmate-app │ ├─ params: sessionId, threadId, prompt, workingDirectory │ ├─ routing: executionTarget=gateway, preferredGatewayProviderId=openclaw │ └─ metadata: xworkmateTaskArtifactContract + │ └─ appThreadKey only; no prefilled openclawSessionKey └─ Listen for SSE session.update events ├─ status=running → poll xworkmate.tasks.get ├─ terminal snapshot → applyGatewayChatResult() @@ -198,6 +199,8 @@ now copied into tasks///artifacts/ before export. xworkmate.tasks.get: Bridge forwards typed lookup to the plugin/native task-registry. + The request uses the persisted association: + {appThreadKey, openclawSessionKey, runId/taskId} Terminal state comes from native task records only. Missing native records return structured errors such as no_native_task_record instead of inferring success from artifacts or reconstructing a Bridge task dictionary. diff --git a/docs/architecture/openclaw-session-key-state-and-data-flow.md b/docs/architecture/openclaw-session-key-state-and-data-flow.md index 70f7b04d..791750a9 100644 --- a/docs/architecture/openclaw-session-key-state-and-data-flow.md +++ b/docs/architecture/openclaw-session-key-state-and-data-flow.md @@ -16,14 +16,15 @@ session identity boundary. Rules: - App code may keep `sessionKey` for local TaskThread and UI session APIs. -- XWorkmate OpenClaw integration payloads must use `appThreadKey` and - `openclawSessionKey`. -- Bridge may send `sessionKey` only when calling OpenClaw native APIs that - require that field, such as `chat.send`. +- App `session.start` only carries `appThreadKey` in typed metadata; it does + not preemptively choose the OpenClaw native session key. +- Bridge resolves or reuses `openclawSessionKey`, persists the mapping, and + then uses that key for OpenClaw native APIs such as `chat.send`. - Plugin lookup must read `appThreadKey -> openclawSessionKey` from `SessionEntry.pluginExtensions`, not from string replace or reverse parsing. -- Legacy `sessionKey` aliases are not accepted from App or Bridge as mapping - inputs. +- `xworkmate.tasks.get` must be invoked with the persisted association + (`appThreadKey`, `openclawSessionKey`, `runId/taskId`), not with a legacy + `sessionKey` alias. ## State Graph diff --git a/lib/runtime/go_task_service_client.dart b/lib/runtime/go_task_service_client.dart index a123b701..1ba27242 100644 --- a/lib/runtime/go_task_service_client.dart +++ b/lib/runtime/go_task_service_client.dart @@ -328,7 +328,6 @@ class GoTaskServiceRequest { if (_usesGatewaySessionMode(acpMode)) ...{ 'executionTarget': normalizedTarget.promptValue, 'appThreadKey': threadId, - 'openclawSessionKey': threadId, if (agentId.trim().isNotEmpty) 'agentId': agentId.trim(), if (metadata.isNotEmpty) 'metadata': metadata, }, diff --git a/test/runtime/assistant_execution_target_test.dart b/test/runtime/assistant_execution_target_test.dart index 184b8ddd..7461223a 100644 --- a/test/runtime/assistant_execution_target_test.dart +++ b/test/runtime/assistant_execution_target_test.dart @@ -94,6 +94,32 @@ void main() { expect(params, isNot(contains('artifactScope'))); }); + test( + 'gateway ACP params expose appThreadKey without preempting the OpenClaw session key', + () { + const request = GoTaskServiceRequest( + sessionId: 'draft:1780718661814229-2', + threadId: 'draft:1780718661814229-2', + target: AssistantExecutionTarget.gateway, + prompt: 'collect latest AI news', + workingDirectory: '/tmp/xworkmate-thread', + model: '', + thinking: 'low', + selectedSkills: [], + inlineAttachments: [], + localAttachments: [], + agentId: '', + metadata: {}, + provider: SingleAgentProvider.openclaw, + ); + + final params = request.toExternalAcpParams(); + + expect(params['appThreadKey'], 'draft:1780718661814229-2'); + expect(params, isNot(contains('openclawSessionKey'))); + }, + ); + test('recognizes openclaw as the canonical gateway provider', () { final provider = SingleAgentProvider.fromJsonValue('openclaw');