clawconnect-agent 0.1.11 → 0.1.13
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/README.md +16 -0
- package/README.zh-CN.md +16 -0
- package/dist/commands/install.js +3 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/pair.js +2 -1
- package/dist/commands/pair.js.map +1 -1
- package/dist/commands/profile-hints.d.ts +7 -0
- package/dist/commands/profile-hints.js +71 -0
- package/dist/commands/profile-hints.js.map +1 -0
- package/dist/commands/send-file.js +25 -5
- package/dist/commands/send-file.js.map +1 -1
- package/dist/core/relay/timeline-event-builder.d.ts +5 -1
- package/dist/core/relay/timeline-event-builder.js +4 -0
- package/dist/core/relay/timeline-event-builder.js.map +1 -1
- package/dist/core/relay/timeline-event-log.d.ts +3 -0
- package/dist/core/relay/timeline-event-log.js +4 -0
- package/dist/core/relay/timeline-event-log.js.map +1 -1
- package/dist/hermes/hermes-relay-manager.d.ts +27 -24
- package/dist/hermes/hermes-relay-manager.js +126 -343
- package/dist/hermes/hermes-relay-manager.js.map +1 -1
- package/dist/hermes/hermes-runtime-test-support.d.ts +27 -0
- package/dist/hermes/hermes-runtime-test-support.js +703 -0
- package/dist/hermes/hermes-runtime-test-support.js.map +1 -0
- package/dist/hermes/hermes-runtime.d.ts +1 -2
- package/dist/hermes/hermes-runtime.js +1 -2
- package/dist/hermes/hermes-runtime.js.map +1 -1
- package/dist/hermes/relay/active-hermes-chat-runs.d.ts +12 -0
- package/dist/hermes/relay/active-hermes-chat-runs.js +42 -0
- package/dist/hermes/relay/active-hermes-chat-runs.js.map +1 -0
- package/dist/hermes/relay/hermes-slash-command-catalog.d.ts +22 -0
- package/dist/hermes/relay/hermes-slash-command-catalog.js +248 -0
- package/dist/hermes/relay/hermes-slash-command-catalog.js.map +1 -0
- package/dist/hermes/relay/hermes-state-db-realtime.d.ts +69 -0
- package/dist/hermes/relay/hermes-state-db-realtime.js +444 -0
- package/dist/hermes/relay/hermes-state-db-realtime.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-api-client.d.ts +15 -0
- package/dist/hermes/runtime/hermes-runtime-api-client.js +510 -0
- package/dist/hermes/runtime/hermes-runtime-api-client.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-chat.d.ts +4 -14
- package/dist/hermes/runtime/hermes-runtime-chat.js +205 -436
- package/dist/hermes/runtime/hermes-runtime-chat.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-command-utils.d.ts +1 -0
- package/dist/hermes/runtime/hermes-runtime-command-utils.js +10 -1
- package/dist/hermes/runtime/hermes-runtime-command-utils.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-history-completion.d.ts +12 -0
- package/dist/hermes/runtime/hermes-runtime-history-completion.js +162 -0
- package/dist/hermes/runtime/hermes-runtime-history-completion.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-history.js +85 -22
- package/dist/hermes/runtime/hermes-runtime-history.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-preloaded-skills.d.ts +11 -0
- package/dist/hermes/runtime/hermes-runtime-preloaded-skills.js +67 -0
- package/dist/hermes/runtime/hermes-runtime-preloaded-skills.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-process.d.ts +3 -1
- package/dist/hermes/runtime/hermes-runtime-process.js +35 -6
- package/dist/hermes/runtime/hermes-runtime-process.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-sessions.js +28 -3
- package/dist/hermes/runtime/hermes-runtime-sessions.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-slash-command.d.ts +8 -0
- package/dist/hermes/runtime/hermes-runtime-slash-command.js +144 -0
- package/dist/hermes/runtime/hermes-runtime-slash-command.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-state-db.d.ts +4 -0
- package/dist/hermes/runtime/hermes-runtime-state-db.js +193 -0
- package/dist/hermes/runtime/hermes-runtime-state-db.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-tool-log-watcher.d.ts +8 -0
- package/dist/hermes/runtime/hermes-runtime-tool-log-watcher.js +121 -0
- package/dist/hermes/runtime/hermes-runtime-tool-log-watcher.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-usage.d.ts +2 -0
- package/dist/hermes/runtime/hermes-runtime-usage.js +59 -5
- package/dist/hermes/runtime/hermes-runtime-usage.js.map +1 -1
- package/dist/i18n/index.js +4 -0
- package/dist/i18n/index.js.map +1 -1
- package/dist/index.js +22 -3
- package/dist/index.js.map +1 -1
- package/dist/openclaw/handlers/local-handlers.js +66 -20
- package/dist/openclaw/handlers/local-handlers.js.map +1 -1
- package/dist/openclaw/relay/chat-send-dedupe-coordinator.d.ts +20 -0
- package/dist/openclaw/relay/chat-send-dedupe-coordinator.js +161 -0
- package/dist/openclaw/relay/chat-send-dedupe-coordinator.js.map +1 -0
- package/dist/openclaw/relay/relay-manager-hello.d.ts +6 -0
- package/dist/openclaw/relay/relay-manager-hello.js +11 -0
- package/dist/openclaw/relay/relay-manager-hello.js.map +1 -0
- package/dist/openclaw/relay/relay-manager-history-timing.d.ts +5 -0
- package/dist/openclaw/relay/relay-manager-history-timing.js +6 -0
- package/dist/openclaw/relay/relay-manager-history-timing.js.map +1 -0
- package/dist/openclaw/relay/relay-manager-office-events.d.ts +2 -0
- package/dist/openclaw/relay/relay-manager-office-events.js +9 -0
- package/dist/openclaw/relay/relay-manager-office-events.js.map +1 -0
- package/dist/openclaw/relay/relay-manager-payload-helpers.d.ts +18 -0
- package/dist/openclaw/relay/relay-manager-payload-helpers.js +213 -0
- package/dist/openclaw/relay/relay-manager-payload-helpers.js.map +1 -0
- package/dist/openclaw/relay/relay-manager-protocol.d.ts +49 -0
- package/dist/openclaw/relay/relay-manager-protocol.js +2 -0
- package/dist/openclaw/relay/relay-manager-protocol.js.map +1 -0
- package/dist/openclaw/relay-manager.d.ts +3 -27
- package/dist/openclaw/relay-manager.js +19 -402
- package/dist/openclaw/relay-manager.js.map +1 -1
- package/dist/openclaw/session-store.js +42 -1
- package/dist/openclaw/session-store.js.map +1 -1
- package/package.json +1 -1
- package/dist/hermes/runtime/hermes-runtime-artifacts.d.ts +0 -3
- package/dist/hermes/runtime/hermes-runtime-artifacts.js +0 -75
- package/dist/hermes/runtime/hermes-runtime-artifacts.js.map +0 -1
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export type RelayHelloMessage
|
|
4
|
-
type: "hello";
|
|
5
|
-
platform: string;
|
|
6
|
-
agentVersion: string;
|
|
7
|
-
capabilities?: string[];
|
|
8
|
-
slashCommands?: readonly RelaySlashCommandDescriptor[];
|
|
9
|
-
};
|
|
10
|
-
export interface RelayManagerOptions {
|
|
11
|
-
relayServerUrl: string;
|
|
12
|
-
gatewayId: string;
|
|
13
|
-
relaySecret: string;
|
|
14
|
-
gatewayUrl: string | (() => string);
|
|
15
|
-
gatewayToken?: string;
|
|
16
|
-
gatewayPassword?: string;
|
|
17
|
-
onConnected?: () => void;
|
|
18
|
-
onDisconnected?: () => void;
|
|
19
|
-
/** Optional abort signal. When aborted the relay WebSocket is closed
|
|
20
|
-
* cleanly (code 1001) and the retry loop stops. */
|
|
21
|
-
signal?: AbortSignal;
|
|
22
|
-
}
|
|
1
|
+
import type { RelayManagerOptions } from "./relay/relay-manager-protocol.js";
|
|
2
|
+
export { buildRelayHelloMessage } from "./relay/relay-manager-hello.js";
|
|
3
|
+
export type { RelayHelloMessage, RelayManagerOptions } from "./relay/relay-manager-protocol.js";
|
|
23
4
|
/**
|
|
24
5
|
* Connects to the cloud relay server and the local OpenClaw Gateway,
|
|
25
6
|
* then bridges messages between them indefinitely.
|
|
@@ -29,8 +10,3 @@ export interface RelayManagerOptions {
|
|
|
29
10
|
* connection closes.
|
|
30
11
|
*/
|
|
31
12
|
export declare function runRelayManager(opts: RelayManagerOptions): Promise<boolean>;
|
|
32
|
-
export declare function buildRelayHelloMessage(opts: {
|
|
33
|
-
platform: string;
|
|
34
|
-
agentVersion: string;
|
|
35
|
-
capabilities?: string[];
|
|
36
|
-
}): RelayHelloMessage;
|
|
@@ -6,21 +6,18 @@ import { handleProviderCommand } from "./handlers/provider-handlers.js";
|
|
|
6
6
|
import { DEFAULT_GATEWAY_SESSION_DEFAULTS, buildContextUsageFingerprint, readContextUsageSnapshot, canonicalizeRelayParams, canonicalizeSessionKey, extractGatewaySessionDefaults, } from "./relay/session-context.js";
|
|
7
7
|
import { appendUniqueSuffix, extractChatRole, extractChatText, normalizeChatEventPayload, normalizeChatState, withMessageText, } from "../core/relay/chat-payload.js";
|
|
8
8
|
import { extractHistoryOutcome, readOpenClawTranscriptChatHistory, withTimeout, } from "./relay/chat-history.js";
|
|
9
|
-
import {
|
|
9
|
+
import { OpenClawChatSendDedupeCoordinator, } from "./relay/chat-send-dedupe-coordinator.js";
|
|
10
10
|
import { buildMobileAssistantDeltaPayload, buildMobileAssistantErrorPayload, buildMobileAssistantFinalPayload, } from "../core/relay/mobile-chat-run-bridge.js";
|
|
11
|
-
import { buildOfficeEventPayload } from "../core/relay/office-payload.js";
|
|
12
11
|
import { prepareChatSendParams } from "./relay/chat-send-attachments.js";
|
|
13
12
|
import { relayOutgoingMediaInHistoryResponse, relayOutgoingMediaInPayload } from "./relay/outgoing-media-relay.js";
|
|
14
13
|
import { prepareOpenClawVoiceInputCommand } from "./relay/openclaw-voice-input.js";
|
|
15
14
|
import { voiceInputSetupMessage } from "../core/relay/voice-input.js";
|
|
16
15
|
import { gatewayCapabilitiesForType } from "../gateway-profiles.js";
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const CHAT_HISTORY_FINAL_RETRY_DELAY_MS = 750;
|
|
23
|
-
const CHAT_SEND_MIRROR_DEDUPE_RETRY_DELAYS_MS = [250, 1000, 2500, 5000, 10000, 20000, 30000];
|
|
16
|
+
import { buildRelayHelloMessage } from "./relay/relay-manager-hello.js";
|
|
17
|
+
import { CHAT_HISTORY_FALLBACK_INITIAL_DELAY_MS, CHAT_HISTORY_FALLBACK_MAX_ATTEMPTS, CHAT_HISTORY_FALLBACK_RETRY_DELAY_MS, CHAT_HISTORY_FETCH_TIMEOUT_MS, CHAT_HISTORY_FINAL_RETRY_DELAY_MS, } from "./relay/relay-manager-history-timing.js";
|
|
18
|
+
import { publishOfficeSnapshot } from "./relay/relay-manager-office-events.js";
|
|
19
|
+
export { buildRelayHelloMessage } from "./relay/relay-manager-hello.js";
|
|
20
|
+
import { buildEmptyHistoryPage, buildFinalPayloadFromHistoryOutcome, buildLegacyOpenClawHistoryParams, extractChatErrorMessage, hasHistoryCursor, mergeCanonicalChatPayload, mobileAssistantUsageFromPayload, nonTextContentBlocks, nonTextContentBlocksFromHistory, resolveChatPayloadSeq, resolveChatPayloadTimestamp, shouldUseLegacyOpenClawHistoryFallback, } from "./relay/relay-manager-payload-helpers.js";
|
|
24
21
|
// ---------------------------------------------------------------------------
|
|
25
22
|
// Main entry point
|
|
26
23
|
// ---------------------------------------------------------------------------
|
|
@@ -50,11 +47,7 @@ export async function runRelayManager(opts) {
|
|
|
50
47
|
const chatBuffers = new Map();
|
|
51
48
|
const chatFallbacks = new Map();
|
|
52
49
|
const chatRunContexts = new Map();
|
|
53
|
-
const
|
|
54
|
-
const chatSendDedupeRunKeys = new Map();
|
|
55
|
-
const chatSendDedupeTimers = new Map();
|
|
56
|
-
const chatSendDedupeAttempts = new Map();
|
|
57
|
-
const chatSendDedupeRunning = new Map();
|
|
50
|
+
const chatSendDedupe = new OpenClawChatSendDedupeCoordinator(() => sessionDefaults);
|
|
58
51
|
const outgoingMediaUploadCache = new Map();
|
|
59
52
|
const contextUsageRefreshes = new Map();
|
|
60
53
|
const contextUsageFingerprints = new Map();
|
|
@@ -65,139 +58,6 @@ export async function runRelayManager(opts) {
|
|
|
65
58
|
chatFallbacks.delete(runId);
|
|
66
59
|
}
|
|
67
60
|
};
|
|
68
|
-
const clearChatSendDedupe = (clientRunId) => {
|
|
69
|
-
const timer = chatSendDedupeTimers.get(clientRunId);
|
|
70
|
-
if (timer) {
|
|
71
|
-
clearTimeout(timer);
|
|
72
|
-
chatSendDedupeTimers.delete(clientRunId);
|
|
73
|
-
}
|
|
74
|
-
chatSendDedupeRequests.delete(clientRunId);
|
|
75
|
-
chatSendDedupeAttempts.delete(clientRunId);
|
|
76
|
-
for (const [runId, mappedClientRunId] of chatSendDedupeRunKeys.entries()) {
|
|
77
|
-
if (mappedClientRunId === clientRunId) {
|
|
78
|
-
chatSendDedupeRunKeys.delete(runId);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
const registerChatSendDedupe = (request, runId) => {
|
|
83
|
-
chatSendDedupeRequests.set(request.clientRunId, request);
|
|
84
|
-
if (runId) {
|
|
85
|
-
chatSendDedupeRunKeys.set(runId, request.clientRunId);
|
|
86
|
-
}
|
|
87
|
-
scheduleChatSendDedupe(request.clientRunId);
|
|
88
|
-
};
|
|
89
|
-
function scheduleChatSendDedupe(clientRunId, delayOverrideMs) {
|
|
90
|
-
if (!chatSendDedupeRequests.has(clientRunId)) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const existing = chatSendDedupeTimers.get(clientRunId);
|
|
94
|
-
if (existing) {
|
|
95
|
-
clearTimeout(existing);
|
|
96
|
-
}
|
|
97
|
-
const attempt = chatSendDedupeAttempts.get(clientRunId) ?? 0;
|
|
98
|
-
const delayMs = delayOverrideMs ?? CHAT_SEND_MIRROR_DEDUPE_RETRY_DELAYS_MS[Math.min(attempt, CHAT_SEND_MIRROR_DEDUPE_RETRY_DELAYS_MS.length - 1)];
|
|
99
|
-
const timer = setTimeout(() => {
|
|
100
|
-
chatSendDedupeTimers.delete(clientRunId);
|
|
101
|
-
void runChatSendDedupeAttempt(clientRunId).then((changed) => {
|
|
102
|
-
if (changed || !chatSendDedupeRequests.has(clientRunId)) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const nextAttempt = attempt + 1;
|
|
106
|
-
if (nextAttempt >= CHAT_SEND_MIRROR_DEDUPE_RETRY_DELAYS_MS.length) {
|
|
107
|
-
clearChatSendDedupe(clientRunId);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
chatSendDedupeAttempts.set(clientRunId, nextAttempt);
|
|
111
|
-
scheduleChatSendDedupe(clientRunId);
|
|
112
|
-
});
|
|
113
|
-
}, delayMs);
|
|
114
|
-
timer.unref?.();
|
|
115
|
-
chatSendDedupeTimers.set(clientRunId, timer);
|
|
116
|
-
}
|
|
117
|
-
function scheduleChatSendDedupeForRun(runId, delayMs = 100) {
|
|
118
|
-
const clientRunId = chatSendDedupeRunKeys.get(runId);
|
|
119
|
-
if (!clientRunId) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
scheduleChatSendDedupe(clientRunId, delayMs);
|
|
123
|
-
}
|
|
124
|
-
async function runChatSendDedupeAttempt(clientRunId) {
|
|
125
|
-
const running = chatSendDedupeRunning.get(clientRunId);
|
|
126
|
-
if (running) {
|
|
127
|
-
return running;
|
|
128
|
-
}
|
|
129
|
-
const promise = (async () => {
|
|
130
|
-
const request = chatSendDedupeRequests.get(clientRunId);
|
|
131
|
-
if (!request) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
const result = await dedupeOpenClawChatSendUserMirrorTranscript(request, sessionDefaults, { maxRetries: 2 });
|
|
136
|
-
if (result.changed) {
|
|
137
|
-
console.log(`[relay] removed ${result.removedCount} duplicate OpenClaw prompt mirror(s) from ${result.transcriptPath ?? "transcript"}`);
|
|
138
|
-
clearChatSendDedupe(clientRunId);
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
console.warn(`[relay] chat.send transcript dedupe failed runId=${clientRunId}: ${String(error)}`);
|
|
144
|
-
}
|
|
145
|
-
return false;
|
|
146
|
-
})().finally(() => {
|
|
147
|
-
chatSendDedupeRunning.delete(clientRunId);
|
|
148
|
-
});
|
|
149
|
-
chatSendDedupeRunning.set(clientRunId, promise);
|
|
150
|
-
return promise;
|
|
151
|
-
}
|
|
152
|
-
async function dedupePendingChatSendMirrorsForSession(rawParams) {
|
|
153
|
-
const sessionKey = resolveRawParamsSessionKey(rawParams);
|
|
154
|
-
const pending = Array.from(chatSendDedupeRequests.entries()).filter(([, request]) => {
|
|
155
|
-
const requestSessionKey = canonicalizeSessionKey(request.sessionKey ?? sessionDefaults.mainSessionKey, sessionDefaults);
|
|
156
|
-
return requestSessionKey === sessionKey;
|
|
157
|
-
});
|
|
158
|
-
for (const [clientRunId] of pending) {
|
|
159
|
-
await runChatSendDedupeAttempt(clientRunId);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function buildChatSendDedupeRequest(paramsRecord) {
|
|
163
|
-
if (!paramsRecord) {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
const message = typeof paramsRecord.message === "string" && paramsRecord.message.trim().length > 0
|
|
167
|
-
? paramsRecord.message.trim()
|
|
168
|
-
: "";
|
|
169
|
-
const idempotencyKey = typeof paramsRecord.idempotencyKey === "string" && paramsRecord.idempotencyKey.trim().length > 0
|
|
170
|
-
? paramsRecord.idempotencyKey.trim()
|
|
171
|
-
: "";
|
|
172
|
-
if (!message || !idempotencyKey) {
|
|
173
|
-
return undefined;
|
|
174
|
-
}
|
|
175
|
-
const clientRunId = idempotencyKey.endsWith(":user")
|
|
176
|
-
? idempotencyKey.slice(0, -":user".length)
|
|
177
|
-
: idempotencyKey;
|
|
178
|
-
if (!clientRunId) {
|
|
179
|
-
return undefined;
|
|
180
|
-
}
|
|
181
|
-
return {
|
|
182
|
-
clientRunId,
|
|
183
|
-
message,
|
|
184
|
-
sessionKey: resolveRawParamsSessionKey(paramsRecord),
|
|
185
|
-
senderId: "openclaw-macos",
|
|
186
|
-
senderName: "ClawConnect Agent",
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
function resolveRawParamsSessionKey(rawParams) {
|
|
190
|
-
const record = rawParams && typeof rawParams === "object" && !Array.isArray(rawParams)
|
|
191
|
-
? rawParams
|
|
192
|
-
: {};
|
|
193
|
-
const rawSessionKey = typeof record.sessionKey === "string" && record.sessionKey.trim().length > 0
|
|
194
|
-
? record.sessionKey.trim()
|
|
195
|
-
: sessionDefaults.mainSessionKey;
|
|
196
|
-
const normalized = canonicalizeSessionKey(rawSessionKey, sessionDefaults);
|
|
197
|
-
return typeof normalized === "string" && normalized.trim().length > 0
|
|
198
|
-
? normalized.trim()
|
|
199
|
-
: sessionDefaults.mainSessionKey;
|
|
200
|
-
}
|
|
201
61
|
const publishContextUsageSnapshot = async (sessionKey, force = false) => {
|
|
202
62
|
const normalizedSessionKey = canonicalizeSessionKey(sessionKey, sessionDefaults);
|
|
203
63
|
if (typeof normalizedSessionKey !== "string" || normalizedSessionKey.trim().length === 0) {
|
|
@@ -320,13 +180,6 @@ export async function runRelayManager(opts) {
|
|
|
320
180
|
function send(msg) {
|
|
321
181
|
sendRelayJson(relayWs, msg);
|
|
322
182
|
}
|
|
323
|
-
function publishOfficeSnapshot(eventName, payload) {
|
|
324
|
-
const officePayload = buildOfficeEventPayload(eventName, payload, () => new Date().toISOString());
|
|
325
|
-
if (!officePayload) {
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
send({ type: "event", event: "office", payload: officePayload });
|
|
329
|
-
}
|
|
330
183
|
async function publishAndSendGatewayEvent(eventName, payload, publishOffice, userMessage) {
|
|
331
184
|
const outgoingPayload = eventName === "chat"
|
|
332
185
|
? await relayOutgoingMediaInPayload(payload, {
|
|
@@ -339,7 +192,7 @@ export async function runRelayManager(opts) {
|
|
|
339
192
|
})
|
|
340
193
|
: payload;
|
|
341
194
|
if (publishOffice) {
|
|
342
|
-
publishOfficeSnapshot(eventName, outgoingPayload);
|
|
195
|
+
publishOfficeSnapshot(send, eventName, outgoingPayload);
|
|
343
196
|
}
|
|
344
197
|
send({ type: "event", event: eventName, payload: outgoingPayload });
|
|
345
198
|
}
|
|
@@ -357,7 +210,7 @@ export async function runRelayManager(opts) {
|
|
|
357
210
|
}
|
|
358
211
|
async function requestChatHistoryFromClawConnect(params) {
|
|
359
212
|
try {
|
|
360
|
-
await
|
|
213
|
+
await chatSendDedupe.dedupePendingForSession(params);
|
|
361
214
|
const transcriptHistory = await readOpenClawTranscriptChatHistory(params, sessionDefaults);
|
|
362
215
|
if (transcriptHistory) {
|
|
363
216
|
return transcriptHistory;
|
|
@@ -367,236 +220,15 @@ export async function runRelayManager(opts) {
|
|
|
367
220
|
console.warn(`[relay] transcript chat.history unavailable: ${String(err)}`);
|
|
368
221
|
}
|
|
369
222
|
if (hasHistoryCursor(params)) {
|
|
370
|
-
return buildEmptyHistoryPage(params);
|
|
223
|
+
return buildEmptyHistoryPage(params, sessionDefaults);
|
|
371
224
|
}
|
|
372
|
-
if (!shouldUseLegacyOpenClawHistoryFallback(params)) {
|
|
373
|
-
return buildEmptyHistoryPage(params);
|
|
225
|
+
if (!shouldUseLegacyOpenClawHistoryFallback(params, sessionDefaults)) {
|
|
226
|
+
return buildEmptyHistoryPage(params, sessionDefaults);
|
|
374
227
|
}
|
|
375
228
|
if (!gatewayClient) {
|
|
376
229
|
throw new Error("gateway not connected");
|
|
377
230
|
}
|
|
378
|
-
return gatewayClient.request("chat.history", buildLegacyOpenClawHistoryParams(params));
|
|
379
|
-
}
|
|
380
|
-
function shouldUseLegacyOpenClawHistoryFallback(params) {
|
|
381
|
-
const record = params && typeof params === "object" && !Array.isArray(params)
|
|
382
|
-
? params
|
|
383
|
-
: {};
|
|
384
|
-
const rawSessionKey = typeof record.sessionKey === "string" && record.sessionKey.trim().length > 0
|
|
385
|
-
? record.sessionKey.trim()
|
|
386
|
-
: sessionDefaults.mainSessionKey;
|
|
387
|
-
const normalized = canonicalizeSessionKey(rawSessionKey, sessionDefaults);
|
|
388
|
-
return typeof normalized === "string" && normalized === sessionDefaults.mainSessionKey;
|
|
389
|
-
}
|
|
390
|
-
function buildLegacyOpenClawHistoryParams(params) {
|
|
391
|
-
const record = params && typeof params === "object" && !Array.isArray(params)
|
|
392
|
-
? params
|
|
393
|
-
: {};
|
|
394
|
-
const rawSessionKey = typeof record.sessionKey === "string" && record.sessionKey.trim().length > 0
|
|
395
|
-
? record.sessionKey.trim()
|
|
396
|
-
: sessionDefaults.mainSessionKey;
|
|
397
|
-
const sessionKey = canonicalizeSessionKey(rawSessionKey, sessionDefaults);
|
|
398
|
-
const legacyParams = {
|
|
399
|
-
sessionKey: typeof sessionKey === "string" && sessionKey.trim().length > 0
|
|
400
|
-
? sessionKey.trim()
|
|
401
|
-
: sessionDefaults.mainSessionKey,
|
|
402
|
-
};
|
|
403
|
-
const limit = normalizePositiveInteger(record.limit);
|
|
404
|
-
if (limit !== undefined) {
|
|
405
|
-
legacyParams.limit = limit;
|
|
406
|
-
}
|
|
407
|
-
const maxChars = normalizePositiveInteger(record.maxChars);
|
|
408
|
-
if (maxChars !== undefined) {
|
|
409
|
-
legacyParams.maxChars = maxChars;
|
|
410
|
-
}
|
|
411
|
-
return legacyParams;
|
|
412
|
-
}
|
|
413
|
-
function buildEmptyHistoryPage(params) {
|
|
414
|
-
const record = params && typeof params === "object" && !Array.isArray(params)
|
|
415
|
-
? params
|
|
416
|
-
: {};
|
|
417
|
-
const rawSessionKey = typeof record.sessionKey === "string" && record.sessionKey.trim().length > 0
|
|
418
|
-
? record.sessionKey.trim()
|
|
419
|
-
: sessionDefaults.mainSessionKey;
|
|
420
|
-
const sessionKey = canonicalizeSessionKey(rawSessionKey, sessionDefaults);
|
|
421
|
-
return {
|
|
422
|
-
sessionKey: typeof sessionKey === "string" && sessionKey.trim().length > 0
|
|
423
|
-
? sessionKey.trim()
|
|
424
|
-
: sessionDefaults.mainSessionKey,
|
|
425
|
-
messages: [],
|
|
426
|
-
hasMore: false,
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
function hasHistoryCursor(params) {
|
|
430
|
-
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
const cursor = params.cursor;
|
|
434
|
-
return typeof cursor === "string" && cursor.trim().length > 0;
|
|
435
|
-
}
|
|
436
|
-
function normalizePositiveInteger(value) {
|
|
437
|
-
const parsed = typeof value === "number" && Number.isFinite(value)
|
|
438
|
-
? Math.round(value)
|
|
439
|
-
: typeof value === "string" && value.trim().length > 0
|
|
440
|
-
? Number.parseInt(value.trim(), 10)
|
|
441
|
-
: Number.NaN;
|
|
442
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
443
|
-
}
|
|
444
|
-
function buildFinalPayloadFromHistoryOutcome(basePayload, outcome) {
|
|
445
|
-
const payload = outcome.text.trim()
|
|
446
|
-
? withMessageText(basePayload, outcome.text)
|
|
447
|
-
: basePayload && typeof basePayload === "object" && !Array.isArray(basePayload)
|
|
448
|
-
? { ...basePayload }
|
|
449
|
-
: {};
|
|
450
|
-
const historyMessage = outcome.message;
|
|
451
|
-
const content = Array.isArray(historyMessage.content) ? historyMessage.content : [];
|
|
452
|
-
if (content.length === 0) {
|
|
453
|
-
return payload;
|
|
454
|
-
}
|
|
455
|
-
const payloadRecord = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
456
|
-
? payload
|
|
457
|
-
: {};
|
|
458
|
-
const existingMessage = payloadRecord.message && typeof payloadRecord.message === "object" && !Array.isArray(payloadRecord.message)
|
|
459
|
-
? payloadRecord.message
|
|
460
|
-
: {};
|
|
461
|
-
return {
|
|
462
|
-
...payloadRecord,
|
|
463
|
-
message: {
|
|
464
|
-
...existingMessage,
|
|
465
|
-
...stripUndefinedHistoryMessageFields(historyMessage),
|
|
466
|
-
role: "assistant",
|
|
467
|
-
content,
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
function stripUndefinedHistoryMessageFields(message) {
|
|
472
|
-
return Object.fromEntries(Object.entries(message).filter(([, value]) => value !== undefined));
|
|
473
|
-
}
|
|
474
|
-
function mergeCanonicalChatPayload(basePayload, canonicalPayload) {
|
|
475
|
-
const base = asRecord(basePayload);
|
|
476
|
-
const canonical = asRecord(canonicalPayload);
|
|
477
|
-
if (!base) {
|
|
478
|
-
return canonicalPayload;
|
|
479
|
-
}
|
|
480
|
-
if (!canonical) {
|
|
481
|
-
return basePayload;
|
|
482
|
-
}
|
|
483
|
-
const baseMessage = asRecord(base.message);
|
|
484
|
-
const canonicalMessage = asRecord(canonical.message);
|
|
485
|
-
const merged = {
|
|
486
|
-
...base,
|
|
487
|
-
...canonical,
|
|
488
|
-
};
|
|
489
|
-
return baseMessage || canonicalMessage
|
|
490
|
-
? {
|
|
491
|
-
...merged,
|
|
492
|
-
message: {
|
|
493
|
-
...(baseMessage ?? {}),
|
|
494
|
-
...(canonicalMessage ?? {}),
|
|
495
|
-
},
|
|
496
|
-
}
|
|
497
|
-
: merged;
|
|
498
|
-
}
|
|
499
|
-
function resolveChatPayloadSeq(payload) {
|
|
500
|
-
return normalizeFiniteNumber(deepField(payload, ["seq", "sequence", "index"]))
|
|
501
|
-
?? resolveChatPayloadTimestamp(payload);
|
|
502
|
-
}
|
|
503
|
-
function resolveChatPayloadTimestamp(payload) {
|
|
504
|
-
const value = normalizeFiniteNumber(deepField(payload, ["ts", "timestamp", "createdAt", "created_at", "time"]));
|
|
505
|
-
if (value !== undefined) {
|
|
506
|
-
return value > 10_000_000_000 ? Math.round(value) : Math.round(value * 1000);
|
|
507
|
-
}
|
|
508
|
-
return Date.now();
|
|
509
|
-
}
|
|
510
|
-
function extractChatErrorMessage(payload) {
|
|
511
|
-
const direct = firstNonEmptyString(deepField(payload, ["errorMessage", "error_message", "message", "text"]));
|
|
512
|
-
if (direct) {
|
|
513
|
-
return direct;
|
|
514
|
-
}
|
|
515
|
-
const error = asRecord(asRecord(payload)?.error);
|
|
516
|
-
const nested = firstNonEmptyString(error?.message, error?.userMessage, error?.detail);
|
|
517
|
-
return nested ?? "Request failed";
|
|
518
|
-
}
|
|
519
|
-
function mobileAssistantUsageFromPayload(payload) {
|
|
520
|
-
const record = asRecord(payload);
|
|
521
|
-
const usage = asRecord(record?.usage);
|
|
522
|
-
return stripUndefined({
|
|
523
|
-
currentModel: firstNonEmptyString(record?.currentModel, record?.model, usage?.currentModel, usage?.model),
|
|
524
|
-
provider: firstNonEmptyString(record?.provider, usage?.provider),
|
|
525
|
-
contextUsage: normalizeNonNegativeInteger(record?.contextUsage)
|
|
526
|
-
?? normalizeNonNegativeInteger(record?.promptTokens)
|
|
527
|
-
?? normalizeNonNegativeInteger(record?.inputTokens)
|
|
528
|
-
?? normalizeNonNegativeInteger(usage?.contextUsage)
|
|
529
|
-
?? normalizeNonNegativeInteger(usage?.promptTokens)
|
|
530
|
-
?? normalizeNonNegativeInteger(usage?.inputTokens),
|
|
531
|
-
contextLimit: normalizeNonNegativeInteger(record?.contextLimit)
|
|
532
|
-
?? normalizeNonNegativeInteger(record?.maxInputTokens)
|
|
533
|
-
?? normalizeNonNegativeInteger(usage?.contextLimit)
|
|
534
|
-
?? normalizeNonNegativeInteger(usage?.maxInputTokens),
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
function nonTextContentBlocks(payload) {
|
|
538
|
-
const payloadRecord = asRecord(payload);
|
|
539
|
-
const message = asRecord(payloadRecord?.message);
|
|
540
|
-
const topLevelContent = payloadRecord?.content;
|
|
541
|
-
const content = Array.isArray(message?.content)
|
|
542
|
-
? message.content
|
|
543
|
-
: Array.isArray(topLevelContent)
|
|
544
|
-
? topLevelContent
|
|
545
|
-
: [];
|
|
546
|
-
return content.filter((block) => {
|
|
547
|
-
const record = asRecord(block);
|
|
548
|
-
return Boolean(record?.type) && record?.type !== "text";
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
function nonTextContentBlocksFromHistory(message) {
|
|
552
|
-
return Array.isArray(message.content)
|
|
553
|
-
? message.content.filter((block) => {
|
|
554
|
-
const record = asRecord(block);
|
|
555
|
-
return Boolean(record?.type) && record?.type !== "text";
|
|
556
|
-
})
|
|
557
|
-
: [];
|
|
558
|
-
}
|
|
559
|
-
function deepField(payload, keys) {
|
|
560
|
-
const record = asRecord(payload);
|
|
561
|
-
const message = asRecord(record?.message);
|
|
562
|
-
const data = asRecord(record?.data);
|
|
563
|
-
for (const key of keys) {
|
|
564
|
-
if (record && record[key] !== undefined)
|
|
565
|
-
return record[key];
|
|
566
|
-
if (message && message[key] !== undefined)
|
|
567
|
-
return message[key];
|
|
568
|
-
if (data && data[key] !== undefined)
|
|
569
|
-
return data[key];
|
|
570
|
-
}
|
|
571
|
-
return undefined;
|
|
572
|
-
}
|
|
573
|
-
function firstNonEmptyString(...values) {
|
|
574
|
-
for (const value of values) {
|
|
575
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
576
|
-
return value.trim();
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return undefined;
|
|
580
|
-
}
|
|
581
|
-
function normalizeFiniteNumber(value) {
|
|
582
|
-
const number = typeof value === "number" && Number.isFinite(value)
|
|
583
|
-
? value
|
|
584
|
-
: typeof value === "string" && value.trim().length > 0
|
|
585
|
-
? Number(value.trim())
|
|
586
|
-
: Number.NaN;
|
|
587
|
-
return Number.isFinite(number) ? number : undefined;
|
|
588
|
-
}
|
|
589
|
-
function normalizeNonNegativeInteger(value) {
|
|
590
|
-
const number = normalizeFiniteNumber(value);
|
|
591
|
-
return number !== undefined && number >= 0 ? Math.round(number) : undefined;
|
|
592
|
-
}
|
|
593
|
-
function stripUndefined(value) {
|
|
594
|
-
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
595
|
-
}
|
|
596
|
-
function asRecord(value) {
|
|
597
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
598
|
-
? value
|
|
599
|
-
: undefined;
|
|
231
|
+
return gatewayClient.request("chat.history", buildLegacyOpenClawHistoryParams(params, sessionDefaults));
|
|
600
232
|
}
|
|
601
233
|
relayWs.on("open", () => {
|
|
602
234
|
console.log(`Connected to relay server (gatewayId=${opts.gatewayId})`);
|
|
@@ -615,7 +247,7 @@ export async function runRelayManager(opts) {
|
|
|
615
247
|
onConnected: () => {
|
|
616
248
|
console.log("Gateway connected.");
|
|
617
249
|
send({ type: "gateway_connected" });
|
|
618
|
-
publishOfficeSnapshot("gateway_connected", {
|
|
250
|
+
publishOfficeSnapshot(send, "gateway_connected", {
|
|
619
251
|
sessionKey: sessionDefaults.mainSessionKey,
|
|
620
252
|
});
|
|
621
253
|
void refreshSessionDefaults();
|
|
@@ -623,7 +255,7 @@ export async function runRelayManager(opts) {
|
|
|
623
255
|
onDisconnected: (reason) => {
|
|
624
256
|
console.log(`Gateway disconnected: ${reason}`);
|
|
625
257
|
send({ type: "gateway_disconnected", reason });
|
|
626
|
-
publishOfficeSnapshot("gateway_disconnected", {
|
|
258
|
+
publishOfficeSnapshot(send, "gateway_disconnected", {
|
|
627
259
|
reason,
|
|
628
260
|
});
|
|
629
261
|
},
|
|
@@ -642,7 +274,7 @@ export async function runRelayManager(opts) {
|
|
|
642
274
|
let realtimePayload = normalizedPayload;
|
|
643
275
|
if (runId) {
|
|
644
276
|
if (state === "final" || state === "error" || state === "failed" || state === "fail" || state === "aborted") {
|
|
645
|
-
|
|
277
|
+
chatSendDedupe.scheduleForRun(runId, 100);
|
|
646
278
|
}
|
|
647
279
|
if (role === "assistant" && (state === "delta" || state === "final" || state === "error" || state === "failed" || state === "fail")) {
|
|
648
280
|
clearChatFallback(runId);
|
|
@@ -837,7 +469,7 @@ export async function runRelayManager(opts) {
|
|
|
837
469
|
? params
|
|
838
470
|
: undefined;
|
|
839
471
|
const chatSendDedupeRequest = msg.method === "chat.send"
|
|
840
|
-
?
|
|
472
|
+
? chatSendDedupe.buildRequest(paramsRecord)
|
|
841
473
|
: undefined;
|
|
842
474
|
if (!gatewayClient && msg.method !== "chat.history") {
|
|
843
475
|
throw new Error("gateway not connected");
|
|
@@ -872,7 +504,7 @@ export async function runRelayManager(opts) {
|
|
|
872
504
|
scheduleChatHistoryFallback(runId, runContext);
|
|
873
505
|
}
|
|
874
506
|
if (msg.method === "chat.send" && chatSendDedupeRequest) {
|
|
875
|
-
|
|
507
|
+
chatSendDedupe.register(chatSendDedupeRequest, runId);
|
|
876
508
|
}
|
|
877
509
|
// Let a model switch settle before publishing context usage again.
|
|
878
510
|
// Forced refreshes can replay a stale model snapshot and overwrite the new selection.
|
|
@@ -922,13 +554,7 @@ export async function runRelayManager(opts) {
|
|
|
922
554
|
clearTimeout(timer);
|
|
923
555
|
}
|
|
924
556
|
chatFallbacks.clear();
|
|
925
|
-
|
|
926
|
-
clearTimeout(timer);
|
|
927
|
-
}
|
|
928
|
-
chatSendDedupeTimers.clear();
|
|
929
|
-
chatSendDedupeRequests.clear();
|
|
930
|
-
chatSendDedupeRunKeys.clear();
|
|
931
|
-
chatSendDedupeAttempts.clear();
|
|
557
|
+
chatSendDedupe.clearAll();
|
|
932
558
|
for (const timer of contextUsageRefreshes.values()) {
|
|
933
559
|
clearTimeout(timer);
|
|
934
560
|
}
|
|
@@ -941,13 +567,4 @@ export async function runRelayManager(opts) {
|
|
|
941
567
|
});
|
|
942
568
|
});
|
|
943
569
|
}
|
|
944
|
-
export function buildRelayHelloMessage(opts) {
|
|
945
|
-
return {
|
|
946
|
-
type: "hello",
|
|
947
|
-
platform: opts.platform,
|
|
948
|
-
agentVersion: opts.agentVersion,
|
|
949
|
-
capabilities: opts.capabilities,
|
|
950
|
-
slashCommands: OPENCLAW_SLASH_COMMAND_CATALOG,
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
570
|
//# sourceMappingURL=relay-manager.js.map
|