@winmatrix/agent-sdk 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude/ClaudeAdapter.d.ts +1 -0
- package/dist/adapters/claude/ClaudeAdapter.d.ts.map +1 -1
- package/dist/adapters/claude/ClaudeAdapter.js +30 -6
- package/dist/adapters/claude/ClaudeAdapter.js.map +1 -1
- package/dist/adapters/claude/claudeCommand.js +1 -1
- package/dist/adapters/claude/claudeCommand.js.map +1 -1
- package/dist/adapters/codex/CodexAdapter.d.ts +20 -0
- package/dist/adapters/codex/CodexAdapter.d.ts.map +1 -0
- package/dist/adapters/codex/CodexAdapter.js +286 -0
- package/dist/adapters/codex/CodexAdapter.js.map +1 -0
- package/dist/adapters/hermes/HermesAdapter.d.ts +1 -0
- package/dist/adapters/hermes/HermesAdapter.d.ts.map +1 -1
- package/dist/adapters/hermes/HermesAdapter.js +79 -14
- package/dist/adapters/hermes/HermesAdapter.js.map +1 -1
- package/dist/adapters/openclaw/OpenClawAdapter.d.ts +12 -37
- package/dist/adapters/openclaw/OpenClawAdapter.d.ts.map +1 -1
- package/dist/adapters/openclaw/OpenClawAdapter.js +168 -634
- package/dist/adapters/openclaw/OpenClawAdapter.js.map +1 -1
- package/dist/core/AgentAdapter.d.ts +31 -0
- package/dist/core/AgentAdapter.d.ts.map +1 -1
- package/dist/core/AgentAdapter.js +25 -0
- package/dist/core/AgentAdapter.js.map +1 -1
- package/dist/core/RuntimeDetector.d.ts.map +1 -1
- package/dist/core/RuntimeDetector.js +52 -19
- package/dist/core/RuntimeDetector.js.map +1 -1
- package/dist/core/WinMatrixAgentClient.d.ts +8 -4
- package/dist/core/WinMatrixAgentClient.d.ts.map +1 -1
- package/dist/core/WinMatrixAgentClient.js +9 -2
- package/dist/core/WinMatrixAgentClient.js.map +1 -1
- package/dist/index.d.ts +4 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -7
- package/dist/index.js.map +1 -1
- package/dist/utils/resumeFallback.d.ts +4 -0
- package/dist/utils/resumeFallback.d.ts.map +1 -0
- package/dist/utils/resumeFallback.js +55 -0
- package/dist/utils/resumeFallback.js.map +1 -0
- package/package.json +7 -2
- package/dist/adapters/openclaw/unwrapOpenClawPayloads.d.ts +0 -6
- package/dist/adapters/openclaw/unwrapOpenClawPayloads.d.ts.map +0 -1
- package/dist/adapters/openclaw/unwrapOpenClawPayloads.js +0 -24
- package/dist/adapters/openclaw/unwrapOpenClawPayloads.js.map +0 -1
- package/dist/runtime/DualRuntimeBridge.d.ts +0 -38
- package/dist/runtime/DualRuntimeBridge.d.ts.map +0 -1
- package/dist/runtime/DualRuntimeBridge.js +0 -321
- package/dist/runtime/DualRuntimeBridge.js.map +0 -1
- package/dist/runtime/bridgeConfigLoader.d.ts +0 -14
- package/dist/runtime/bridgeConfigLoader.d.ts.map +0 -1
- package/dist/runtime/bridgeConfigLoader.js +0 -133
- package/dist/runtime/bridgeConfigLoader.js.map +0 -1
- package/dist/runtime/bridgeConfigTypes.d.ts +0 -26
- package/dist/runtime/bridgeConfigTypes.d.ts.map +0 -1
- package/dist/runtime/bridgeConfigTypes.js +0 -6
- package/dist/runtime/bridgeConfigTypes.js.map +0 -1
- package/dist/runtime/bridgeHome.d.ts +0 -19
- package/dist/runtime/bridgeHome.d.ts.map +0 -1
- package/dist/runtime/bridgeHome.js +0 -61
- package/dist/runtime/bridgeHome.js.map +0 -1
- package/dist/runtime/bridgeReconnectPolicy.d.ts +0 -8
- package/dist/runtime/bridgeReconnectPolicy.d.ts.map +0 -1
- package/dist/runtime/bridgeReconnectPolicy.js +0 -16
- package/dist/runtime/bridgeReconnectPolicy.js.map +0 -1
- package/dist/runtime/bridgeTaskJournal.d.ts +0 -7
- package/dist/runtime/bridgeTaskJournal.d.ts.map +0 -1
- package/dist/runtime/bridgeTaskJournal.js +0 -66
- package/dist/runtime/bridgeTaskJournal.js.map +0 -1
- package/dist/runtime/localRuntimeAvailabilityMonitor.d.ts +0 -22
- package/dist/runtime/localRuntimeAvailabilityMonitor.d.ts.map +0 -1
- package/dist/runtime/localRuntimeAvailabilityMonitor.js +0 -81
- package/dist/runtime/localRuntimeAvailabilityMonitor.js.map +0 -1
- package/dist/runtime/runDualRuntimeBridge.d.ts +0 -2
- package/dist/runtime/runDualRuntimeBridge.d.ts.map +0 -1
- package/dist/runtime/runDualRuntimeBridge.js +0 -32
- package/dist/runtime/runDualRuntimeBridge.js.map +0 -1
|
@@ -1,246 +1,83 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenClawAdapter —
|
|
2
|
+
* OpenClawAdapter — spawn `openclaw agent --json` CLI,通过 Gateway 执行 agent turn。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 从 WS-RPC gateway 模式改为 CLI spawn 模式(对齐 CodexAdapter/HermesAdapter):
|
|
5
|
+
* - 绕过 gateway protocol mismatch + operator scope 问题(CLI 内部处理 auth)
|
|
6
|
+
* - `--json` 结构化输出(精确解析,不猜正则)
|
|
7
|
+
* - 非流式(一次性 JSON,等进程退出后 yield result)
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
* 无需 device identity 签名。
|
|
9
|
+
* CLI 命令:`openclaw agent --json --agent <id> [--session-id <sid>] -m <prompt>`
|
|
9
10
|
*/
|
|
10
|
-
import {
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
11
12
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
12
13
|
import { homedir } from 'node:os';
|
|
13
14
|
import { join, dirname } from 'node:path';
|
|
14
|
-
import WebSocket from 'ws';
|
|
15
15
|
import { parseSkillMd } from '../../utils/parseSkillMd.js';
|
|
16
|
+
import { waitForExit, killProcess } from '../../utils/processUtils.js';
|
|
17
|
+
import { executeWithResumeFallback } from '../../utils/resumeFallback.js';
|
|
16
18
|
// ── constants ──
|
|
17
|
-
const PROTOCOL_VERSION = 3;
|
|
18
19
|
const OPENCLAW_DIR = join(homedir(), '.openclaw');
|
|
19
20
|
const OPENCLAW_CONFIG = join(OPENCLAW_DIR, 'openclaw.json');
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
function
|
|
24
|
-
if (frame.event === 'chat') {
|
|
25
|
-
const state = frame.payload?.state;
|
|
26
|
-
return state === 'final' || state === 'error';
|
|
27
|
-
}
|
|
28
|
-
if (frame.event === 'agent') {
|
|
29
|
-
const stream = typeof frame.payload?.stream === 'string'
|
|
30
|
-
? frame.payload.stream
|
|
31
|
-
: '';
|
|
32
|
-
const data = frame.payload?.data;
|
|
33
|
-
if (stream === 'lifecycle') {
|
|
34
|
-
const phase = data?.phase;
|
|
35
|
-
return phase === 'end' || phase === 'error';
|
|
36
|
-
}
|
|
37
|
-
if (stream === 'error')
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
/** 递归提取嵌套对象或数组中的 text 字符串 */
|
|
43
|
-
function extractText(obj) {
|
|
44
|
-
if (typeof obj === 'string')
|
|
45
|
-
return obj;
|
|
46
|
-
if (Array.isArray(obj)) {
|
|
47
|
-
const parts = [];
|
|
48
|
-
for (const item of obj) {
|
|
49
|
-
if (typeof item === 'string') {
|
|
50
|
-
parts.push(item);
|
|
51
|
-
}
|
|
52
|
-
else if (item && typeof item === 'object' && typeof item.text === 'string') {
|
|
53
|
-
parts.push(item.text);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return parts.join('');
|
|
57
|
-
}
|
|
58
|
-
if (!obj || typeof obj !== 'object')
|
|
59
|
-
return '';
|
|
60
|
-
const o = obj;
|
|
61
|
-
if (typeof o.text === 'string')
|
|
62
|
-
return o.text;
|
|
63
|
-
if (typeof o.content === 'string')
|
|
64
|
-
return o.content;
|
|
65
|
-
if (typeof o.delta === 'string')
|
|
66
|
-
return o.delta;
|
|
67
|
-
if (Array.isArray(o.content)) {
|
|
68
|
-
const parts = [];
|
|
69
|
-
for (const item of o.content) {
|
|
70
|
-
if (typeof item === 'string') {
|
|
71
|
-
parts.push(item);
|
|
72
|
-
}
|
|
73
|
-
else if (item && typeof item === 'object' && typeof item.text === 'string') {
|
|
74
|
-
parts.push(item.text);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return parts.join('');
|
|
78
|
-
}
|
|
79
|
-
return '';
|
|
80
|
-
}
|
|
81
|
-
/** 从 chat 事件 payload 提取文本 */
|
|
82
|
-
function extractTextFromPayload(payload) {
|
|
83
|
-
const fromMessage = extractText(payload.message);
|
|
84
|
-
if (fromMessage)
|
|
85
|
-
return fromMessage;
|
|
86
|
-
if (Array.isArray(payload.messages)) {
|
|
87
|
-
const parts = [];
|
|
88
|
-
for (const m of payload.messages) {
|
|
89
|
-
const t = extractText(m);
|
|
90
|
-
if (t)
|
|
91
|
-
parts.push(t);
|
|
92
|
-
}
|
|
93
|
-
if (parts.length > 0)
|
|
94
|
-
return parts.join('');
|
|
95
|
-
}
|
|
96
|
-
if (typeof payload.content === 'string')
|
|
97
|
-
return payload.content;
|
|
98
|
-
if (typeof payload.text === 'string')
|
|
99
|
-
return payload.text;
|
|
100
|
-
if (typeof payload.delta === 'string')
|
|
101
|
-
return payload.delta;
|
|
102
|
-
const fromContent = extractText(payload.content);
|
|
103
|
-
if (fromContent)
|
|
104
|
-
return fromContent;
|
|
105
|
-
if (typeof payload.result === 'string')
|
|
106
|
-
return payload.result;
|
|
107
|
-
if (typeof payload.output === 'string')
|
|
108
|
-
return payload.output;
|
|
109
|
-
return '';
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* 从 agent assistant 事件 data 提取文本。
|
|
113
|
-
* 参照 server 端 extractAgentAssistantText — 覆盖 text/delta/content/chunk/messages/message/payload/result/response/output。
|
|
114
|
-
*/
|
|
115
|
-
export function extractAgentAssistantText(data) {
|
|
116
|
-
if (typeof data.text === 'string')
|
|
117
|
-
return data.text;
|
|
118
|
-
if (typeof data.delta === 'string')
|
|
119
|
-
return data.delta;
|
|
120
|
-
if (typeof data.content === 'string')
|
|
121
|
-
return data.content;
|
|
122
|
-
if (typeof data.chunk === 'string')
|
|
123
|
-
return data.chunk;
|
|
124
|
-
const fromMessages = extractTextFromMessages(data.messages);
|
|
125
|
-
if (fromMessages)
|
|
126
|
-
return fromMessages;
|
|
127
|
-
const fromMessage = extractText(data.message);
|
|
128
|
-
if (fromMessage)
|
|
129
|
-
return fromMessage;
|
|
130
|
-
const fromContent = extractText(data.content);
|
|
131
|
-
if (fromContent)
|
|
132
|
-
return fromContent;
|
|
133
|
-
const fromDelta = extractText(data.delta);
|
|
134
|
-
if (fromDelta)
|
|
135
|
-
return fromDelta;
|
|
136
|
-
const fromChunk = extractText(data.chunk);
|
|
137
|
-
if (fromChunk)
|
|
138
|
-
return fromChunk;
|
|
139
|
-
const fromPayload = extractText(data.payload);
|
|
140
|
-
if (fromPayload)
|
|
141
|
-
return fromPayload;
|
|
142
|
-
const fromResult = extractText(data.result);
|
|
143
|
-
if (fromResult)
|
|
144
|
-
return fromResult;
|
|
145
|
-
const fromResponse = extractText(data.response);
|
|
146
|
-
if (fromResponse)
|
|
147
|
-
return fromResponse;
|
|
148
|
-
const fromOutput = extractText(data.output);
|
|
149
|
-
if (fromOutput)
|
|
150
|
-
return fromOutput;
|
|
151
|
-
// deep extraction
|
|
152
|
-
const fromDeep = extractTextDeep(data);
|
|
153
|
-
if (fromDeep)
|
|
154
|
-
return fromDeep;
|
|
155
|
-
return '';
|
|
156
|
-
}
|
|
157
|
-
export function extractTextFromMessages(messages) {
|
|
158
|
-
if (!Array.isArray(messages))
|
|
159
|
-
return '';
|
|
160
|
-
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
161
|
-
const item = messages[i];
|
|
162
|
-
if (!item || typeof item !== 'object')
|
|
163
|
-
continue;
|
|
164
|
-
const msg = item;
|
|
165
|
-
const role = typeof msg.role === 'string' ? msg.role.toLowerCase() : '';
|
|
166
|
-
if (role !== 'assistant' && role !== 'model')
|
|
167
|
-
continue;
|
|
168
|
-
const text = extractText(msg.content) || extractTextDeep(msg);
|
|
169
|
-
if (text)
|
|
170
|
-
return text;
|
|
171
|
-
}
|
|
172
|
-
return '';
|
|
173
|
-
}
|
|
174
|
-
export function extractTextDeep(obj, depth = 0) {
|
|
175
|
-
if (!obj || typeof obj !== 'object' || depth > 4)
|
|
176
|
-
return '';
|
|
177
|
-
const o = obj;
|
|
178
|
-
for (const key of ['text', 'content', 'delta', 'chunk', 'result', 'output', 'response', 'message']) {
|
|
179
|
-
const v = o[key];
|
|
180
|
-
if (typeof v === 'string' && v.trim())
|
|
181
|
-
return v;
|
|
182
|
-
if (v && typeof v === 'object') {
|
|
183
|
-
const inner = extractTextDeep(v, depth + 1);
|
|
184
|
-
if (inner)
|
|
185
|
-
return inner;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return '';
|
|
189
|
-
}
|
|
190
|
-
/** 从 ~/.openclaw/openclaw.json 读取 gateway token */
|
|
191
|
-
function readGatewayTokenFromConfig() {
|
|
21
|
+
// ── helpers ──
|
|
22
|
+
// ── helpers ──
|
|
23
|
+
/** 解析 openclaw agent --json 的完整输出,提取 answer/sessionId/usage */
|
|
24
|
+
function parseOpenClawCliResult(rawJson) {
|
|
192
25
|
try {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
26
|
+
const parsed = JSON.parse(rawJson);
|
|
27
|
+
const result = parsed.result;
|
|
28
|
+
// answer text: result.payloads[*].text join
|
|
29
|
+
let text = '';
|
|
30
|
+
if (result?.payloads && Array.isArray(result.payloads)) {
|
|
31
|
+
const texts = result.payloads
|
|
32
|
+
.map((p) => p.text ?? '')
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
if (texts.length > 0)
|
|
35
|
+
text = texts.join('\n');
|
|
36
|
+
}
|
|
37
|
+
// sessionId: result.meta.agentMeta.sessionId
|
|
38
|
+
const meta = result?.meta;
|
|
39
|
+
const agentMeta = meta?.agentMeta;
|
|
40
|
+
const sessionId = typeof agentMeta?.sessionId === 'string' ? agentMeta.sessionId : undefined;
|
|
41
|
+
// usage: result.meta.agentMeta.usage
|
|
42
|
+
const rawUsage = agentMeta?.usage;
|
|
43
|
+
const usage = rawUsage && typeof rawUsage.input === 'number' && typeof rawUsage.output === 'number'
|
|
44
|
+
? { input: rawUsage.input, output: rawUsage.output }
|
|
45
|
+
: undefined;
|
|
46
|
+
return { text, sessionId, usage };
|
|
198
47
|
}
|
|
199
48
|
catch {
|
|
200
|
-
return
|
|
49
|
+
return { text: rawJson.trim() };
|
|
201
50
|
}
|
|
202
51
|
}
|
|
203
|
-
/**
|
|
204
|
-
* sessionKey 编码: agent:<agentId>:direct:<sessionKey>(参照 server toAgentSessionKey)。
|
|
205
|
-
* 若 sessionKey 已是 agent: 格式则不重复编码,避免双重包装。
|
|
206
|
-
*/
|
|
207
|
-
export function toAgentSessionKey(sessionKey, agentId) {
|
|
208
|
-
if (sessionKey.toLowerCase().startsWith('agent:'))
|
|
209
|
-
return sessionKey;
|
|
210
|
-
return `agent:${agentId}:direct:${sessionKey}`;
|
|
211
|
-
}
|
|
212
52
|
export class OpenClawAdapter {
|
|
213
53
|
meta = {
|
|
214
54
|
agentType: 'openclaw',
|
|
215
55
|
runtime: 'openclaw',
|
|
216
56
|
capabilities: {
|
|
217
|
-
|
|
57
|
+
// 非流式(一次性 JSON);CLI 无 --stream-json
|
|
58
|
+
streaming: false,
|
|
218
59
|
toolCall: true,
|
|
219
60
|
session: true,
|
|
220
|
-
|
|
61
|
+
// SIGTERM killProcess 可取消
|
|
62
|
+
cancellation: true,
|
|
63
|
+
supportsCancel: true,
|
|
64
|
+
supportsResume: true,
|
|
221
65
|
},
|
|
222
66
|
tools: [],
|
|
223
67
|
skills: [],
|
|
224
68
|
toolCategories: {},
|
|
225
69
|
};
|
|
226
|
-
|
|
227
|
-
gatewayPort;
|
|
228
|
-
token;
|
|
70
|
+
openclawPath;
|
|
229
71
|
mcpBridgeUrl;
|
|
230
72
|
mcpApiKey;
|
|
231
73
|
agentId;
|
|
232
74
|
constructor(config) {
|
|
233
75
|
const opts = config;
|
|
234
|
-
this.
|
|
235
|
-
this.gatewayPort = opts?.gatewayPort ?? 18789;
|
|
236
|
-
this.token = opts?.token ?? readGatewayTokenFromConfig();
|
|
76
|
+
this.openclawPath = opts?.openclawPath ?? 'openclaw';
|
|
237
77
|
this.mcpBridgeUrl = opts?.mcpBridgeUrl;
|
|
238
78
|
this.mcpApiKey = opts?.mcpApiKey;
|
|
239
79
|
this.agentId = opts?.agentId;
|
|
240
80
|
}
|
|
241
|
-
get gatewayUrl() {
|
|
242
|
-
return `ws://${this.gatewayHost}:${this.gatewayPort}`;
|
|
243
|
-
}
|
|
244
81
|
// ── MCP config helpers ──
|
|
245
82
|
/**
|
|
246
83
|
* Write MCP server config to openclaw.json, merge with existing keys.
|
|
@@ -281,230 +118,101 @@ export class OpenClawAdapter {
|
|
|
281
118
|
}
|
|
282
119
|
// ── execute ──
|
|
283
120
|
async *execute(task, signal) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
121
|
+
yield* executeWithResumeFallback(task, signal, (nextTask, nextSignal) => this.executeOnce(nextTask, nextSignal));
|
|
122
|
+
}
|
|
123
|
+
async *executeOnce(task, signal) {
|
|
124
|
+
const workDir = task.workDir;
|
|
125
|
+
if (workDir && !existsSync(workDir)) {
|
|
126
|
+
yield { type: 'error', message: `Work directory does not exist: ${workDir}` };
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// MCP 注入:Gateway 从 openclaw.json 读 mcp.servers.winmatrix
|
|
289
130
|
this.writeMCPConfig(task.mcpToken);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
131
|
+
// 构造 CLI args: openclaw agent --json --agent main [--session-id <sid>] -m <prompt>
|
|
132
|
+
// 注意:openclaw CLI 的 --agent 要的是 openclaw 内部 agent id(如 'main'),
|
|
133
|
+
// 不是 WinMatrix 的外联 Agent id(ea_xxx)。WinMatrix agentId 仅用于 sessionKey 编码/MCP,
|
|
134
|
+
// openclaw agent id 固定用 'main'(用户可在 ~/.openclaw/agents/ 配置多个,默认 main)。
|
|
135
|
+
const openclawAgentId = 'main';
|
|
136
|
+
const prompt = task.systemPrompt
|
|
137
|
+
? `${task.systemPrompt}\n\n---\n\n${task.input}`
|
|
138
|
+
: task.input;
|
|
139
|
+
const args = ['agent', '--json', '--agent', openclawAgentId, '-m', prompt];
|
|
140
|
+
// resume: --session-id <sessionId>
|
|
141
|
+
if (task.resumeSession && task.sessionId) {
|
|
142
|
+
args.push('--session-id', task.sessionId);
|
|
143
|
+
}
|
|
144
|
+
// model override(临时复用 task.hermes?.model;待 OpenClawTaskOptions 加独立 model 字段后改读 task.openclaw?.model)
|
|
145
|
+
if (task.hermes?.model) {
|
|
146
|
+
args.push('--model', task.hermes.model);
|
|
147
|
+
}
|
|
148
|
+
// Spawn
|
|
149
|
+
let proc;
|
|
294
150
|
try {
|
|
295
|
-
|
|
151
|
+
proc = spawn(this.openclawPath, args, {
|
|
152
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
153
|
+
cwd: workDir ?? process.cwd(),
|
|
154
|
+
});
|
|
296
155
|
}
|
|
297
156
|
catch (err) {
|
|
298
|
-
yield {
|
|
157
|
+
yield {
|
|
158
|
+
type: 'error',
|
|
159
|
+
message: `Failed to spawn openclaw: ${err instanceof Error ? err.message : String(err)}`,
|
|
160
|
+
};
|
|
299
161
|
return;
|
|
300
162
|
}
|
|
301
|
-
|
|
302
|
-
const pendingRequests = new Map();
|
|
303
|
-
const eventQueue = [];
|
|
304
|
-
let wakeUp = null;
|
|
305
|
-
let streamDone = false;
|
|
306
|
-
let finalized = false;
|
|
307
|
-
const persistHandler = (data) => {
|
|
308
|
-
try {
|
|
309
|
-
const frame = JSON.parse(data.toString());
|
|
310
|
-
// Route res frames to pendingRequests
|
|
311
|
-
if (frame.type === 'res' && frame.id) {
|
|
312
|
-
const pending = pendingRequests.get(frame.id);
|
|
313
|
-
if (pending) {
|
|
314
|
-
clearTimeout(pending.timer);
|
|
315
|
-
pendingRequests.delete(frame.id);
|
|
316
|
-
pending.resolve({
|
|
317
|
-
ok: frame.ok === true,
|
|
318
|
-
payload: frame.payload,
|
|
319
|
-
error: frame.error,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
// Route event frames to event queue (capped to prevent leaks).
|
|
325
|
-
// Never drop final/error events — they're required to terminate the stream loop.
|
|
326
|
-
if (frame.type === 'event') {
|
|
327
|
-
if (eventQueue.length >= MAX_EVENT_QUEUE) {
|
|
328
|
-
let dropIdx = -1;
|
|
329
|
-
for (let i = 0; i < eventQueue.length; i++) {
|
|
330
|
-
if (!isStreamEndEvent(eventQueue[i])) {
|
|
331
|
-
dropIdx = i;
|
|
332
|
-
break;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (dropIdx >= 0) {
|
|
336
|
-
eventQueue.splice(dropIdx, 1);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
eventQueue.push(frame);
|
|
340
|
-
wakeUp?.();
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
// skip malformed
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
ws.on('message', persistHandler);
|
|
348
|
-
const onClose = () => {
|
|
349
|
-
streamDone = true;
|
|
350
|
-
// Reject all pending requests
|
|
351
|
-
for (const [, p] of pendingRequests) {
|
|
352
|
-
clearTimeout(p.timer);
|
|
353
|
-
p.reject(new Error('WebSocket closed'));
|
|
354
|
-
}
|
|
355
|
-
pendingRequests.clear();
|
|
356
|
-
wakeUp?.();
|
|
357
|
-
};
|
|
358
|
-
ws.on('close', onClose);
|
|
163
|
+
let completed = false;
|
|
359
164
|
const onAbort = () => {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
ws.send(JSON.stringify({ type: 'req', id: `abort-${Date.now()}`, method: 'chat.abort', params: { sessionKey: sessionId } }));
|
|
363
|
-
}
|
|
364
|
-
catch { /* ignore */ }
|
|
365
|
-
ws.close();
|
|
165
|
+
if (!completed)
|
|
166
|
+
killProcess(proc);
|
|
366
167
|
};
|
|
367
168
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
368
169
|
try {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
// Send chat.send via pendingRequests map (not the generic sendRequest)
|
|
375
|
-
const chatSendResult = await this.sendRequestViaMap(ws, pendingRequests, 'chat.send', {
|
|
376
|
-
sessionKey,
|
|
377
|
-
message,
|
|
378
|
-
idempotencyKey: `wm-${randomUUID()}`,
|
|
170
|
+
// 累积 stdout(整块 JSON,非流式)
|
|
171
|
+
let stdoutBuffer = '';
|
|
172
|
+
let stderrBuffer = '';
|
|
173
|
+
proc.stderr?.on('data', (chunk) => {
|
|
174
|
+
stderrBuffer += chunk.toString('utf-8');
|
|
379
175
|
});
|
|
380
|
-
|
|
381
|
-
const errMsg = typeof chatSendResult.error === 'string'
|
|
382
|
-
? chatSendResult.error
|
|
383
|
-
: JSON.stringify(chatSendResult.error);
|
|
384
|
-
yield { type: 'error', message: `chat.send failed: ${errMsg}` };
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
const ack = chatSendResult.payload;
|
|
388
|
-
const runId = ack?.runId;
|
|
389
|
-
sessionId = sessionKey;
|
|
390
|
-
// Stream events from the persistent handler's queue
|
|
391
|
-
let resultText = '';
|
|
392
|
-
let msgIdx = 0;
|
|
393
|
-
while (!streamDone) {
|
|
176
|
+
for await (const chunk of proc.stdout) {
|
|
394
177
|
if (signal?.aborted)
|
|
395
178
|
break;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
yield { type: 'delta', content: text };
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
else if (state === 'final' && !finalized) {
|
|
413
|
-
finalized = true;
|
|
414
|
-
const text = extractTextFromPayload(payload);
|
|
415
|
-
if (text)
|
|
416
|
-
resultText += text;
|
|
417
|
-
streamDone = true;
|
|
418
|
-
}
|
|
419
|
-
else if (state === 'error' && !finalized) {
|
|
420
|
-
finalized = true;
|
|
421
|
-
const errMsg = typeof payload.errorMessage === 'string'
|
|
422
|
-
? payload.errorMessage
|
|
423
|
-
: 'chat error';
|
|
424
|
-
yield { type: 'error', message: errMsg };
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
if (frame.event === 'agent') {
|
|
429
|
-
const payload = frame.payload;
|
|
430
|
-
if (!payload)
|
|
431
|
-
continue;
|
|
432
|
-
if (runId && payload.runId && payload.runId !== runId)
|
|
433
|
-
continue;
|
|
434
|
-
const stream = typeof payload.stream === 'string' ? payload.stream : '';
|
|
435
|
-
const data = (payload.data ?? {});
|
|
436
|
-
if (stream === 'assistant') {
|
|
437
|
-
const delta = extractAgentAssistantText(data);
|
|
438
|
-
if (delta) {
|
|
439
|
-
resultText += delta;
|
|
440
|
-
yield { type: 'delta', content: delta };
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
else if (stream === 'tool' && typeof data.name === 'string') {
|
|
444
|
-
// Observability only — Gateway executes tools via MCP
|
|
445
|
-
const argsStr = typeof data.arguments === 'string'
|
|
446
|
-
? data.arguments
|
|
447
|
-
: JSON.stringify(data.arguments ?? {});
|
|
448
|
-
yield { type: 'thinking', content: `[工具] ${data.name}(${argsStr.slice(0, 200)})` };
|
|
449
|
-
}
|
|
450
|
-
else if (stream === 'result') {
|
|
451
|
-
const resultTxt = extractAgentAssistantText(data);
|
|
452
|
-
if (resultTxt) {
|
|
453
|
-
resultText += resultTxt;
|
|
454
|
-
yield { type: 'delta', content: resultTxt };
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
else if (stream === 'error' && !finalized) {
|
|
458
|
-
finalized = true;
|
|
459
|
-
const errMsg = typeof data.error === 'string'
|
|
460
|
-
? data.error
|
|
461
|
-
: typeof data.message === 'string'
|
|
462
|
-
? data.message
|
|
463
|
-
: 'agent error';
|
|
464
|
-
yield { type: 'error', message: errMsg };
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
else if (stream === 'lifecycle') {
|
|
468
|
-
const phase = data.phase;
|
|
469
|
-
if (phase === 'end' && !finalized) {
|
|
470
|
-
finalized = true;
|
|
471
|
-
streamDone = true;
|
|
472
|
-
}
|
|
473
|
-
else if (phase === 'error' && !finalized) {
|
|
474
|
-
finalized = true;
|
|
475
|
-
const errMsg = (typeof data.error === 'string' ? data.error : undefined)
|
|
476
|
-
?? (typeof data.message === 'string' ? data.message : undefined)
|
|
477
|
-
?? 'agent lifecycle error';
|
|
478
|
-
yield { type: 'error', message: errMsg };
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
if (ws.readyState === WebSocket.CLOSED || ws.readyState === WebSocket.CLOSING) {
|
|
485
|
-
break;
|
|
179
|
+
stdoutBuffer += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');
|
|
180
|
+
}
|
|
181
|
+
const exitResult = await waitForExit(proc);
|
|
182
|
+
completed = true;
|
|
183
|
+
if (exitResult.code === 0 && stdoutBuffer.trim()) {
|
|
184
|
+
const { text, sessionId, usage } = parseOpenClawCliResult(stdoutBuffer);
|
|
185
|
+
if (text) {
|
|
186
|
+
yield {
|
|
187
|
+
type: 'result',
|
|
188
|
+
content: text,
|
|
189
|
+
...(sessionId ? { sessionId } : {}),
|
|
190
|
+
...(usage ? { usage } : {}),
|
|
191
|
+
};
|
|
486
192
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
193
|
+
else {
|
|
194
|
+
// JSON 解析成功但 text 为空
|
|
195
|
+
yield {
|
|
196
|
+
type: 'result',
|
|
197
|
+
content: stdoutBuffer.trim().slice(0, 500),
|
|
198
|
+
};
|
|
492
199
|
}
|
|
493
200
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
201
|
+
else {
|
|
202
|
+
// exit≠0:取 stderr 最后非空行(真实错误通常在末尾,前面可能混 plugin warning 噪声)
|
|
203
|
+
const stderrLines = stderrBuffer.trim().split('\n').filter((l) => l.trim());
|
|
204
|
+
const errMsg = stderrLines[stderrLines.length - 1] || exitResult.error || `openclaw exited with code ${exitResult.code}`;
|
|
205
|
+
yield { type: 'error', message: `OpenClaw agent failed: ${errMsg}` };
|
|
206
|
+
}
|
|
499
207
|
}
|
|
500
208
|
catch (err) {
|
|
501
|
-
yield {
|
|
209
|
+
yield {
|
|
210
|
+
type: 'error',
|
|
211
|
+
message: `OpenClaw agent error: ${err instanceof Error ? err.message : String(err)}`,
|
|
212
|
+
};
|
|
502
213
|
}
|
|
503
214
|
finally {
|
|
504
215
|
signal?.removeEventListener('abort', onAbort);
|
|
505
|
-
ws.off('message', persistHandler);
|
|
506
|
-
ws.off('close', onClose);
|
|
507
|
-
ws.close();
|
|
508
216
|
}
|
|
509
217
|
}
|
|
510
218
|
// ── discoverSkills ──
|
|
@@ -553,246 +261,72 @@ export class OpenClawAdapter {
|
|
|
553
261
|
catch { /* silently skip */ }
|
|
554
262
|
return skills;
|
|
555
263
|
}
|
|
556
|
-
// ── authenticated WebSocket connect ──
|
|
557
|
-
authenticatedConnect(url, host, signal) {
|
|
558
|
-
return new Promise((resolve, reject) => {
|
|
559
|
-
const ws = new WebSocket(url, {
|
|
560
|
-
headers: { Origin: `http://${host}` },
|
|
561
|
-
});
|
|
562
|
-
let handshakeDone = false;
|
|
563
|
-
const onAbort = () => {
|
|
564
|
-
ws.close();
|
|
565
|
-
if (!handshakeDone)
|
|
566
|
-
reject(new Error('Aborted'));
|
|
567
|
-
};
|
|
568
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
569
|
-
const cleanup = () => {
|
|
570
|
-
handshakeDone = true;
|
|
571
|
-
signal?.removeEventListener('abort', onAbort);
|
|
572
|
-
ws.off('message', onMessage);
|
|
573
|
-
};
|
|
574
|
-
const onMessage = (data) => {
|
|
575
|
-
try {
|
|
576
|
-
const frame = JSON.parse(data.toString());
|
|
577
|
-
if (frame.event === 'connect.challenge') {
|
|
578
|
-
const connectId = `connect-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
579
|
-
const role = 'operator';
|
|
580
|
-
const scopes = ['operator.admin', 'operator.write', 'operator.read'];
|
|
581
|
-
const connectParams = {
|
|
582
|
-
minProtocol: PROTOCOL_VERSION,
|
|
583
|
-
maxProtocol: PROTOCOL_VERSION,
|
|
584
|
-
client: {
|
|
585
|
-
// 'gateway-client' + mode:'backend' = loopback backend 客户端(官方支持,无需 device identity)。
|
|
586
|
-
// 服务端 workstation 使用 'openclaw-control-ui' + mode:'webchat',两者 client.id 不同是因为
|
|
587
|
-
// daemon 走本地 loopback(同机部署),server 走容器远程连接。
|
|
588
|
-
id: 'gateway-client',
|
|
589
|
-
displayName: 'WinMatrix Agent',
|
|
590
|
-
version: '1.0.0',
|
|
591
|
-
platform: process.platform,
|
|
592
|
-
mode: 'backend',
|
|
593
|
-
},
|
|
594
|
-
role,
|
|
595
|
-
scopes,
|
|
596
|
-
};
|
|
597
|
-
if (this.token) {
|
|
598
|
-
connectParams.auth = { token: this.token };
|
|
599
|
-
}
|
|
600
|
-
ws.send(JSON.stringify({ type: 'req', id: connectId, method: 'connect', params: connectParams }));
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
if (frame.type === 'res' && frame.id?.startsWith('connect-')) {
|
|
604
|
-
if (frame.ok) {
|
|
605
|
-
cleanup();
|
|
606
|
-
resolve(ws);
|
|
607
|
-
}
|
|
608
|
-
else {
|
|
609
|
-
cleanup();
|
|
610
|
-
const errDetail = typeof frame.error === 'string' ? frame.error : JSON.stringify(frame.error);
|
|
611
|
-
reject(new Error(`Gateway connect rejected: ${errDetail}`));
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
catch {
|
|
616
|
-
// skip malformed frames during handshake
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
ws.on('message', onMessage);
|
|
620
|
-
ws.on('error', (err) => {
|
|
621
|
-
cleanup();
|
|
622
|
-
reject(new Error(`OpenClaw Gateway not reachable at ${url}: ${err.message}`));
|
|
623
|
-
});
|
|
624
|
-
ws.on('close', () => {
|
|
625
|
-
if (!handshakeDone) {
|
|
626
|
-
cleanup();
|
|
627
|
-
reject(new Error('WebSocket closed before handshake completed'));
|
|
628
|
-
}
|
|
629
|
-
});
|
|
630
|
-
if (signal?.aborted) {
|
|
631
|
-
ws.close();
|
|
632
|
-
reject(new Error('Aborted'));
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
// ── RPC helpers ──
|
|
637
|
-
/**
|
|
638
|
-
* Generic sendRequest — temporary onMessage handler pattern.
|
|
639
|
-
* Used by healthCheck/listSessions/getSession (short-lived connections).
|
|
640
|
-
*/
|
|
641
|
-
sendRequest(ws, method, params) {
|
|
642
|
-
return new Promise((resolve, reject) => {
|
|
643
|
-
const id = `${method}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
644
|
-
let resolved = false;
|
|
645
|
-
const timer = setTimeout(() => {
|
|
646
|
-
if (resolved)
|
|
647
|
-
return;
|
|
648
|
-
resolved = true;
|
|
649
|
-
ws.off('message', onMessage);
|
|
650
|
-
reject(new Error(`OpenClaw request timeout: ${method}`));
|
|
651
|
-
}, 30_000);
|
|
652
|
-
const onMessage = (data) => {
|
|
653
|
-
try {
|
|
654
|
-
const frame = JSON.parse(data.toString());
|
|
655
|
-
if (frame.type === 'res' && frame.id === id) {
|
|
656
|
-
if (resolved)
|
|
657
|
-
return;
|
|
658
|
-
resolved = true;
|
|
659
|
-
clearTimeout(timer);
|
|
660
|
-
ws.off('message', onMessage);
|
|
661
|
-
resolve({
|
|
662
|
-
ok: frame.ok === true,
|
|
663
|
-
payload: frame.payload,
|
|
664
|
-
error: frame.error,
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
catch {
|
|
669
|
-
// skip malformed frames
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
ws.on('message', onMessage);
|
|
673
|
-
ws.send(JSON.stringify({ type: 'req', id, method, params }));
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Send a request using a pendingRequests map (persistent handler multiplexing).
|
|
678
|
-
* Used by execute() to multiplex req/res with event streaming on the same ws.
|
|
679
|
-
*/
|
|
680
|
-
sendRequestViaMap(ws, pendingRequests, method, params) {
|
|
681
|
-
return new Promise((resolve, reject) => {
|
|
682
|
-
const id = `${method}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
683
|
-
const timer = setTimeout(() => {
|
|
684
|
-
pendingRequests.delete(id);
|
|
685
|
-
reject(new Error(`OpenClaw request timeout: ${method}`));
|
|
686
|
-
}, 30_000);
|
|
687
|
-
pendingRequests.set(id, { resolve, reject, timer });
|
|
688
|
-
ws.send(JSON.stringify({ type: 'req', id, method, params }));
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
264
|
// ── healthCheck ──
|
|
692
265
|
async healthCheck() {
|
|
693
266
|
try {
|
|
694
|
-
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
267
|
+
// 用 openclaw --version 验 CLI 可执行(轻量,不连 gateway)
|
|
268
|
+
const { spawnSync } = await import('node:child_process');
|
|
269
|
+
const result = spawnSync(this.openclawPath, ['--version'], {
|
|
270
|
+
timeout: 5_000,
|
|
271
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
272
|
+
encoding: 'utf-8',
|
|
273
|
+
});
|
|
274
|
+
if (result.status === 0) {
|
|
275
|
+
const version = result.stdout.trim().split('\n')[0].slice(0, 100);
|
|
699
276
|
return { available: true, version };
|
|
700
277
|
}
|
|
701
|
-
return { available:
|
|
278
|
+
return { available: false, error: `openclaw --version exited with code ${result.status}` };
|
|
702
279
|
}
|
|
703
280
|
catch (err) {
|
|
704
|
-
return {
|
|
281
|
+
return {
|
|
282
|
+
available: false,
|
|
283
|
+
error: err instanceof Error ? err.message : String(err),
|
|
284
|
+
};
|
|
705
285
|
}
|
|
706
286
|
}
|
|
707
|
-
// ──
|
|
287
|
+
// ── session management ──
|
|
708
288
|
async listSessions(options) {
|
|
289
|
+
// CLI: openclaw sessions --json --agent <id> --limit <n>
|
|
709
290
|
try {
|
|
710
|
-
const
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
|
|
291
|
+
const { spawnSync } = await import('node:child_process');
|
|
292
|
+
const agentId = this.agentId ?? 'main';
|
|
293
|
+
const limit = options.limit ?? 20;
|
|
294
|
+
const result = spawnSync(this.openclawPath, ['sessions', '--json', '--agent', agentId, '--limit', String(limit)], {
|
|
295
|
+
timeout: 10_000,
|
|
296
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
297
|
+
encoding: 'utf-8',
|
|
298
|
+
cwd: options.dir ?? process.cwd(),
|
|
714
299
|
});
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
300
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
301
|
+
const parsed = JSON.parse(result.stdout);
|
|
302
|
+
const sessions = (parsed.sessions ?? []).map((s) => ({
|
|
303
|
+
id: s.sessionId ?? s.id ?? '',
|
|
304
|
+
customTitle: s.title,
|
|
305
|
+
createdAt: s.createdAt ?? '',
|
|
306
|
+
lastActivityAt: s.lastActivityAt ?? '',
|
|
307
|
+
messageCount: s.messageCount,
|
|
308
|
+
}));
|
|
309
|
+
return { sessions };
|
|
718
310
|
}
|
|
719
|
-
const data = result.payload;
|
|
720
|
-
const raw = data?.sessions ?? [];
|
|
721
|
-
const sessions = raw.map((s) => ({
|
|
722
|
-
id: String(s.id ?? s.session_id ?? s.sessionKey ?? ''),
|
|
723
|
-
cwd: typeof s.cwd === 'string' ? s.cwd : undefined,
|
|
724
|
-
summary: typeof s.summary === 'string' ? s.summary : undefined,
|
|
725
|
-
createdAt: typeof s.created_at === 'string' ? s.created_at : undefined,
|
|
726
|
-
lastActivityAt: typeof s.last_activity_at === 'string' ? s.last_activity_at : undefined,
|
|
727
|
-
messageCount: typeof s.message_count === 'number' ? s.message_count : undefined,
|
|
728
|
-
status: typeof s.status === 'string' ? s.status : undefined,
|
|
729
|
-
}));
|
|
730
|
-
return { sessions };
|
|
731
|
-
}
|
|
732
|
-
catch (err) {
|
|
733
|
-
return { sessions: [], error: err instanceof Error ? err.message : String(err) };
|
|
734
311
|
}
|
|
312
|
+
catch { /* fall through to empty */ }
|
|
313
|
+
return { sessions: [] };
|
|
735
314
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
userPrompts: [],
|
|
752
|
-
assistantText: '',
|
|
753
|
-
lastAssistantText: '',
|
|
754
|
-
error: typeof result.error === 'string' ? result.error : JSON.stringify(result.error),
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
const data = result.payload;
|
|
758
|
-
const messages = data?.messages ?? [];
|
|
759
|
-
let assistantText = '';
|
|
760
|
-
let lastAssistantText = '';
|
|
761
|
-
const userPrompts = [];
|
|
762
|
-
for (const msg of messages) {
|
|
763
|
-
if (msg.role === 'user' && msg.content) {
|
|
764
|
-
userPrompts.push(msg.content);
|
|
765
|
-
}
|
|
766
|
-
else if (msg.role === 'assistant' && msg.content) {
|
|
767
|
-
assistantText += msg.content + '\n';
|
|
768
|
-
lastAssistantText = msg.content;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
return {
|
|
772
|
-
session: {
|
|
773
|
-
id: sessionId,
|
|
774
|
-
...(data?.session ?? {}),
|
|
775
|
-
},
|
|
776
|
-
messageCount: messages.length,
|
|
777
|
-
truncated: messages.length >= (options.messageLimit ?? 100),
|
|
778
|
-
userPrompts,
|
|
779
|
-
assistantText: assistantText.trim(),
|
|
780
|
-
lastAssistantText,
|
|
781
|
-
rawMessages: options.includeRawMessages ? messages : undefined,
|
|
782
|
-
rawMessagesIncluded: options.includeRawMessages === true,
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
catch (err) {
|
|
786
|
-
return {
|
|
787
|
-
session: { id: sessionId },
|
|
788
|
-
messageCount: 0,
|
|
789
|
-
truncated: false,
|
|
790
|
-
userPrompts: [],
|
|
791
|
-
assistantText: '',
|
|
792
|
-
lastAssistantText: '',
|
|
793
|
-
error: err instanceof Error ? err.message : String(err),
|
|
794
|
-
};
|
|
795
|
-
}
|
|
315
|
+
async getSession(sessionId, options) {
|
|
316
|
+
// CLI 无直接 get session 命令;返回 stub(daemon AgentProcessManager 有 fallback)
|
|
317
|
+
return {
|
|
318
|
+
session: {
|
|
319
|
+
id: sessionId,
|
|
320
|
+
customTitle: 'N/A',
|
|
321
|
+
createdAt: '',
|
|
322
|
+
lastActivityAt: '',
|
|
323
|
+
},
|
|
324
|
+
messageCount: 0,
|
|
325
|
+
truncated: false,
|
|
326
|
+
userPrompts: [],
|
|
327
|
+
assistantText: '',
|
|
328
|
+
lastAssistantText: '',
|
|
329
|
+
};
|
|
796
330
|
}
|
|
797
331
|
}
|
|
798
332
|
//# sourceMappingURL=OpenClawAdapter.js.map
|