Refactor OpenClaw task integration as thin adapter
This commit is contained in:
parent
ea781b5206
commit
c16704b052
@ -97,7 +97,8 @@ lib/runtime/external_code_agent_acp_desktop_transport.dart
|
||||
lib/app/app_controller_openclaw_task_queue.dart
|
||||
OpenClawTaskQueue
|
||||
→ 本地队列管理 (max 5 active, 20 queued)
|
||||
→ 持久化 & 恢复 (pollOpenClawTaskAssociationInternal)
|
||||
→ 持久化 typed association
|
||||
→ 恢复查询只发送 appThreadKey/openclawSessionKey/runId 到 native task-registry
|
||||
```
|
||||
|
||||
Protocol boundary:
|
||||
@ -124,7 +125,9 @@ Protocol boundary:
|
||||
### 断点风险:
|
||||
- 网关 WebSocket 断连 → 任务丢失
|
||||
- 分布式转发 hop=3 限制 → 深层拓扑不可达
|
||||
- 任务轮询恢复依赖 `xworkmate.tasks.get` → 非实时
|
||||
- 任务轮询恢复依赖 `xworkmate.tasks.get` → Bridge 只转发 typed lookup,
|
||||
terminal state 以 OpenClaw native task-registry 为准;缺少 native task
|
||||
record 时返回 `no_native_task_record`,不从 artifactScope/runId 重建任务。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -198,26 +198,32 @@ Fragile:
|
||||
- signing secret rotation invalidates all existing refs
|
||||
```
|
||||
|
||||
## State 5: Snapshot (Bridge)
|
||||
## State 5: Native Task Lookup And Artifact Snapshot
|
||||
|
||||
```
|
||||
Caller: completeOpenClawTask() in openclaw_async_tasks.go
|
||||
triggered by probeOpenClawTask() detecting completion
|
||||
Caller: App polling/recovery via xworkmate.tasks.get
|
||||
Bridge forwards typed appThreadKey/openclawSessionKey/runId to Plugin
|
||||
|
||||
Process:
|
||||
1. Call gateway.request('xworkmate.artifacts.collect-and-snapshot')
|
||||
1. Plugin resolves task state from OpenClaw native task-registry
|
||||
→ api.runtime.tasks.runs.bindSession({sessionKey: openclawSessionKey})
|
||||
→ resolve(runId) or findLatest()
|
||||
|
||||
2. Call gateway.request('xworkmate.artifacts.collect-and-snapshot')
|
||||
→ Copy OpenClaw media/tmp outputs into the task scope
|
||||
|
||||
2. Call gateway.request('xworkmate.artifacts.export')
|
||||
3. Call gateway.request('xworkmate.artifacts.export')
|
||||
→ Get manifest from plugin
|
||||
|
||||
3. openClawArtifactExport()
|
||||
4. openClawArtifactExport()
|
||||
→ Transform manifest files into stable result shape
|
||||
→ decorateOpenClawArtifactDownloadURLs()
|
||||
→ Replace each file.ref with signed download URL:
|
||||
/artifacts/openclaw/download?ref=<signed>&t=<expiry>
|
||||
|
||||
4. Build terminal snapshot:
|
||||
5. Return task-registry-backed snapshot:
|
||||
→ Terminal success/failure is not decided by Bridge state
|
||||
→ Missing native task record returns no_native_task_record
|
||||
{
|
||||
success: true,
|
||||
status: "completed",
|
||||
@ -232,10 +238,16 @@ Process:
|
||||
}
|
||||
}
|
||||
|
||||
5. Store snapshot for xworkmate.tasks.get queries
|
||||
5. Do not store terminal task truth for xworkmate.tasks.get queries.
|
||||
The query path forwards to OpenClaw native task-registry through the plugin.
|
||||
|
||||
6. Send SSE session.update to app
|
||||
|
||||
Removed compatibility paths:
|
||||
- Bridge no longer falls back when xworkmate.session.prepare is unsupported.
|
||||
- Bridge no longer reassociates OpenClaw tasks from artifactScope/runId.
|
||||
- Bridge no longer treats artifact export as terminal task-state evidence.
|
||||
|
||||
Fragile:
|
||||
- If export returns empty manifest, snapshot has no artifacts
|
||||
- Artifact download URLs expire after 24h
|
||||
@ -339,7 +351,7 @@ stateDiagram-v2
|
||||
| Plugin workspace | resolveWorkspaceDir() | `~/.openclaw/workspace` |
|
||||
| Plugin scope | tasks/<session>/<run>/ | `<workspace>/tasks/<s>/<r>/` |
|
||||
| Plugin export | exportXWorkmateArtifacts() | Scans only scope dir |
|
||||
| Bridge snapshot | completeOpenClawTask() | In-memory, 24h signed URLs |
|
||||
| Bridge snapshot | xworkmate.tasks.get proxy | Native task-registry + plugin artifact manifest |
|
||||
| Bridge download | /artifacts/openclaw/download | Proxied from plugin read |
|
||||
| App sync | syncArtifactsFromBridge() | `~/.xworkmate/threads/<s>/` |
|
||||
| OpenClaw media | saveMediaBuffer(subdir) | `~/.openclaw/media/<subdir>/` |
|
||||
|
||||
@ -165,9 +165,9 @@ No persistence:
|
||||
|
||||
| Method | Params | Returns |
|
||||
|--------|--------|---------|
|
||||
| `xworkmate.tasks.get` | sessionId, threadId, turnId (optional) | Terminal snapshot or running status |
|
||||
| `xworkmate.tasks.cancel` | sessionId, threadId, turnId | Cancel confirmation |
|
||||
| `reassociateOpenClawTask` | taskHandle (from stored params) | Reconnected session |
|
||||
| `xworkmate.tasks.get` | appThreadKey, openclawSessionKey, runId/taskId | Native task-registry snapshot or structured lookup error |
|
||||
| `xworkmate.tasks.cancel` | appThreadKey, openclawSessionKey, runId/taskId | Cancel confirmation |
|
||||
| Removed: Bridge task reassociation | artifactScope/runId-derived taskHandle | No longer supported; route through native task registry |
|
||||
|
||||
## App Recovery Flow (Detailed)
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ xworkmate-bridge
|
||||
│ ├─ queueTimeout: 10 min
|
||||
│ └─ Returns: admission slot or OPENCLAW_GATEWAY_BUSY
|
||||
│
|
||||
└─ startOpenClawGatewayTask()
|
||||
└─ startOpenClawGatewayTask()
|
||||
├─ ensureProductionGatewayConnected()
|
||||
├─ openClawArtifactPrepare()
|
||||
│ └─ gateway.request('xworkmate.session.prepare')
|
||||
@ -85,20 +85,18 @@ xworkmate-bridge
|
||||
│ sessionKey is the OpenClaw native field and equals openclawSessionKey
|
||||
│ (no expectedArtifactDirs root field)
|
||||
│
|
||||
├─ Create OpenClawTaskRecord
|
||||
├─ Keep OpenClawTaskRecord only as live transport context
|
||||
│ ├─ SessionID, ThreadID, TurnID, RunID
|
||||
│ ├─ SessionKey (from gateway response)
|
||||
│ ├─ TaskLoadClass (short_task/long_task/complex_chain_task)
|
||||
│ ├─ RuntimeBudgetMinutes (10/30/60)
|
||||
│ └─ PreparedArtifact scope ref
|
||||
│
|
||||
└─ startOpenClawTaskMonitor()
|
||||
└─ Every 1s: probeOpenClawTask()
|
||||
└─ gateway.request('agent.wait', timeout=1s)
|
||||
├─ completed → completeOpenClawTask()
|
||||
├─ failed → failOpenClawTask()
|
||||
├─ SLA expired → TASK_SLA_EXPIRED
|
||||
└─ silent failure >10min → cleanup
|
||||
└─ Native task status lookup
|
||||
└─ xworkmate.tasks.get forwards typed
|
||||
{appThreadKey, openclawSessionKey, runId/taskId}
|
||||
to OpenClaw native task-registry via the plugin.
|
||||
Bridge no longer rebuilds task state from artifactScope/runId.
|
||||
|
||||
───────────────────────────────────────────────────────────
|
||||
Protocol: Custom JSON-RPC v4 over WebSocket
|
||||
@ -177,11 +175,11 @@ now copied into tasks/<session>/<run>/artifacts/ before export.
|
||||
───────────────────────────────────────────────────────────
|
||||
|
||||
Back to xworkmate-bridge:
|
||||
completeOpenClawTask()
|
||||
terminal transport handling
|
||||
├─ Call xworkmate.artifacts.collect-and-snapshot via gateway
|
||||
├─ Call xworkmate.artifacts.export via gateway
|
||||
├─ Collect artifact manifest
|
||||
├─ Build terminal snapshot with:
|
||||
├─ Build App transport payload with:
|
||||
│ ├─ status: completed/failed/cancelled
|
||||
│ ├─ artifacts: { items: [...], scope: "..." }
|
||||
│ └─ text: final output
|
||||
@ -192,6 +190,12 @@ now copied into tasks/<session>/<run>/artifacts/ before export.
|
||||
│
|
||||
└─ Send SSE session.update to app
|
||||
|
||||
xworkmate.tasks.get:
|
||||
Bridge forwards typed lookup to the plugin/native task-registry.
|
||||
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.
|
||||
|
||||
Back to xworkmate-app:
|
||||
ExternalCodeAgentAcpDesktopTransport
|
||||
├─ Receive terminal snapshot via SSE or xworkmate.tasks.get
|
||||
|
||||
@ -61,7 +61,7 @@ Scope: xworkmate-app, xworkmate-bridge, openclaw-multi-session-plugins, openclaw
|
||||
→ startOpenClawGatewayTask()
|
||||
→ openClawArtifactPrepare() // calls xworkmate.artifacts.prepare
|
||||
→ gatewayruntime.send('chat.send')
|
||||
→ startOpenClawTaskMonitor() // background probe every 1s
|
||||
→ returns running handle; Bridge does not own terminal state
|
||||
|
||||
3. openclaw.svc.plus
|
||||
Gateway receives 'chat.send'
|
||||
@ -75,9 +75,9 @@ Scope: xworkmate-app, xworkmate-bridge, openclaw-multi-session-plugins, openclaw
|
||||
→ returns manifest + base64 files to bridge
|
||||
|
||||
5. xworkmate-bridge
|
||||
openClawArtifactExport() → collects artifacts from plugin
|
||||
→ completeOpenClawTask() → builds terminal snapshot
|
||||
→ sends session.update (SSE) to app
|
||||
forwards native transcript/task events to app
|
||||
→ xworkmate.tasks.get is forwarded to Plugin/OpenClaw native task-registry
|
||||
→ artifact collect/export remains an explicit proxy operation
|
||||
|
||||
6. xworkmate-app
|
||||
ExternalCodeAgentAcpDesktopTransport receives SSE
|
||||
@ -171,15 +171,10 @@ type OpenClawTaskRecord struct {
|
||||
SessionKey string // 传递给 plugin 做 scope
|
||||
GatewayProviderID string // "openclaw"
|
||||
TaskLoadClass string // short_task/long_task/complex_chain_task
|
||||
ArtifactSinceUnixMs int64 // artifact 时间窗口起始
|
||||
RuntimeBudgetMinutes int // 10/30/60
|
||||
StartedAt, DeadlineAt, LastProbeAt time.Time
|
||||
StartedAt, DeadlineAt time.Time
|
||||
ProgressStage, ProgressMessage string
|
||||
ProgressTerminal bool
|
||||
FirstSilentFailureAt time.Time // 静默失败计时
|
||||
PreparedArtifact *openClawPreparedArtifactScope
|
||||
ArtifactContract openClawArtifactContract
|
||||
AdmissionRelease func() // 释放并发槽位
|
||||
}
|
||||
```
|
||||
|
||||
@ -307,7 +302,7 @@ xworkmate-bridge 的 session 存储在 `Server.sessions map[string]*session`,
|
||||
|
||||
**6.7 TaskSnapshot 字段不完整**
|
||||
|
||||
Bridge 返回的 terminal snapshot 依赖 `completeOpenClawTask()` 正确组装 artifact 列表。如果 plugin 返回的 artifact manifest 中有文件但内容为空、或相对路径超出 scope,bridge 的 snapshot 会缺少 artifact 条目。
|
||||
Bridge 不再组装 terminal snapshot。Terminal success/failure 必须来自 OpenClaw native task-registry;artifact manifest 只由 Plugin 的 collect/export/read 路径提供。如果 plugin 返回的 artifact manifest 中有文件但内容为空、或相对路径超出 scope,App 侧 artifact 栏可能缺少条目,但任务终态不应因此由 Bridge 推断。
|
||||
|
||||
**6.8 SSE 流中断后的轮询策略**
|
||||
|
||||
|
||||
@ -14,8 +14,8 @@
|
||||
|
||||
- Do not modify `/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw.svc.plus`.
|
||||
- Do not rely on prompt text parsing for metadata.
|
||||
- Do not use `agent:main:${appThreadKey}` or `replace("agent:main:", "")` as the new-path session mapping.
|
||||
- Legacy string derivation is allowed only as one-time migration that writes a durable mapping.
|
||||
- Do not reverse-map session keys from string conventions. The deterministic `agent:main:<appThreadKey>` form is allowed only for initial native session creation before prepare persists the mapping.
|
||||
- Legacy string derivation and migration-only mapping fields are removed from the current flow.
|
||||
- Do not restore plugin-owned task DB, session DB, or event bus.
|
||||
- Validation must reference `docs/cases/`, especially `docs/cases/openclaw-gateway-e2e-regression/README.md`.
|
||||
|
||||
@ -48,8 +48,7 @@ type XWorkmateSessionMappingV1 = {
|
||||
expectedArtifactDirs: string[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
source: "session_start" | "bridge_prepare" | "legacy_migration";
|
||||
legacyDerived?: boolean;
|
||||
source: "session_start" | "bridge_prepare";
|
||||
};
|
||||
```
|
||||
|
||||
@ -69,7 +68,7 @@ Add tests for:
|
||||
- Mapping is written to `SessionEntry.pluginExtensions`.
|
||||
- Idempotent same mapping updates `updatedAt`.
|
||||
- Conflicting existing mapping fails closed.
|
||||
- Legacy derivation, if needed, writes `legacyDerived: true` and persists the mapping.
|
||||
- Missing typed mapping returns `mapping_not_found` / `invalid_lookup`; current flow does not derive replacement keys.
|
||||
|
||||
**Step 2: Run failing test**
|
||||
|
||||
@ -133,7 +132,7 @@ In `index.ts`, register `xworkmate.session.prepare`. It should:
|
||||
- call `prepareXWorkmateArtifacts`;
|
||||
- return `{ ok: true, mapping, artifactScope, artifactDirectory, expectedArtifactDirs }`.
|
||||
|
||||
Keep `xworkmate.artifacts.prepare/export/collect-and-snapshot` as thin artifact operations. Remove in-memory `createXWorkmateTaskStore()` as a required source of truth.
|
||||
Keep `xworkmate.artifacts.prepare/export/collect-and-snapshot` as thin artifact operations. The in-memory plugin task store is removed and must not be reintroduced as a task source of truth.
|
||||
|
||||
**Step 4: Run plugin suite**
|
||||
|
||||
@ -322,7 +321,7 @@ Retain only transport/reconciliation caches that have clear owner, scope, and in
|
||||
Run:
|
||||
|
||||
```bash
|
||||
rg -n "agent:main:\\$|replace\\(|ThreadSessionMapper|createXWorkmateTaskStore|sessionMappingsBy|records = new Map|bridgeAgents" \
|
||||
rg -n "ThreadSessionMapper|sessionMappingsBy|records = new Map|bridgeAgents" \
|
||||
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins \
|
||||
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge \
|
||||
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app
|
||||
|
||||
@ -62,9 +62,7 @@ class ExternalCodeAgentAcpDesktopTransport implements GoTaskServiceClient {
|
||||
if (update.isDone && update.message.trim().isNotEmpty) {
|
||||
completedMessage = update.message.trim();
|
||||
}
|
||||
if (update.payload['status']?.toString().trim().toLowerCase() ==
|
||||
'running' &&
|
||||
(update.payload['runId']?.toString().trim().isNotEmpty == true)) {
|
||||
if (OpenClawTaskAssociation.fromJsonOrNull(update.payload) != null) {
|
||||
runningTaskSnapshot = <String, dynamic>{...update.payload};
|
||||
}
|
||||
onUpdate(update);
|
||||
@ -130,6 +128,9 @@ class ExternalCodeAgentAcpDesktopTransport implements GoTaskServiceClient {
|
||||
final association = OpenClawTaskAssociation.fromJsonOrNull(
|
||||
runningTaskSnapshot,
|
||||
);
|
||||
if (association == null) {
|
||||
return null;
|
||||
}
|
||||
final attempts = _recoveryAttemptsForRequest(request);
|
||||
for (var attempt = 0; attempt < attempts; attempt += 1) {
|
||||
if (attempt > 0) {
|
||||
@ -139,12 +140,7 @@ class ExternalCodeAgentAcpDesktopTransport implements GoTaskServiceClient {
|
||||
try {
|
||||
response = await _client.request(
|
||||
method: 'xworkmate.tasks.get',
|
||||
params:
|
||||
association?.toTaskGetParams() ??
|
||||
<String, dynamic>{
|
||||
'sessionId': request.sessionId,
|
||||
'threadId': request.threadId,
|
||||
},
|
||||
params: association.toTaskGetParams(),
|
||||
endpointOverride: endpoint,
|
||||
);
|
||||
} on GatewayAcpException {
|
||||
@ -177,9 +173,11 @@ class ExternalCodeAgentAcpDesktopTransport implements GoTaskServiceClient {
|
||||
final result = _recoveredResultFromTaskSnapshot(snapshot);
|
||||
final resultArtifacts = _castMap(result['artifacts']);
|
||||
final artifactItems = resultArtifacts['items'] ?? resultArtifacts;
|
||||
final hasArtifacts = result.isNotEmpty &&
|
||||
final hasArtifacts =
|
||||
result.isNotEmpty &&
|
||||
(artifactItems is List && artifactItems.isNotEmpty ||
|
||||
result['artifacts'] is List && (result['artifacts'] as List).isNotEmpty);
|
||||
result['artifacts'] is List &&
|
||||
(result['artifacts'] as List).isNotEmpty);
|
||||
if (!hasArtifacts && status == 'completed' && attempt < attempts - 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -993,19 +993,10 @@ class OpenClawTaskAssociation {
|
||||
|
||||
Map<String, dynamic> toTaskGetParams() {
|
||||
return <String, dynamic>{
|
||||
'sessionId': sessionId,
|
||||
'threadId': threadId,
|
||||
'turnId': turnId,
|
||||
'runId': runId,
|
||||
'artifactScope': artifactScope,
|
||||
'artifactDirectory': artifactDirectory,
|
||||
'gatewayProviderId': gatewayProviderId,
|
||||
'taskLoadClass': taskLoadClass,
|
||||
'appThreadKey': appThreadKey,
|
||||
'openclawSessionKey': openclawSessionKey,
|
||||
'includeArtifacts': true,
|
||||
'requiredArtifactExtensions': requiredArtifactExtensions,
|
||||
'expectedArtifactExtensions': expectedArtifactExtensions,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1015,14 +1006,10 @@ class OpenClawTaskAssociation {
|
||||
}
|
||||
final json = value.cast<String, dynamic>();
|
||||
final runId = json['runId']?.toString().trim() ?? '';
|
||||
final artifactScope = json['artifactScope']?.toString().trim() ?? '';
|
||||
final appThreadKey = json['appThreadKey']?.toString().trim() ?? '';
|
||||
final openclawSessionKey =
|
||||
json['openclawSessionKey']?.toString().trim() ?? '';
|
||||
if (runId.isEmpty ||
|
||||
artifactScope.isEmpty ||
|
||||
appThreadKey.isEmpty ||
|
||||
openclawSessionKey.isEmpty) {
|
||||
if (runId.isEmpty || appThreadKey.isEmpty || openclawSessionKey.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
double asDouble(Object? raw) {
|
||||
@ -1037,7 +1024,7 @@ class OpenClawTaskAssociation {
|
||||
threadId: json['threadId']?.toString().trim() ?? '',
|
||||
turnId: json['turnId']?.toString().trim() ?? '',
|
||||
runId: runId,
|
||||
artifactScope: artifactScope,
|
||||
artifactScope: json['artifactScope']?.toString().trim() ?? '',
|
||||
artifactDirectory: json['artifactDirectory']?.toString().trim() ?? '',
|
||||
gatewayProviderId:
|
||||
json['gatewayProviderId']?.toString().trim().isNotEmpty == true
|
||||
|
||||
@ -399,6 +399,12 @@ class _AssistantArtifactSidebarState extends State<AssistantArtifactSidebar> {
|
||||
}
|
||||
|
||||
String _filesEmptyMessage(AssistantArtifactSnapshot snapshot) {
|
||||
if (widget.artifactSyncStatus.trim().toLowerCase() == 'failed') {
|
||||
return appText(
|
||||
'本轮没有检测到实际生成的文件。请重新执行,并要求 OpenClaw 在当前 workspace 中创建文件。',
|
||||
'No files were generated for this run. Try again and ask OpenClaw to create files in the current workspace.',
|
||||
);
|
||||
}
|
||||
final filesMessage = snapshot.filesMessage.trim();
|
||||
if (filesMessage.isNotEmpty) {
|
||||
return filesMessage;
|
||||
|
||||
@ -87,7 +87,11 @@ void main() {
|
||||
params['openclawSessionKey'],
|
||||
'agent:main:draft:1780658097668838-1',
|
||||
);
|
||||
expect(params['runId'], 'run-1');
|
||||
expect(params, isNot(contains('sessionKey')));
|
||||
expect(params, isNot(contains('sessionId')));
|
||||
expect(params, isNot(contains('threadId')));
|
||||
expect(params, isNot(contains('artifactScope')));
|
||||
});
|
||||
|
||||
test('recognizes openclaw as the canonical gateway provider', () {
|
||||
|
||||
@ -658,8 +658,19 @@ void main() {
|
||||
if (method == 'session.start') {
|
||||
final event = jsonEncode(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'xworkmate.bridge.accepted',
|
||||
'params': <String, dynamic>{'sessionId': 'unit-fixture-task-a'},
|
||||
'method': 'session.update',
|
||||
'params': <String, dynamic>{
|
||||
'sessionId': 'unit-fixture-task-a',
|
||||
'threadId': 'unit-fixture-task-a',
|
||||
'turnId': 'turn-recovered',
|
||||
'type': 'status',
|
||||
'event': 'running',
|
||||
'status': 'running',
|
||||
'runId': 'turn-recovered',
|
||||
'appThreadKey': 'unit-fixture-task-a',
|
||||
'openclawSessionKey': 'agent:main:unit-fixture-task-a',
|
||||
'gatewayProviderId': 'openclaw',
|
||||
},
|
||||
});
|
||||
final eventBytes = utf8.encode('data: $event\n\n');
|
||||
request.response.headers.set(
|
||||
@ -774,6 +785,10 @@ void main() {
|
||||
'turnId': 'turn-final',
|
||||
'type': 'status',
|
||||
'event': 'completed',
|
||||
'status': 'completed',
|
||||
'runId': 'turn-final',
|
||||
'appThreadKey': 'unit-fixture-task-final',
|
||||
'openclawSessionKey': 'agent:main:unit-fixture-task-final',
|
||||
'message': 'early completed output without artifacts',
|
||||
'success': true,
|
||||
},
|
||||
@ -882,8 +897,19 @@ void main() {
|
||||
if (method == 'session.message') {
|
||||
final event = jsonEncode(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'xworkmate.bridge.accepted',
|
||||
'params': <String, dynamic>{'sessionId': 'unit-fixture-task-sse'},
|
||||
'method': 'session.update',
|
||||
'params': <String, dynamic>{
|
||||
'sessionId': 'unit-fixture-task-sse',
|
||||
'threadId': 'unit-fixture-task-sse',
|
||||
'turnId': 'turn-recovered-sse',
|
||||
'type': 'status',
|
||||
'event': 'running',
|
||||
'status': 'running',
|
||||
'runId': 'turn-recovered-sse',
|
||||
'appThreadKey': 'unit-fixture-task-sse',
|
||||
'openclawSessionKey': 'agent:main:unit-fixture-task-sse',
|
||||
'gatewayProviderId': 'openclaw',
|
||||
},
|
||||
});
|
||||
request.response.headers.set(
|
||||
HttpHeaders.contentTypeHeader,
|
||||
@ -971,8 +997,19 @@ void main() {
|
||||
if (method == 'session.start') {
|
||||
final event = jsonEncode(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'xworkmate.bridge.accepted',
|
||||
'params': <String, dynamic>{'sessionId': 'unit-fixture-task-b'},
|
||||
'method': 'session.update',
|
||||
'params': <String, dynamic>{
|
||||
'sessionId': 'unit-fixture-task-b',
|
||||
'threadId': 'unit-fixture-task-b',
|
||||
'turnId': 'turn-recovered-running',
|
||||
'type': 'status',
|
||||
'event': 'running',
|
||||
'status': 'running',
|
||||
'runId': 'turn-recovered-running',
|
||||
'appThreadKey': 'unit-fixture-task-b',
|
||||
'openclawSessionKey': 'agent:main:unit-fixture-task-b',
|
||||
'gatewayProviderId': 'openclaw',
|
||||
},
|
||||
});
|
||||
final eventBytes = utf8.encode('data: $event\n\n');
|
||||
request.response.headers.set(
|
||||
@ -1092,6 +1129,8 @@ void main() {
|
||||
'artifactDirectory':
|
||||
'/home/ubuntu/.openclaw/workspace/tasks/unit-fixture-task-handle/run-running',
|
||||
'gatewayProviderId': 'openclaw',
|
||||
'appThreadKey': 'unit-fixture-task-handle',
|
||||
'openclawSessionKey': 'agent:main:unit-fixture-task-handle',
|
||||
},
|
||||
});
|
||||
final eventBytes = utf8.encode('data: $event\n\n');
|
||||
@ -1178,9 +1217,15 @@ void main() {
|
||||
expect(snapshotPolls, 1);
|
||||
expect(taskGetParams.single['runId'], 'run-running');
|
||||
expect(
|
||||
taskGetParams.single['artifactScope'],
|
||||
'tasks/unit-fixture-task-handle/run-running',
|
||||
taskGetParams.single['appThreadKey'],
|
||||
'unit-fixture-task-handle',
|
||||
);
|
||||
expect(
|
||||
taskGetParams.single['openclawSessionKey'],
|
||||
'agent:main:unit-fixture-task-handle',
|
||||
);
|
||||
expect(taskGetParams.single, isNot(contains('sessionKey')));
|
||||
expect(taskGetParams.single, isNot(contains('artifactScope')));
|
||||
expect(result.success, isTrue);
|
||||
expect(result.message, 'completed after task handle');
|
||||
expect(result.artifacts.single.relativePath, 'reports/final.md');
|
||||
@ -1200,8 +1245,19 @@ void main() {
|
||||
if (method == 'session.start') {
|
||||
final event = jsonEncode(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'xworkmate.bridge.accepted',
|
||||
'params': <String, dynamic>{'sessionId': 'unit-fixture-task-c'},
|
||||
'method': 'session.update',
|
||||
'params': <String, dynamic>{
|
||||
'sessionId': 'unit-fixture-task-c',
|
||||
'threadId': 'unit-fixture-task-c',
|
||||
'turnId': 'turn-failed',
|
||||
'type': 'status',
|
||||
'event': 'running',
|
||||
'status': 'running',
|
||||
'runId': 'turn-failed',
|
||||
'appThreadKey': 'unit-fixture-task-c',
|
||||
'openclawSessionKey': 'agent:main:unit-fixture-task-c',
|
||||
'gatewayProviderId': 'openclaw',
|
||||
},
|
||||
});
|
||||
final eventBytes = utf8.encode('data: $event\n\n');
|
||||
request.response.headers.set(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user