clawconnect-agent 0.2.12 → 0.2.14
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/commands/send-file.d.ts +2 -0
- package/dist/commands/send-file.js +8 -0
- package/dist/commands/send-file.js.map +1 -1
- package/dist/core/relay/source-commit-publish-flow.d.ts +36 -0
- package/dist/core/relay/source-commit-publish-flow.js +107 -0
- package/dist/core/relay/source-commit-publish-flow.js.map +1 -0
- package/dist/core/relay/source-commit.d.ts +7 -0
- package/dist/core/relay/source-commit.js +25 -1
- package/dist/core/relay/source-commit.js.map +1 -1
- package/dist/core/thinking-level.d.ts +45 -0
- package/dist/core/thinking-level.js +58 -0
- package/dist/core/thinking-level.js.map +1 -0
- package/dist/hermes/hermes-relay-manager.d.ts +3 -0
- package/dist/hermes/hermes-relay-manager.js +7 -0
- package/dist/hermes/hermes-relay-manager.js.map +1 -1
- package/dist/hermes/hermes-session-reasoning-store.d.ts +13 -0
- package/dist/hermes/hermes-session-reasoning-store.js +99 -0
- package/dist/hermes/hermes-session-reasoning-store.js.map +1 -0
- package/dist/hermes/relay/hermes-cron-change-relay.d.ts +28 -0
- package/dist/hermes/relay/hermes-cron-change-relay.js +55 -0
- package/dist/hermes/relay/hermes-cron-change-relay.js.map +1 -0
- package/dist/hermes/runtime/hermes-cron-change-watcher.d.ts +34 -0
- package/dist/hermes/runtime/hermes-cron-change-watcher.js +135 -0
- package/dist/hermes/runtime/hermes-cron-change-watcher.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-api-client.d.ts +10 -0
- package/dist/hermes/runtime/hermes-runtime-api-client.js +7 -4
- package/dist/hermes/runtime/hermes-runtime-api-client.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-api-settings.d.ts +6 -0
- package/dist/hermes/runtime/hermes-runtime-api-settings.js +18 -12
- package/dist/hermes/runtime/hermes-runtime-api-settings.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-chat-cli.d.ts +16 -0
- package/dist/hermes/runtime/hermes-runtime-chat-cli.js +359 -0
- package/dist/hermes/runtime/hermes-runtime-chat-cli.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-chat-file-transfer.d.ts +29 -0
- package/dist/hermes/runtime/hermes-runtime-chat-file-transfer.js +323 -0
- package/dist/hermes/runtime/hermes-runtime-chat-file-transfer.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-chat-message.d.ts +10 -0
- package/dist/hermes/runtime/hermes-runtime-chat-message.js +133 -0
- package/dist/hermes/runtime/hermes-runtime-chat-message.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-chat-output.d.ts +17 -0
- package/dist/hermes/runtime/hermes-runtime-chat-output.js +90 -0
- package/dist/hermes/runtime/hermes-runtime-chat-output.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-chat-queue.d.ts +1 -0
- package/dist/hermes/runtime/hermes-runtime-chat-queue.js +18 -0
- package/dist/hermes/runtime/hermes-runtime-chat-queue.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-chat.d.ts +3 -33
- package/dist/hermes/runtime/hermes-runtime-chat.js +47 -908
- package/dist/hermes/runtime/hermes-runtime-chat.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-command-router.js +6 -1
- package/dist/hermes/runtime/hermes-runtime-command-router.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-reasoning.d.ts +29 -0
- package/dist/hermes/runtime/hermes-runtime-reasoning.js +72 -0
- package/dist/hermes/runtime/hermes-runtime-reasoning.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-sessions.d.ts +2 -2
- package/dist/hermes/runtime/hermes-runtime-sessions.js +23 -2
- package/dist/hermes/runtime/hermes-runtime-sessions.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-slash-command.d.ts +14 -1
- package/dist/hermes/runtime/hermes-runtime-slash-command.js +6 -1
- package/dist/hermes/runtime/hermes-runtime-slash-command.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-thinking.d.ts +14 -0
- package/dist/hermes/runtime/hermes-runtime-thinking.js +105 -0
- package/dist/hermes/runtime/hermes-runtime-thinking.js.map +1 -0
- package/dist/openclaw/handlers/thinking-handlers.d.ts +22 -0
- package/dist/openclaw/handlers/thinking-handlers.js +104 -0
- package/dist/openclaw/handlers/thinking-handlers.js.map +1 -0
- package/dist/openclaw/relay/chat-history.d.ts +5 -1
- package/dist/openclaw/relay/chat-history.js +18 -18
- package/dist/openclaw/relay/chat-history.js.map +1 -1
- package/dist/openclaw/relay/openclaw-source-commit-watcher.d.ts +12 -4
- package/dist/openclaw/relay/openclaw-source-commit-watcher.js +13 -2
- package/dist/openclaw/relay/openclaw-source-commit-watcher.js.map +1 -1
- package/dist/openclaw/relay/openclaw-source-cursor-connections.d.ts +25 -0
- package/dist/openclaw/relay/openclaw-source-cursor-connections.js +77 -0
- package/dist/openclaw/relay/openclaw-source-cursor-connections.js.map +1 -0
- package/dist/openclaw/relay/source-commit-projection.d.ts +28 -6
- package/dist/openclaw/relay/source-commit-projection.js +106 -30
- package/dist/openclaw/relay/source-commit-projection.js.map +1 -1
- package/dist/openclaw/relay/source-row-fingerprints.d.ts +22 -0
- package/dist/openclaw/relay/source-row-fingerprints.js +48 -0
- package/dist/openclaw/relay/source-row-fingerprints.js.map +1 -0
- package/dist/openclaw/relay-manager.js +68 -24
- package/dist/openclaw/relay-manager.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type CanonicalTimelineEvent } from "../../core/relay/timeline-event-log.js";
|
|
2
2
|
import type { HistoryResponse } from "./chat-history.js";
|
|
3
3
|
import type { SourceCommit } from "../../core/relay/source-commit.js";
|
|
4
|
+
import { type SourceRowFingerprints } from "./source-row-fingerprints.js";
|
|
4
5
|
export type SourceCommitProjectionBatch = {
|
|
5
6
|
sourceCommit: SourceCommit;
|
|
6
7
|
events: CanonicalTimelineEvent[];
|
|
@@ -10,16 +11,37 @@ export type SourceCommitProjectionBatch = {
|
|
|
10
11
|
* The cursor only gates visibility; each source row keeps its own canonical
|
|
11
12
|
* identity and source order coordinate.
|
|
12
13
|
*/
|
|
13
|
-
export declare function buildSourceCommitTimelineEvents(history: HistoryResponse, sourceCommit: SourceCommit, previousCommittedThroughSeq?: number
|
|
14
|
+
export declare function buildSourceCommitTimelineEvents(history: HistoryResponse, sourceCommit: SourceCommit, previousCommittedThroughSeq?: number,
|
|
15
|
+
/** 宿主原地改写后重投的行:源 seq → 原始内容指纹,参与事件 ID。 */
|
|
16
|
+
contentRevisions?: ReadonlyMap<number, string>): CanonicalTimelineEvent[];
|
|
17
|
+
export type SourceCommitPageReader = (cursorSeq: number) => Promise<HistoryResponse>;
|
|
18
|
+
export type SourceCommitMediaRelay = (page: HistoryResponse) => Promise<HistoryResponse>;
|
|
14
19
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
20
|
+
* 投影新提交的源行。宿主行在提交时可能已带投递媒体(openclawDelivery /
|
|
21
|
+
* openclawDisplayContent 中的托管外发图片),必须先经 relayMedia 上传并改写为 Relay
|
|
22
|
+
* 文件块,否则图片只能等下一次 history 刷新才出现。fingerprints 记录每行原始内容,
|
|
23
|
+
* 供宿主原地改写后的重投比较。
|
|
19
24
|
*/
|
|
20
25
|
export declare function projectSourceCommitHistoryPages(options: {
|
|
21
26
|
sourceCommit: SourceCommit;
|
|
22
27
|
previousCommittedThroughSeq?: number;
|
|
23
|
-
readPage:
|
|
28
|
+
readPage: SourceCommitPageReader;
|
|
29
|
+
relayMedia?: SourceCommitMediaRelay;
|
|
30
|
+
fingerprints?: SourceRowFingerprints;
|
|
31
|
+
publish: (batch: SourceCommitProjectionBatch) => void | Promise<void>;
|
|
32
|
+
}): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* 宿主原地改写后的重投(OpenClaw 先提交最终回复,再把托管图片补写进同一行,seq 水位不变)。
|
|
35
|
+
*
|
|
36
|
+
* 只比较本进程投影过的范围:从记录了指纹的最小源 seq 起重读,原始内容指纹变化、或此前
|
|
37
|
+
* 不可见而现在可见的行才重新发布。重投事件 ID 含内容指纹:同一内容永远得到同一 ID,
|
|
38
|
+
* Relay 按 eventId 与内容哈希幂等;不同内容得到新 ID,作为同一 canonical 消息的新版本。
|
|
39
|
+
* 返回重新发布的行数。
|
|
40
|
+
*/
|
|
41
|
+
export declare function projectSourceCommitRewrite(options: {
|
|
42
|
+
sourceCommit: SourceCommit;
|
|
43
|
+
readPage: SourceCommitPageReader;
|
|
44
|
+
relayMedia?: SourceCommitMediaRelay;
|
|
45
|
+
fingerprints: SourceRowFingerprints;
|
|
24
46
|
publish: (batch: SourceCommitProjectionBatch) => void | Promise<void>;
|
|
25
47
|
}): Promise<number>;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { parseCanonicalTimelineEvent } from "../../core/relay/timeline-event-log.js";
|
|
3
|
+
import { sourceRowFingerprint, } from "./source-row-fingerprints.js";
|
|
3
4
|
/**
|
|
4
5
|
* Turn the authoritative source snapshot into a deterministic append stream.
|
|
5
6
|
* The cursor only gates visibility; each source row keeps its own canonical
|
|
6
7
|
* identity and source order coordinate.
|
|
7
8
|
*/
|
|
8
|
-
export function buildSourceCommitTimelineEvents(history, sourceCommit, previousCommittedThroughSeq
|
|
9
|
+
export function buildSourceCommitTimelineEvents(history, sourceCommit, previousCommittedThroughSeq,
|
|
10
|
+
/** 宿主原地改写后重投的行:源 seq → 原始内容指纹,参与事件 ID。 */
|
|
11
|
+
contentRevisions) {
|
|
9
12
|
const messages = history.timelineSnapshot?.messages ?? [];
|
|
10
13
|
return messages.flatMap((message, index) => {
|
|
11
14
|
if (message.projectionVersion !== 3) {
|
|
@@ -30,8 +33,12 @@ export function buildSourceCommitTimelineEvents(history, sourceCommit, previousC
|
|
|
30
33
|
: message.messageState === "failed" || message.messageState === "aborted"
|
|
31
34
|
? "run.failed"
|
|
32
35
|
: "message.completed";
|
|
36
|
+
const contentRevision = contentRevisions?.get(sourceSeq);
|
|
37
|
+
const eventIdParts = [sourceCommit.sourceOrderScope, sourceCommit.sourceGeneration, messageId, sourceSeq];
|
|
38
|
+
if (contentRevision)
|
|
39
|
+
eventIdParts.push(contentRevision);
|
|
33
40
|
const eventId = `evt_source_commit_${createHash("sha256")
|
|
34
|
-
.update(JSON.stringify(
|
|
41
|
+
.update(JSON.stringify(eventIdParts))
|
|
35
42
|
.digest("hex")
|
|
36
43
|
.slice(0, 32)}`;
|
|
37
44
|
return [parseCanonicalTimelineEvent({
|
|
@@ -77,14 +84,32 @@ export function buildSourceCommitTimelineEvents(history, sourceCommit, previousC
|
|
|
77
84
|
})];
|
|
78
85
|
});
|
|
79
86
|
}
|
|
87
|
+
/** 只保留 (afterSeq, throughSeq] 内、且通过 include 的快照行。 */
|
|
88
|
+
function withSnapshotRows(history, afterSeq, throughSeq, include = () => true) {
|
|
89
|
+
if (!history.timelineSnapshot)
|
|
90
|
+
return history;
|
|
91
|
+
return {
|
|
92
|
+
...history,
|
|
93
|
+
timelineSnapshot: {
|
|
94
|
+
...history.timelineSnapshot,
|
|
95
|
+
messages: history.timelineSnapshot.messages.filter((message) => {
|
|
96
|
+
const seq = message.sourceOrderSeq;
|
|
97
|
+
return Number.isSafeInteger(seq)
|
|
98
|
+
&& seq > afterSeq
|
|
99
|
+
&& seq <= throughSeq
|
|
100
|
+
&& include(message);
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
80
105
|
/**
|
|
81
106
|
* Walk source-history pages without treating a page size as a source
|
|
82
107
|
* watermark. The caller's page reader must report the raw SQLite sequence it
|
|
83
108
|
* actually covered; visible heartbeat filtering is therefore safe, while a
|
|
84
109
|
* missing page fails before the local observer cursor can advance.
|
|
85
110
|
*/
|
|
86
|
-
|
|
87
|
-
let readThroughSeq = options.
|
|
111
|
+
async function walkSourceCommitPages(options) {
|
|
112
|
+
let readThroughSeq = options.startSeq;
|
|
88
113
|
const targetSeq = options.sourceCommit.committedThroughSeq;
|
|
89
114
|
while (readThroughSeq < targetSeq) {
|
|
90
115
|
const history = await options.readPage(readThroughSeq);
|
|
@@ -101,32 +126,17 @@ export async function projectSourceCommitHistoryPages(options) {
|
|
|
101
126
|
throw new Error(`source commit ${options.sourceCommit.sourceRevision} history page has a source gap`);
|
|
102
127
|
}
|
|
103
128
|
const pageCommittedThroughSeq = Math.min(sourceReadThroughSeq, targetSeq);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
&& seq <= pageCommittedThroughSeq;
|
|
116
|
-
}) ?? [];
|
|
117
|
-
const pageHistory = history.timelineSnapshot
|
|
118
|
-
? {
|
|
119
|
-
...history,
|
|
120
|
-
timelineSnapshot: {
|
|
121
|
-
...history.timelineSnapshot,
|
|
122
|
-
messages: pageMessages,
|
|
123
|
-
},
|
|
124
|
-
}
|
|
125
|
-
: history;
|
|
126
|
-
const events = buildSourceCommitTimelineEvents(pageHistory, pageSourceCommit, readThroughSeq);
|
|
127
|
-
if (events.length > 0) {
|
|
128
|
-
await options.publish({ sourceCommit: pageSourceCommit, events });
|
|
129
|
-
}
|
|
129
|
+
await options.visit({
|
|
130
|
+
rawPage: history,
|
|
131
|
+
afterSeq: readThroughSeq,
|
|
132
|
+
pageSourceCommit: {
|
|
133
|
+
...options.sourceCommit,
|
|
134
|
+
committedThroughSeq: pageCommittedThroughSeq,
|
|
135
|
+
sourceRevision: pageCommittedThroughSeq === targetSeq
|
|
136
|
+
? options.sourceCommit.sourceRevision
|
|
137
|
+
: `seq:${pageCommittedThroughSeq}`,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
130
140
|
readThroughSeq = sourceReadThroughSeq;
|
|
131
141
|
if (readThroughSeq < targetSeq && history.sourceHasMore !== true) {
|
|
132
142
|
throw new Error(`source commit ${options.sourceCommit.sourceRevision} history page ended before source watermark`);
|
|
@@ -134,6 +144,72 @@ export async function projectSourceCommitHistoryPages(options) {
|
|
|
134
144
|
}
|
|
135
145
|
return readThroughSeq;
|
|
136
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* 投影新提交的源行。宿主行在提交时可能已带投递媒体(openclawDelivery /
|
|
149
|
+
* openclawDisplayContent 中的托管外发图片),必须先经 relayMedia 上传并改写为 Relay
|
|
150
|
+
* 文件块,否则图片只能等下一次 history 刷新才出现。fingerprints 记录每行原始内容,
|
|
151
|
+
* 供宿主原地改写后的重投比较。
|
|
152
|
+
*/
|
|
153
|
+
export async function projectSourceCommitHistoryPages(options) {
|
|
154
|
+
const relayMedia = options.relayMedia ?? (async (page) => page);
|
|
155
|
+
return walkSourceCommitPages({
|
|
156
|
+
sourceCommit: options.sourceCommit,
|
|
157
|
+
startSeq: options.previousCommittedThroughSeq ?? 0,
|
|
158
|
+
readPage: options.readPage,
|
|
159
|
+
visit: async ({ rawPage, pageSourceCommit, afterSeq }) => {
|
|
160
|
+
const throughSeq = pageSourceCommit.committedThroughSeq;
|
|
161
|
+
const rawRows = withSnapshotRows(rawPage, afterSeq, throughSeq).timelineSnapshot?.messages ?? [];
|
|
162
|
+
const pageHistory = withSnapshotRows(await relayMedia(rawPage), afterSeq, throughSeq);
|
|
163
|
+
const events = buildSourceCommitTimelineEvents(pageHistory, pageSourceCommit, afterSeq);
|
|
164
|
+
if (events.length > 0) {
|
|
165
|
+
await options.publish({ sourceCommit: pageSourceCommit, events });
|
|
166
|
+
}
|
|
167
|
+
options.fingerprints?.record(pageSourceCommit, rawRows);
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 宿主原地改写后的重投(OpenClaw 先提交最终回复,再把托管图片补写进同一行,seq 水位不变)。
|
|
173
|
+
*
|
|
174
|
+
* 只比较本进程投影过的范围:从记录了指纹的最小源 seq 起重读,原始内容指纹变化、或此前
|
|
175
|
+
* 不可见而现在可见的行才重新发布。重投事件 ID 含内容指纹:同一内容永远得到同一 ID,
|
|
176
|
+
* Relay 按 eventId 与内容哈希幂等;不同内容得到新 ID,作为同一 canonical 消息的新版本。
|
|
177
|
+
* 返回重新发布的行数。
|
|
178
|
+
*/
|
|
179
|
+
export async function projectSourceCommitRewrite(options) {
|
|
180
|
+
const trackedFromSeq = options.fingerprints.trackedFromSeq(options.sourceCommit);
|
|
181
|
+
if (trackedFromSeq === undefined)
|
|
182
|
+
return 0;
|
|
183
|
+
const relayMedia = options.relayMedia ?? (async (page) => page);
|
|
184
|
+
let republished = 0;
|
|
185
|
+
await walkSourceCommitPages({
|
|
186
|
+
sourceCommit: options.sourceCommit,
|
|
187
|
+
startSeq: trackedFromSeq - 1,
|
|
188
|
+
readPage: options.readPage,
|
|
189
|
+
visit: async ({ rawPage, pageSourceCommit, afterSeq }) => {
|
|
190
|
+
const throughSeq = pageSourceCommit.committedThroughSeq;
|
|
191
|
+
const revisions = new Map();
|
|
192
|
+
const rawRows = withSnapshotRows(rawPage, afterSeq, throughSeq, (message) => {
|
|
193
|
+
const fingerprint = sourceRowFingerprint(message);
|
|
194
|
+
if (options.fingerprints.matches(pageSourceCommit, message.sourceOrderSeq, fingerprint))
|
|
195
|
+
return false;
|
|
196
|
+
revisions.set(message.sourceOrderSeq, fingerprint);
|
|
197
|
+
return true;
|
|
198
|
+
}).timelineSnapshot?.messages ?? [];
|
|
199
|
+
if (rawRows.length === 0)
|
|
200
|
+
return;
|
|
201
|
+
// 媒体处理使用整页(侧车媒体行按源身份归并到父消息),之后只保留变化的行。
|
|
202
|
+
const pageHistory = withSnapshotRows(await relayMedia(rawPage), afterSeq, throughSeq, (message) => revisions.has(message.sourceOrderSeq));
|
|
203
|
+
const events = buildSourceCommitTimelineEvents(pageHistory, pageSourceCommit, afterSeq, revisions);
|
|
204
|
+
if (events.length > 0) {
|
|
205
|
+
await options.publish({ sourceCommit: pageSourceCommit, events });
|
|
206
|
+
}
|
|
207
|
+
options.fingerprints.record(pageSourceCommit, rawRows);
|
|
208
|
+
republished += events.length;
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
return republished;
|
|
212
|
+
}
|
|
137
213
|
/** 源投影必须带会话归属;缺失时显式失败,绝不猜成 main 写错会话。 */
|
|
138
214
|
function requireProjectionSessionKey(value) {
|
|
139
215
|
const sessionKey = typeof value === "string" ? value.trim() : "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-commit-projection.js","sourceRoot":"","sources":["../../../src/openclaw/relay/source-commit-projection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,2BAA2B,EAA+B,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"source-commit-projection.js","sourceRoot":"","sources":["../../../src/openclaw/relay/source-commit-projection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SourceCommit } from "../../core/relay/source-commit.js";
|
|
2
|
+
import type { HistoryResponse } from "./chat-history.js";
|
|
3
|
+
export type SourceSnapshotMessage = NonNullable<HistoryResponse["timelineSnapshot"]>["messages"][number];
|
|
4
|
+
/**
|
|
5
|
+
* 源行原始内容指纹:对未经媒体处理的投影行整体做 SHA-256。投影是确定的,
|
|
6
|
+
* 同一源行内容不变时指纹不变;宿主原地补写(如追加托管图片)后指纹变化。
|
|
7
|
+
*/
|
|
8
|
+
export declare function sourceRowFingerprint(message: SourceSnapshotMessage): string;
|
|
9
|
+
/**
|
|
10
|
+
* 记录本进程已投影过的源行指纹(按源作用域分组,源 seq → 指纹)。
|
|
11
|
+
*
|
|
12
|
+
* 只覆盖本进程投影过的范围:进程启动前投影的行没有指纹,不参与原地改写比较,
|
|
13
|
+
* 那部分由手机 history 刷新兜底。会话切换到新的源作用域时丢弃旧作用域,内存只随
|
|
14
|
+
* 当前会话增长。
|
|
15
|
+
*/
|
|
16
|
+
export declare class SourceRowFingerprints {
|
|
17
|
+
private readonly byScope;
|
|
18
|
+
record(commit: SourceCommit, messages: readonly SourceSnapshotMessage[]): void;
|
|
19
|
+
matches(commit: SourceCommit, sourceSeq: number, fingerprint: string): boolean;
|
|
20
|
+
/** 本进程在该作用域记录过指纹的最小源 seq;没有记录时返回 undefined。 */
|
|
21
|
+
trackedFromSeq(commit: SourceCommit): number | undefined;
|
|
22
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { sourceCommitScope } from "../../core/relay/source-commit.js";
|
|
3
|
+
/**
|
|
4
|
+
* 源行原始内容指纹:对未经媒体处理的投影行整体做 SHA-256。投影是确定的,
|
|
5
|
+
* 同一源行内容不变时指纹不变;宿主原地补写(如追加托管图片)后指纹变化。
|
|
6
|
+
*/
|
|
7
|
+
export function sourceRowFingerprint(message) {
|
|
8
|
+
return createHash("sha256").update(JSON.stringify(message)).digest("hex").slice(0, 32);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 记录本进程已投影过的源行指纹(按源作用域分组,源 seq → 指纹)。
|
|
12
|
+
*
|
|
13
|
+
* 只覆盖本进程投影过的范围:进程启动前投影的行没有指纹,不参与原地改写比较,
|
|
14
|
+
* 那部分由手机 history 刷新兜底。会话切换到新的源作用域时丢弃旧作用域,内存只随
|
|
15
|
+
* 当前会话增长。
|
|
16
|
+
*/
|
|
17
|
+
export class SourceRowFingerprints {
|
|
18
|
+
byScope = new Map();
|
|
19
|
+
record(commit, messages) {
|
|
20
|
+
const scope = sourceCommitScope(commit);
|
|
21
|
+
let rows = this.byScope.get(scope);
|
|
22
|
+
if (!rows) {
|
|
23
|
+
this.byScope.clear();
|
|
24
|
+
rows = new Map();
|
|
25
|
+
this.byScope.set(scope, rows);
|
|
26
|
+
}
|
|
27
|
+
for (const message of messages) {
|
|
28
|
+
const seq = message.sourceOrderSeq;
|
|
29
|
+
if (typeof seq !== "number" || !Number.isSafeInteger(seq))
|
|
30
|
+
continue;
|
|
31
|
+
rows.set(seq, sourceRowFingerprint(message));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
matches(commit, sourceSeq, fingerprint) {
|
|
35
|
+
return this.byScope.get(sourceCommitScope(commit))?.get(sourceSeq) === fingerprint;
|
|
36
|
+
}
|
|
37
|
+
/** 本进程在该作用域记录过指纹的最小源 seq;没有记录时返回 undefined。 */
|
|
38
|
+
trackedFromSeq(commit) {
|
|
39
|
+
const rows = this.byScope.get(sourceCommitScope(commit));
|
|
40
|
+
if (!rows || rows.size === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
let minimum;
|
|
43
|
+
for (const seq of rows.keys())
|
|
44
|
+
minimum = minimum === undefined ? seq : Math.min(minimum, seq);
|
|
45
|
+
return minimum;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=source-row-fingerprints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-row-fingerprints.js","sourceRoot":"","sources":["../../../src/openclaw/relay/source-row-fingerprints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAqB,MAAM,mCAAmC,CAAC;AAKzF;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAA8B;IACjE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,qBAAqB;IACf,OAAO,GAAG,IAAI,GAAG,EAA+B,CAAC;IAElE,MAAM,CAAC,MAAoB,EAAE,QAA0C;QACrE,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC;YACnC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC;gBAAE,SAAS;YACpE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,OAAO,CAAC,MAAoB,EAAE,SAAiB,EAAE,WAAmB;QAClE,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,KAAK,WAAW,CAAC;IACrF,CAAC;IAED,+CAA+C;IAC/C,cAAc,CAAC,MAAoB;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC/C,IAAI,OAA2B,CAAC;QAChC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC9F,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -3,14 +3,18 @@ import { bindRelayAbortSignal, buildRelayUrl, createRelayWebSocket, disconnectRe
|
|
|
3
3
|
import { handleLocalCommand } from "./handlers/local-handlers.js";
|
|
4
4
|
import { createOpenClawContextUsagePublisher } from "./relay/openclaw-context-usage-publisher.js";
|
|
5
5
|
import { handleProviderCommand } from "./handlers/provider-handlers.js";
|
|
6
|
+
import { handleOpenClawThinkingCommand } from "./handlers/thinking-handlers.js";
|
|
6
7
|
import { DEFAULT_GATEWAY_SESSION_DEFAULTS, contextUsageSnapshotFromSessionsList, canonicalizeRelayParams, canonicalizeSessionKey, extractGatewaySessionDefaults, } from "./relay/session-context.js";
|
|
7
8
|
import { appendUniqueSuffix, extractChatRole, extractChatText, normalizeChatEventPayload, normalizeChatState, withMessageText, } from "../core/relay/chat-payload.js";
|
|
8
9
|
import { canonicalizeOpenClawGatewayHistoryResponse, extractHistoryMediaContent, extractExplicitParentMediaContent, extractHistoryOutcome, readOpenClawSourceCommitCursor, readOpenClawTranscriptChatHistory, withTimeout, } from "./relay/chat-history.js";
|
|
9
10
|
import { watchOpenClawSourceCommit } from "./relay/openclaw-source-commit-watcher.js";
|
|
10
11
|
import { createSourceCommitCheckpointClient } from "../core/relay/source-commit-checkpoint-client.js";
|
|
11
|
-
import {
|
|
12
|
+
import { createSourceCommitPublishFlow, SOURCE_COMMIT_PAGE_LIMIT, sourceCommitDeliveryId, } from "../core/relay/source-commit-publish-flow.js";
|
|
13
|
+
import { projectSourceCommitHistoryPages, projectSourceCommitRewrite, } from "./relay/source-commit-projection.js";
|
|
14
|
+
import { SourceRowFingerprints } from "./relay/source-row-fingerprints.js";
|
|
12
15
|
import { join } from "node:path";
|
|
13
16
|
import { resolveOpenClawStateDir } from "./runtime/openclaw-paths.js";
|
|
17
|
+
import { createSourceCommit } from "../core/relay/source-commit.js";
|
|
14
18
|
import { openClawAgentIdFromSessionKey } from "../core/relay/timeline-projection-v3.js";
|
|
15
19
|
import { OpenClawChatSendDedupeCoordinator, } from "./relay/chat-send-dedupe-coordinator.js";
|
|
16
20
|
import { buildMobileAssistantDeltaPayload, buildMobileAssistantCompletedPayload, buildMobileAssistantErrorPayload, buildMobileAssistantFinalPayload, } from "../core/relay/mobile-chat-run-bridge.js";
|
|
@@ -83,6 +87,11 @@ export async function runRelayManager(opts) {
|
|
|
83
87
|
const sourceCommitCheckpoints = createSourceCommitCheckpointClient({
|
|
84
88
|
send: (query) => sendRelayJson(relayWs, query).status === "sent",
|
|
85
89
|
});
|
|
90
|
+
// 源投影流控:至多一个投影帧在 Relay 队列中,命令响应不会被补齐流量挤到超时。
|
|
91
|
+
let relayEventsAcknowledged = false;
|
|
92
|
+
const sourceCommitPublishFlow = createSourceCommitPublishFlow({
|
|
93
|
+
isAcknowledged: () => relayEventsAcknowledged,
|
|
94
|
+
});
|
|
86
95
|
const chatRunContexts = new Map();
|
|
87
96
|
const chatSendDedupe = new OpenClawChatSendDedupeCoordinator(() => sessionDefaults);
|
|
88
97
|
const outgoingMediaUploadCache = new Map();
|
|
@@ -249,7 +258,7 @@ export async function runRelayManager(opts) {
|
|
|
249
258
|
? { status: "delivered" }
|
|
250
259
|
: { status: "retryable", error: result.error ?? new Error(`relay_send_${result.status}`) };
|
|
251
260
|
}
|
|
252
|
-
async function publishAndSendGatewayEvent(eventName, payload, publishOffice, userMessage, confirmWrite = false) {
|
|
261
|
+
async function publishAndSendGatewayEvent(eventName, payload, publishOffice, userMessage, confirmWrite = false, deliveryId) {
|
|
253
262
|
let outgoingPayload = payload;
|
|
254
263
|
if (eventName === "chat") {
|
|
255
264
|
try {
|
|
@@ -282,6 +291,7 @@ export async function runRelayManager(opts) {
|
|
|
282
291
|
type: "event",
|
|
283
292
|
event: eventName,
|
|
284
293
|
payload: outgoingPayload,
|
|
294
|
+
...(deliveryId ? { deliveryId } : {}),
|
|
285
295
|
};
|
|
286
296
|
if (confirmWrite)
|
|
287
297
|
return sendWithWriteConfirmation(message);
|
|
@@ -405,36 +415,56 @@ export async function runRelayManager(opts) {
|
|
|
405
415
|
return;
|
|
406
416
|
const agentId = openClawAgentIdFromSessionKey(normalizedSessionKey);
|
|
407
417
|
const databasePath = join(resolveOpenClawStateDir(), "agents", agentId, "agent", "openclaw-agent.sqlite");
|
|
418
|
+
const fingerprints = new SourceRowFingerprints();
|
|
419
|
+
const readPage = (cursorSeq) => withTimeout(requestChatHistoryFromClawConnect({
|
|
420
|
+
sessionKey: normalizedSessionKey,
|
|
421
|
+
limit: SOURCE_COMMIT_PAGE_LIMIT,
|
|
422
|
+
cursor: `seq:${cursorSeq}`,
|
|
423
|
+
direction: "newer",
|
|
424
|
+
}), CHAT_HISTORY_FETCH_TIMEOUT_MS, "source commit history reconciliation");
|
|
425
|
+
// 与手机拉取 history 走同一媒体处理(共用上传缓存,同一附件得到同一文件)。
|
|
426
|
+
const relayMedia = async (page) => await relayOutgoingMediaForResponse("chat.history", page);
|
|
427
|
+
const publish = ({ sourceCommit: pageSourceCommit, events }) => sourceCommitPublishFlow.publish(sourceCommitDeliveryId(pageSourceCommit, events.map((event) => event.eventId)), async (deliveryId) => {
|
|
428
|
+
await waitForRelaySocketDrain(relayWs, 256 * 1024);
|
|
429
|
+
const deliveryResult = await publishAndSendGatewayEvent("chat", {
|
|
430
|
+
state: "source_commit",
|
|
431
|
+
sessionKey: normalizedSessionKey,
|
|
432
|
+
sourceCommit: pageSourceCommit,
|
|
433
|
+
timelineEvents: events,
|
|
434
|
+
}, true, undefined, true, deliveryId);
|
|
435
|
+
if (deliveryResult?.status === "retryable") {
|
|
436
|
+
throw deliveryResult.error;
|
|
437
|
+
}
|
|
438
|
+
});
|
|
408
439
|
const watcher = watchOpenClawSourceCommit({
|
|
409
440
|
databasePath,
|
|
410
|
-
resolveInitialWatermark: (cursor) => sourceCommitCheckpoints.resolveWatermark(cursor),
|
|
441
|
+
resolveInitialWatermark: (cursor) => sourceCommitCheckpoints.resolveWatermark(createSourceCommit(cursor)),
|
|
411
442
|
readCursor: () => readOpenClawSourceCommitCursor({
|
|
412
443
|
sessionKey: normalizedSessionKey,
|
|
413
444
|
projectionVersion: 3,
|
|
414
445
|
projectionGatewayId: opts.gatewayId,
|
|
415
446
|
}, sessionDefaults),
|
|
416
|
-
onCommit: async (
|
|
447
|
+
onCommit: async (cursor, previousCommittedThroughSeq) => {
|
|
417
448
|
await projectSourceCommitHistoryPages({
|
|
418
|
-
sourceCommit
|
|
449
|
+
// 改写代号只用于本地触发重投,不随 sourceCommit 发给 Relay。
|
|
450
|
+
sourceCommit: createSourceCommit(cursor),
|
|
419
451
|
previousCommittedThroughSeq,
|
|
420
|
-
readPage
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
},
|
|
452
|
+
readPage,
|
|
453
|
+
relayMedia,
|
|
454
|
+
fingerprints,
|
|
455
|
+
publish,
|
|
456
|
+
});
|
|
457
|
+
},
|
|
458
|
+
// OpenClaw 先提交最终回复,再把托管图片原地补写进同一行(seq 不变、改写代号变化);
|
|
459
|
+
// 重投内容变化的行,图片无需刷新即可出现。
|
|
460
|
+
rewriteGenerationOf: (cursor) => cursor.rewriteGeneration,
|
|
461
|
+
onRewrite: async (cursor) => {
|
|
462
|
+
await projectSourceCommitRewrite({
|
|
463
|
+
sourceCommit: createSourceCommit(cursor),
|
|
464
|
+
readPage,
|
|
465
|
+
relayMedia,
|
|
466
|
+
fingerprints,
|
|
467
|
+
publish,
|
|
438
468
|
});
|
|
439
469
|
},
|
|
440
470
|
onError: (error) => {
|
|
@@ -887,6 +917,7 @@ export async function runRelayManager(opts) {
|
|
|
887
917
|
relayHelloTimer = undefined;
|
|
888
918
|
}
|
|
889
919
|
const deliveryMode = reliableDeliveryModeFromRelayHello(msg);
|
|
920
|
+
relayEventsAcknowledged = deliveryMode === "acknowledged";
|
|
890
921
|
deliveryOutbox.attach(relayWs, deliveryMode);
|
|
891
922
|
console.log(`[relay] reliable delivery mode=${deliveryMode}`);
|
|
892
923
|
sourceCommitCheckpoints.markRelayHello(msg.protocolCapabilities);
|
|
@@ -894,7 +925,8 @@ export async function runRelayManager(opts) {
|
|
|
894
925
|
return;
|
|
895
926
|
}
|
|
896
927
|
if (msg.type === "event_ack") {
|
|
897
|
-
|
|
928
|
+
if (!sourceCommitPublishFlow.acknowledge(msg.id))
|
|
929
|
+
deliveryOutbox.acknowledge(msg.id);
|
|
898
930
|
return;
|
|
899
931
|
}
|
|
900
932
|
if (sourceCommitCheckpoints.handleResponse(msg)) {
|
|
@@ -924,6 +956,17 @@ export async function runRelayManager(opts) {
|
|
|
924
956
|
}
|
|
925
957
|
return;
|
|
926
958
|
}
|
|
959
|
+
// 会话思考等级命令经网关 sessions.list / sessions.patch 读写,需要当前网关连接。
|
|
960
|
+
const thinkingPromise = handleOpenClawThinkingCommand(msg.method, msg.params, gatewayClient);
|
|
961
|
+
if (thinkingPromise !== null) {
|
|
962
|
+
const result = await thinkingPromise;
|
|
963
|
+
if (requestId) {
|
|
964
|
+
send(result.ok
|
|
965
|
+
? { type: "res", id: requestId, ok: true, payload: result.payload }
|
|
966
|
+
: { type: "res", id: requestId, ok: false, error: { message: result.error } });
|
|
967
|
+
}
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
927
970
|
// Handle local commands without forwarding to the gateway.
|
|
928
971
|
const localResult = await handleLocalCommand(msg.method, msg.params, {
|
|
929
972
|
requestId,
|
|
@@ -1152,6 +1195,7 @@ export async function runRelayManager(opts) {
|
|
|
1152
1195
|
watcher.close();
|
|
1153
1196
|
sourceCommitWatchers.clear();
|
|
1154
1197
|
sourceCommitCheckpoints.close(`relay closed ${code}`);
|
|
1198
|
+
sourceCommitPublishFlow.close(`relay closed ${code}`);
|
|
1155
1199
|
chatSendDedupe.clearAll();
|
|
1156
1200
|
contextUsage.dispose();
|
|
1157
1201
|
if (relayHelloTimer) {
|