clawconnect-agent 0.2.13 → 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.
Files changed (54) hide show
  1. package/dist/commands/send-file.d.ts +2 -0
  2. package/dist/commands/send-file.js +8 -0
  3. package/dist/commands/send-file.js.map +1 -1
  4. package/dist/core/relay/source-commit.d.ts +7 -0
  5. package/dist/core/relay/source-commit.js +25 -1
  6. package/dist/core/relay/source-commit.js.map +1 -1
  7. package/dist/hermes/hermes-relay-manager.d.ts +3 -0
  8. package/dist/hermes/hermes-relay-manager.js +7 -0
  9. package/dist/hermes/hermes-relay-manager.js.map +1 -1
  10. package/dist/hermes/relay/hermes-cron-change-relay.d.ts +28 -0
  11. package/dist/hermes/relay/hermes-cron-change-relay.js +55 -0
  12. package/dist/hermes/relay/hermes-cron-change-relay.js.map +1 -0
  13. package/dist/hermes/runtime/hermes-cron-change-watcher.d.ts +34 -0
  14. package/dist/hermes/runtime/hermes-cron-change-watcher.js +135 -0
  15. package/dist/hermes/runtime/hermes-cron-change-watcher.js.map +1 -0
  16. package/dist/hermes/runtime/hermes-runtime-chat-cli.d.ts +16 -0
  17. package/dist/hermes/runtime/hermes-runtime-chat-cli.js +359 -0
  18. package/dist/hermes/runtime/hermes-runtime-chat-cli.js.map +1 -0
  19. package/dist/hermes/runtime/hermes-runtime-chat-file-transfer.d.ts +29 -0
  20. package/dist/hermes/runtime/hermes-runtime-chat-file-transfer.js +323 -0
  21. package/dist/hermes/runtime/hermes-runtime-chat-file-transfer.js.map +1 -0
  22. package/dist/hermes/runtime/hermes-runtime-chat-message.d.ts +10 -0
  23. package/dist/hermes/runtime/hermes-runtime-chat-message.js +133 -0
  24. package/dist/hermes/runtime/hermes-runtime-chat-message.js.map +1 -0
  25. package/dist/hermes/runtime/hermes-runtime-chat-output.d.ts +17 -0
  26. package/dist/hermes/runtime/hermes-runtime-chat-output.js +90 -0
  27. package/dist/hermes/runtime/hermes-runtime-chat-output.js.map +1 -0
  28. package/dist/hermes/runtime/hermes-runtime-chat-queue.d.ts +1 -0
  29. package/dist/hermes/runtime/hermes-runtime-chat-queue.js +18 -0
  30. package/dist/hermes/runtime/hermes-runtime-chat-queue.js.map +1 -0
  31. package/dist/hermes/runtime/hermes-runtime-chat.d.ts +3 -33
  32. package/dist/hermes/runtime/hermes-runtime-chat.js +41 -910
  33. package/dist/hermes/runtime/hermes-runtime-chat.js.map +1 -1
  34. package/dist/hermes/runtime/hermes-runtime-slash-command.d.ts +14 -1
  35. package/dist/hermes/runtime/hermes-runtime-slash-command.js +6 -1
  36. package/dist/hermes/runtime/hermes-runtime-slash-command.js.map +1 -1
  37. package/dist/openclaw/relay/chat-history.d.ts +5 -1
  38. package/dist/openclaw/relay/chat-history.js +4 -7
  39. package/dist/openclaw/relay/chat-history.js.map +1 -1
  40. package/dist/openclaw/relay/openclaw-source-commit-watcher.d.ts +7 -4
  41. package/dist/openclaw/relay/openclaw-source-commit-watcher.js +2 -0
  42. package/dist/openclaw/relay/openclaw-source-commit-watcher.js.map +1 -1
  43. package/dist/openclaw/relay/openclaw-source-cursor-connections.d.ts +2 -0
  44. package/dist/openclaw/relay/openclaw-source-cursor-connections.js +12 -0
  45. package/dist/openclaw/relay/openclaw-source-cursor-connections.js.map +1 -1
  46. package/dist/openclaw/relay/source-commit-projection.d.ts +28 -6
  47. package/dist/openclaw/relay/source-commit-projection.js +106 -30
  48. package/dist/openclaw/relay/source-commit-projection.js.map +1 -1
  49. package/dist/openclaw/relay/source-row-fingerprints.d.ts +22 -0
  50. package/dist/openclaw/relay/source-row-fingerprints.js +48 -0
  51. package/dist/openclaw/relay/source-row-fingerprints.js.map +1 -0
  52. package/dist/openclaw/relay-manager.js +44 -22
  53. package/dist/openclaw/relay-manager.js.map +1 -1
  54. package/package.json +1 -1
@@ -0,0 +1,323 @@
1
+ // Hermes 文件发送校验:按 sourceRunId 精确定位当前轮次,只认第一方 send-file 回执与 terminal 结构化结果。
2
+ import { stringValue } from "./hermes-runtime-values.js";
3
+ import { hermesSourceRunIdFromMessage } from "./hermes-runtime-history-completion.js";
4
+ import { readHermesSessionMessages } from "./hermes-runtime-state-db.js";
5
+ import { clearPendingHermesFileTransfer, getPendingHermesFileTransfer, recordPendingHermesFileTransfer, } from "./hermes-file-transfer-state.js";
6
+ const HERMES_FILE_TRANSFER_NOT_SENT_MESSAGE = "文件尚未发送:没有检测到可验证的 clawconnect send-file 成功结果,请补充或确认要发送的文件后重试。";
7
+ const HERMES_FILE_TRANSFER_CONTENT_MISSING_MESSAGE = "本轮没有可显示的结构化回答。";
8
+ const HERMES_FILE_TRANSFER_CANCELLED_MESSAGE = "文件发送已取消。";
9
+ export async function planHermesChatPreparation(params) {
10
+ // A continuation is enabled only by a durable host-side pending state from
11
+ // the same gateway/mobile/Hermes session. The user's wording is deliberately
12
+ // opaque to this router; Hermes receives the full history and decides what it
13
+ // means (including any language, selection, or clarification).
14
+ const pending = params.sessionId
15
+ ? await getPendingHermesFileTransfer({
16
+ gatewayId: params.gatewayId,
17
+ sessionKey: params.sessionKey,
18
+ hermesSessionId: params.sessionId,
19
+ sourceRunId: params.sourceRunId,
20
+ })
21
+ : undefined;
22
+ return {
23
+ // The mobile relay explicitly grants the typed CLI file-transfer
24
+ // capability on every turn. The model/tool protocol, rather than a
25
+ // language-specific intent table, decides whether a send is needed. This
26
+ // deliberately trades the API fast path for a buffered CLI path on mobile
27
+ // turns, where send-file receipts are persisted atomically.
28
+ preloadFileTransferSkill: params.fileTransferCapability === "cli" || pending !== undefined,
29
+ fileTransferMode: pending ? "continuation" : undefined,
30
+ ...(pending ? { pendingFileTransfer: pending } : {}),
31
+ };
32
+ }
33
+ function normalizeHermesRole(value) {
34
+ return typeof value === "string" ? value.trim().toLowerCase() : "";
35
+ }
36
+ export async function verifyHermesFileTransferIfRequired(params) {
37
+ if (!params.plan.preloadFileTransferSkill) {
38
+ return {};
39
+ }
40
+ const messages = params.sessionId ? await readHermesSessionMessages(params.sessionId) : undefined;
41
+ const evidence = params.sourceRunId
42
+ ? collectHermesFileTransferEvidence(messages, params.sourceRunId)
43
+ : [];
44
+ if (evidence.length > 0) {
45
+ await clearPendingHermesFileTransfer(params.gatewayId, params.sessionKey, params.plan.pendingFileTransfer?.sourceRunId);
46
+ return {
47
+ verifiedFileTransferCount: evidence.length,
48
+ output: `已发送 ${evidence.length} 个文件,请查收。`,
49
+ };
50
+ }
51
+ const outcome = params.sourceRunId
52
+ ? collectHermesFileTransferOutcome(messages, params.sourceRunId)
53
+ : undefined;
54
+ if (outcome?.kind === "clarification") {
55
+ if (params.sessionId && params.sourceRunId) {
56
+ await recordPendingHermesFileTransfer({
57
+ gatewayId: params.gatewayId,
58
+ sessionKey: params.sessionKey,
59
+ hermesSessionId: params.sessionId,
60
+ sourceRunId: params.sourceRunId,
61
+ continuation: false,
62
+ });
63
+ }
64
+ // Only the typed content field is displayable. Raw assistant prose is
65
+ // deliberately discarded, and this outcome is never delivery evidence.
66
+ return {
67
+ output: outcome.assistantText ?? HERMES_FILE_TRANSFER_CONTENT_MISSING_MESSAGE,
68
+ };
69
+ }
70
+ if (outcome?.kind === "cancelled") {
71
+ await clearPendingHermesFileTransfer(params.gatewayId, params.sessionKey, params.plan.pendingFileTransfer?.sourceRunId);
72
+ return {
73
+ output: outcome.assistantText ?? HERMES_FILE_TRANSFER_CANCELLED_MESSAGE,
74
+ };
75
+ }
76
+ if (outcome?.kind === "ordinary") {
77
+ await clearPendingHermesFileTransfer(params.gatewayId, params.sessionKey, params.plan.pendingFileTransfer?.sourceRunId);
78
+ // Ordinary chat is shown only through the typed assistantText field. The
79
+ // model's ordinary label is routing metadata, never proof of delivery.
80
+ return {
81
+ output: outcome.assistantText ?? HERMES_FILE_TRANSFER_CONTENT_MISSING_MESSAGE,
82
+ };
83
+ }
84
+ if (outcome?.kind === "attempted") {
85
+ await clearPendingHermesFileTransfer(params.gatewayId, params.sessionKey, params.plan.pendingFileTransfer?.sourceRunId);
86
+ return {
87
+ verifiedFileTransferCount: 0,
88
+ output: HERMES_FILE_TRANSFER_NOT_SENT_MESSAGE,
89
+ };
90
+ }
91
+ if (params.sourceRunId && hasHermesSendFileAttempt(messages, params.sourceRunId)) {
92
+ return {
93
+ verifiedFileTransferCount: 0,
94
+ output: HERMES_FILE_TRANSFER_NOT_SENT_MESSAGE,
95
+ };
96
+ }
97
+ // File-transfer capability is available on every mobile turn, but capability
98
+ // alone does not turn ordinary chat into a file operation. Preserve the Host
99
+ // answer unless this exact turn emitted a typed outcome or actually invoked
100
+ // the first-party terminal command. This keeps failure closed around a real
101
+ // send attempt without replacing unrelated Hermes replies.
102
+ return {};
103
+ }
104
+ function hasHermesSendFileAttempt(messages, sourceRunId) {
105
+ const currentIndex = currentHermesTurnIndex(messages, sourceRunId);
106
+ if (currentIndex < 0 || !messages)
107
+ return false;
108
+ for (const message of messages.slice(currentIndex + 1)) {
109
+ if (normalizeHermesRole(message.role) === "user")
110
+ break;
111
+ if (normalizeHermesRole(message.role) !== "assistant")
112
+ continue;
113
+ const rawToolCalls = typeof message.tool_calls === "string"
114
+ ? parseJsonValue(message.tool_calls)
115
+ : message.tool_calls;
116
+ if (!Array.isArray(rawToolCalls))
117
+ continue;
118
+ for (const rawToolCall of rawToolCalls) {
119
+ const toolCall = rawToolCall && typeof rawToolCall === "object" && !Array.isArray(rawToolCall)
120
+ ? rawToolCall
121
+ : undefined;
122
+ const fn = toolCall?.function && typeof toolCall.function === "object" && !Array.isArray(toolCall.function)
123
+ ? toolCall.function
124
+ : undefined;
125
+ const functionName = normalizeHermesRole(fn?.name);
126
+ if (functionName === "clawconnect_send_file")
127
+ return true;
128
+ if (functionName !== "terminal")
129
+ continue;
130
+ const rawArguments = typeof fn?.arguments === "string"
131
+ ? parseJsonValue(fn.arguments)
132
+ : fn?.arguments;
133
+ const args = rawArguments && typeof rawArguments === "object" && !Array.isArray(rawArguments)
134
+ ? rawArguments
135
+ : undefined;
136
+ if (typeof args?.command === "string" && isClawConnectSendFileCommand(args.command)) {
137
+ return true;
138
+ }
139
+ }
140
+ }
141
+ return false;
142
+ }
143
+ function isClawConnectSendFileCommand(command) {
144
+ return /(?:^|[\s;&|])(?:["'][^"']*clawconnect["']|[^\s;&|]*clawconnect)\s+send-file(?:\s|$)/i.test(command);
145
+ }
146
+ function parseJsonValue(value) {
147
+ try {
148
+ return JSON.parse(value);
149
+ }
150
+ catch {
151
+ return undefined;
152
+ }
153
+ }
154
+ /** Read only first-party terminal outcome records for the exact current turn. */
155
+ export function collectHermesFileTransferOutcome(messages, sourceRunId) {
156
+ const currentIndex = currentHermesTurnIndex(messages, sourceRunId);
157
+ if (currentIndex < 0 || !messages)
158
+ return undefined;
159
+ const outcomes = [];
160
+ for (const message of messages.slice(currentIndex + 1)) {
161
+ if (normalizeHermesRole(message.role) === "user")
162
+ break;
163
+ if (normalizeHermesRole(message.role) !== "tool" || normalizeHermesRole(message.tool_name) !== "terminal")
164
+ continue;
165
+ for (const record of terminalResultRecords(message)) {
166
+ if (record.protocol !== "clawconnect.hermes-file-transfer-outcome.v1"
167
+ || record.sourceRunId !== sourceRunId
168
+ || record.sourceRole !== "assistant"
169
+ || record.status !== "completed"
170
+ || record.exit_code !== 0
171
+ || (record.kind !== "ordinary" && record.kind !== "clarification" && record.kind !== "attempted" && record.kind !== "cancelled"))
172
+ continue;
173
+ const assistantText = typeof record.assistantText === "string" ? record.assistantText.trim() : "";
174
+ if (record.kind !== "attempted" && !assistantText)
175
+ continue;
176
+ outcomes.push({
177
+ protocol: "clawconnect.hermes-file-transfer-outcome.v1",
178
+ kind: record.kind,
179
+ sourceRunId,
180
+ sourceRole: "assistant",
181
+ status: "completed",
182
+ ...(assistantText ? { assistantText } : {}),
183
+ });
184
+ }
185
+ }
186
+ if (outcomes.length !== 1)
187
+ return undefined;
188
+ return outcomes[0];
189
+ }
190
+ export function collectHermesFileTransferEvidence(messages, sourceRunId) {
191
+ if (!messages) {
192
+ return [];
193
+ }
194
+ const currentIndex = currentHermesTurnIndex(messages, sourceRunId);
195
+ if (currentIndex < 0) {
196
+ return [];
197
+ }
198
+ const ids = new Set();
199
+ for (const message of messages.slice(currentIndex + 1)) {
200
+ if (normalizeHermesRole(message.role) === "user") {
201
+ break;
202
+ }
203
+ if (normalizeHermesRole(message.role) !== "tool")
204
+ continue;
205
+ const toolName = normalizeHermesRole(message.tool_name);
206
+ if (toolName === "clawconnect_send_file") {
207
+ const receipt = parseJsonRecord(stringValue(message.content) ?? "");
208
+ if (receipt?.ok === true
209
+ && receipt.sourceRunId === sourceRunId
210
+ && receipt.status === "completed"
211
+ && typeof receipt.fileId === "string"
212
+ && /^file_[a-z0-9]+$/i.test(receipt.fileId)) {
213
+ ids.add(receipt.fileId);
214
+ }
215
+ continue;
216
+ }
217
+ // Only the legacy first-party terminal result is additional evidence. A
218
+ // delegate summary, wrapper output, or assistant claim cannot manufacture
219
+ // an attachment by repeating the command name and a success word.
220
+ if (toolName !== "terminal")
221
+ continue;
222
+ for (const record of terminalResultRecords(message)) {
223
+ if (record.sourceRunId !== sourceRunId
224
+ || record.sourceRole !== "assistant"
225
+ || record.status !== "completed"
226
+ || record.exit_code !== 0
227
+ || typeof record.fileId !== "string"
228
+ || !/^file_[a-z0-9]+$/i.test(record.fileId)) {
229
+ continue;
230
+ }
231
+ ids.add(record.fileId);
232
+ }
233
+ }
234
+ return [...ids];
235
+ }
236
+ function currentHermesTurnIndex(messages, sourceRunId) {
237
+ if (!messages || !sourceRunId.trim())
238
+ return -1;
239
+ let currentIndex = -1;
240
+ for (let index = 0; index < messages.length; index += 1) {
241
+ const message = messages[index];
242
+ if (normalizeHermesRole(message?.role) === "user" && hermesSourceRunIdFromMessage(message) === sourceRunId) {
243
+ currentIndex = index;
244
+ }
245
+ }
246
+ return currentIndex;
247
+ }
248
+ function terminalResultRecords(message) {
249
+ const content = stringValue(message.content);
250
+ if (!content) {
251
+ return [];
252
+ }
253
+ const outer = parseJsonRecord(content);
254
+ if (!outer) {
255
+ return [];
256
+ }
257
+ const records = [outer];
258
+ const output = stringValue(outer.output);
259
+ if (output) {
260
+ for (const fragment of extractJsonObjects(output)) {
261
+ const record = parseJsonRecord(fragment);
262
+ if (record) {
263
+ // Hermes stores the command envelope's exit_code beside the textual
264
+ // send-file JSON payload; the payload itself carries the Relay fields.
265
+ records.push(record.exit_code === undefined && outer.exit_code !== undefined
266
+ ? { ...record, exit_code: outer.exit_code }
267
+ : record);
268
+ }
269
+ }
270
+ }
271
+ return records;
272
+ }
273
+ function parseJsonRecord(value) {
274
+ try {
275
+ const parsed = JSON.parse(value);
276
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
277
+ ? parsed
278
+ : undefined;
279
+ }
280
+ catch {
281
+ return undefined;
282
+ }
283
+ }
284
+ function extractJsonObjects(value) {
285
+ const fragments = [];
286
+ let start = -1;
287
+ let depth = 0;
288
+ let inString = false;
289
+ let escaped = false;
290
+ for (let index = 0; index < value.length; index += 1) {
291
+ const character = value[index];
292
+ if (inString) {
293
+ if (escaped) {
294
+ escaped = false;
295
+ }
296
+ else if (character === "\\") {
297
+ escaped = true;
298
+ }
299
+ else if (character === '"') {
300
+ inString = false;
301
+ }
302
+ continue;
303
+ }
304
+ if (character === '"') {
305
+ inString = true;
306
+ }
307
+ else if (character === "{") {
308
+ if (depth === 0) {
309
+ start = index;
310
+ }
311
+ depth += 1;
312
+ }
313
+ else if (character === "}" && depth > 0) {
314
+ depth -= 1;
315
+ if (depth === 0 && start >= 0) {
316
+ fragments.push(value.slice(start, index + 1));
317
+ start = -1;
318
+ }
319
+ }
320
+ }
321
+ return fragments;
322
+ }
323
+ //# sourceMappingURL=hermes-runtime-chat-file-transfer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-runtime-chat-file-transfer.js","sourceRoot":"","sources":["../../../src/hermes/runtime/hermes-runtime-chat-file-transfer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { HermesUsageSnapshot } from "./hermes-runtime-types.js";
2
+ export type PreparedHermesMessage = {
3
+ apiMessage: string;
4
+ apiInstructions?: string;
5
+ cliMessage: string;
6
+ };
7
+ export declare function prepareRequiredHermesMessage(rawMessage: string, attachments: unknown, sessionKey: string, sourceRunId: string | undefined): Promise<PreparedHermesMessage>;
8
+ export declare function sanitizeHermesUserAttachmentMarkers(message: string): string;
9
+ export declare function cleanupExpiredHermesInbox(inboxDir?: string, nowMs?: number, ttlMs?: number): Promise<void>;
10
+ export declare function buildHermesRuntimeContextHint(snapshot: HermesUsageSnapshot): string | undefined;
@@ -0,0 +1,133 @@
1
+ // Hermes 对话消息准备:附件落盘到 inbox、用户附件标记失活、运行时上下文提示与移动端轮次身份元数据。
2
+ import { randomUUID } from "crypto";
3
+ import { mkdir, readdir, rm, stat, writeFile } from "fs/promises";
4
+ import { join } from "path";
5
+ import { CLAWCONNECT_MOBILE_BRIDGE_HINT, HERMES_INBOX_DIR, } from "./hermes-runtime-process.js";
6
+ import { readCachedHermesStatusSnapshot } from "./hermes-runtime-usage.js";
7
+ import { compactStringArray, sanitizeFileName } from "./hermes-runtime-values.js";
8
+ const HERMES_RUNTIME_CONTEXT_CACHE_MAX_AGE_MS = 5 * 60_000;
9
+ const HERMES_INBOX_TTL_MS = 24 * 60 * 60 * 1000;
10
+ const USER_FILE_MARKER_PREFIX_RE = /\[file attached:/gi;
11
+ export async function prepareRequiredHermesMessage(rawMessage, attachments, sessionKey, sourceRunId) {
12
+ const preparedMessage = await prepareHermesMessage(rawMessage, attachments, sessionKey, sourceRunId);
13
+ if (!preparedMessage.cliMessage.trim()) {
14
+ throw new Error("message_required");
15
+ }
16
+ return preparedMessage;
17
+ }
18
+ const CLAWCONNECT_MOBILE_TURN_INSTRUCTION = "Use [ClawConnect mobile turn] metadata only for ClawConnect file-transfer attribution and message identity. Do not mention it in the answer.";
19
+ function buildClawConnectMobileTurnMetadata(sourceRunId, sessionKey) {
20
+ if (!sourceRunId) {
21
+ return undefined;
22
+ }
23
+ // 这个块是 ClawConnect 和 Hermes history 的稳定身份合同;客户端展示时会剥离它。
24
+ return [
25
+ "[ClawConnect mobile turn]",
26
+ `sourceRunId: ${sourceRunId}`,
27
+ `sessionKey: ${sessionKey}`,
28
+ ].join("\n");
29
+ }
30
+ async function prepareHermesMessage(message, attachments, sessionKey, sourceRunId) {
31
+ const refs = [];
32
+ if (Array.isArray(attachments)) {
33
+ await cleanupExpiredHermesInbox();
34
+ const safeSession = sessionKey.replace(/[^\w.-]/g, "_") || "main";
35
+ for (const attachment of attachments) {
36
+ if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) {
37
+ continue;
38
+ }
39
+ const record = attachment;
40
+ if (typeof record.content !== "string" || record.content.length === 0) {
41
+ continue;
42
+ }
43
+ const fileName = sanitizeFileName(typeof record.fileName === "string" ? record.fileName
44
+ : typeof record.name === "string" ? record.name
45
+ : `attachment-${randomUUID()}`);
46
+ const dir = join(HERMES_INBOX_DIR, safeSession, randomUUID());
47
+ await mkdir(dir, { recursive: true });
48
+ const filePath = join(dir, fileName);
49
+ await writeFile(filePath, Buffer.from(record.content, "base64"));
50
+ const mimeType = typeof record.mimeType === "string" ? record.mimeType : "application/octet-stream";
51
+ refs.push(`[file attached: ${filePath} (${mimeType})]`);
52
+ }
53
+ }
54
+ const userSections = [sanitizeHermesUserAttachmentMarkers(message).trim()];
55
+ if (refs.length > 0) {
56
+ userSections.push(refs.join("\n"));
57
+ }
58
+ const runtimeHint = buildHermesRuntimeContextHint(readCachedHermesStatusSnapshot(HERMES_RUNTIME_CONTEXT_CACHE_MAX_AGE_MS));
59
+ const turnMetadata = buildClawConnectMobileTurnMetadata(sourceRunId, sessionKey);
60
+ const apiMessageSections = [...userSections];
61
+ if (turnMetadata) {
62
+ apiMessageSections.push(turnMetadata);
63
+ }
64
+ const apiInstructionSections = [
65
+ runtimeHint,
66
+ CLAWCONNECT_MOBILE_BRIDGE_HINT,
67
+ turnMetadata ? CLAWCONNECT_MOBILE_TURN_INSTRUCTION : undefined,
68
+ ];
69
+ const cliMessageSections = [
70
+ ...userSections,
71
+ runtimeHint,
72
+ CLAWCONNECT_MOBILE_BRIDGE_HINT,
73
+ turnMetadata
74
+ ? [turnMetadata, CLAWCONNECT_MOBILE_TURN_INSTRUCTION].join("\n")
75
+ : undefined,
76
+ ];
77
+ return {
78
+ apiMessage: apiMessageSections.filter(Boolean).join("\n\n").trim(),
79
+ apiInstructions: apiInstructionSections.filter(Boolean).join("\n\n").trim() || undefined,
80
+ cliMessage: cliMessageSections.filter(Boolean).join("\n\n").trim(),
81
+ };
82
+ }
83
+ export function sanitizeHermesUserAttachmentMarkers(message) {
84
+ // Hermes 的本地文件提示只能由桥接层生成;让用户输入的同形标记失活,
85
+ // 避免把任意 Host 路径伪装成移动端附件交给运行时读取。
86
+ return message.replace(USER_FILE_MARKER_PREFIX_RE, "[file attached:");
87
+ }
88
+ export async function cleanupExpiredHermesInbox(inboxDir = HERMES_INBOX_DIR, nowMs = Date.now(), ttlMs = HERMES_INBOX_TTL_MS) {
89
+ let sessions;
90
+ try {
91
+ sessions = await readdir(inboxDir, { withFileTypes: true });
92
+ }
93
+ catch {
94
+ return;
95
+ }
96
+ await Promise.all(sessions.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map(async (session) => {
97
+ const sessionDir = join(inboxDir, session.name);
98
+ let runs;
99
+ try {
100
+ runs = await readdir(sessionDir, { withFileTypes: true });
101
+ }
102
+ catch {
103
+ return;
104
+ }
105
+ await Promise.all(runs.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map(async (run) => {
106
+ const runDir = join(sessionDir, run.name);
107
+ try {
108
+ const metadata = await stat(runDir);
109
+ if (nowMs - metadata.mtimeMs >= Math.max(0, ttlMs)) {
110
+ await rm(runDir, { recursive: true, force: true });
111
+ }
112
+ }
113
+ catch {
114
+ // 清理失败不能阻断当前聊天;下一轮发送会再次尝试。
115
+ }
116
+ }));
117
+ }));
118
+ }
119
+ export function buildHermesRuntimeContextHint(snapshot) {
120
+ const details = compactStringArray([
121
+ snapshot.currentModel ? `model=${snapshot.currentModel}` : undefined,
122
+ snapshot.provider ? `provider=${snapshot.provider}` : undefined,
123
+ ]);
124
+ if (details.length === 0) {
125
+ return undefined;
126
+ }
127
+ return [
128
+ "[Hermes runtime context]",
129
+ `Current runtime: ${details.join(", ")}.`,
130
+ "If the user asks which model or provider is currently being used, answer from this runtime context.",
131
+ ].join("\n");
132
+ }
133
+ //# sourceMappingURL=hermes-runtime-chat-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-runtime-chat-message.js","sourceRoot":"","sources":["../../../src/hermes/runtime/hermes-runtime-chat-message.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export declare function isHermesChatAbortedError(error: unknown): boolean;
2
+ export declare function recoverHermesCliSessionAfterAbort(params: {
3
+ sessionKey: string;
4
+ resume: string | undefined;
5
+ mappedResume: string | undefined;
6
+ activeHead: number | undefined;
7
+ sourceRunId: string | undefined;
8
+ }): Promise<void>;
9
+ export declare function requireVisibleHermesOutput(output: string): string;
10
+ export declare function recoverEmptyHermesApiOutputFromHistory(params: {
11
+ output: string;
12
+ hermesSessionId?: string;
13
+ sessionKey: string;
14
+ userMessage: string;
15
+ sourceRunId?: string;
16
+ abortSignal?: AbortSignal;
17
+ }): Promise<string | undefined>;
@@ -0,0 +1,90 @@
1
+ // Hermes 对话输出校验与恢复:可见输出约束、Provider 失败识别、API 空输出的历史回补以及 CLI 中止后的会话回卷。
2
+ import { forgetHermesSession } from "../hermes-session-store.js";
3
+ import { sanitizeHermesChatOutput } from "./hermes-runtime-process.js";
4
+ import { detectHermesHistoryCompletion } from "./hermes-runtime-history-completion.js";
5
+ import { rewindHermesSessionAfterActiveHead } from "./hermes-runtime-state-db.js";
6
+ const HERMES_API_EMPTY_OUTPUT_HISTORY_COMPLETION_TIMEOUT_MS = 12_000;
7
+ const HERMES_API_EMPTY_OUTPUT_HISTORY_COMPLETION_POLL_MS = 500;
8
+ const HERMES_EMPTY_RESPONSE_MESSAGE = "Hermes 未返回可见回复,请检查当前模型额度或 Provider 凭据后重试。";
9
+ export function isHermesChatAbortedError(error) {
10
+ return error instanceof Error && error.message === "hermes_chat_aborted";
11
+ }
12
+ export async function recoverHermesCliSessionAfterAbort(params) {
13
+ if (!params.resume) {
14
+ return;
15
+ }
16
+ // Hermes CLI writes the new user row before it reacts to SIGTERM. Rewind
17
+ // only rows beyond the stable pre-run head so completed context remains
18
+ // resumable and the canceled row cannot be answered on the next request.
19
+ if (params.activeHead !== undefined && params.sourceRunId) {
20
+ const recovered = await rewindHermesSessionAfterActiveHead(params.resume, params.activeHead, params.sourceRunId);
21
+ if (recovered) {
22
+ return;
23
+ }
24
+ }
25
+ // Old/unavailable state.db schemas cannot provide a safe row boundary. For
26
+ // an implicit alias, fail closed by dropping only that alias instead of
27
+ // risking a replay of the canceled prompt. Explicit Hermes session ids are
28
+ // never silently deleted or remapped.
29
+ if (params.mappedResume !== params.resume) {
30
+ return;
31
+ }
32
+ try {
33
+ await forgetHermesSession(params.sessionKey, params.mappedResume);
34
+ }
35
+ catch {
36
+ // Abort delivery must not be replaced by a best-effort recovery failure.
37
+ }
38
+ }
39
+ export function requireVisibleHermesOutput(output) {
40
+ const visibleOutput = sanitizeHermesChatOutput(output).trim();
41
+ if (!visibleOutput) {
42
+ // 空完成会让移动端永久留下无内容回复;没有可见文本时必须走显式失败事件。
43
+ throw new Error(HERMES_EMPTY_RESPONSE_MESSAGE);
44
+ }
45
+ if (isHermesProviderFailureOutput(visibleOutput)) {
46
+ // Hermes 某些 Provider 会以 exit 0 返回错误文本;这仍是失败,不能渲染成正常 assistant 回复。
47
+ throw new Error(visibleOutput);
48
+ }
49
+ return visibleOutput;
50
+ }
51
+ function isHermesProviderFailureOutput(output) {
52
+ const firstLine = output.split(/\r?\n/, 1)[0]?.trim() || "";
53
+ return /^(?:[❌✕x]\s*)?API call failed(?: after \d+ retries)?\s*:/i.test(firstLine)
54
+ || /^(?:[❌✕x]\s*)?HTTP\s+(?:401|402|403|429|5\d\d)\b/i.test(firstLine)
55
+ || /^Error code:\s*(?:401|402|403|429|5\d\d)\b/i.test(firstLine);
56
+ }
57
+ export async function recoverEmptyHermesApiOutputFromHistory(params) {
58
+ if (sanitizeHermesChatOutput(params.output).trim()) {
59
+ return undefined;
60
+ }
61
+ if (!params.hermesSessionId?.trim()) {
62
+ return undefined;
63
+ }
64
+ const deadline = Date.now() + HERMES_API_EMPTY_OUTPUT_HISTORY_COMPLETION_TIMEOUT_MS;
65
+ while (Date.now() <= deadline) {
66
+ if (params.abortSignal?.aborted) {
67
+ throw new Error("hermes_chat_aborted");
68
+ }
69
+ const detectedOutput = await detectHermesHistoryCompletion({
70
+ beforeSessions: [],
71
+ resume: params.hermesSessionId,
72
+ sessionKey: params.sessionKey,
73
+ userMessage: params.userMessage,
74
+ sourceRunId: params.sourceRunId,
75
+ });
76
+ if (detectedOutput) {
77
+ return detectedOutput;
78
+ }
79
+ const remainingMs = deadline - Date.now();
80
+ if (remainingMs <= 0) {
81
+ break;
82
+ }
83
+ await sleep(Math.min(HERMES_API_EMPTY_OUTPUT_HISTORY_COMPLETION_POLL_MS, remainingMs));
84
+ }
85
+ return undefined;
86
+ }
87
+ async function sleep(ms) {
88
+ await new Promise((resolve) => setTimeout(resolve, ms));
89
+ }
90
+ //# sourceMappingURL=hermes-runtime-chat-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-runtime-chat-output.js","sourceRoot":"","sources":["../../../src/hermes/runtime/hermes-runtime-chat-output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runSerializedHermesChat<T>(sessionKey: string, operation: () => Promise<T>): Promise<T>;
@@ -0,0 +1,18 @@
1
+ // Hermes 对话按 sessionKey 串行执行;队列状态只保存在本模块,保证同一会话的轮次严格按提交顺序处理。
2
+ const hermesChatQueues = new Map();
3
+ export async function runSerializedHermesChat(sessionKey, operation) {
4
+ const queueKey = sessionKey.trim() || "main";
5
+ const previous = hermesChatQueues.get(queueKey) ?? Promise.resolve();
6
+ const current = previous.catch(() => undefined).then(operation);
7
+ const stored = current.then(() => undefined, () => undefined);
8
+ hermesChatQueues.set(queueKey, stored);
9
+ try {
10
+ return await current;
11
+ }
12
+ finally {
13
+ if (hermesChatQueues.get(queueKey) === stored) {
14
+ hermesChatQueues.delete(queueKey);
15
+ }
16
+ }
17
+ }
18
+ //# sourceMappingURL=hermes-runtime-chat-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-runtime-chat-queue.js","sourceRoot":"","sources":["../../../src/hermes/runtime/hermes-runtime-chat-queue.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAyB,CAAC;AAE1D,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,UAAkB,EAClB,SAA2B;IAE3B,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC;IAC7C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACrE,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC9D,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC;IACvB,CAAC;YAAS,CAAC;QACT,IAAI,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,MAAM,EAAE,CAAC;YAC9C,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;AACH,CAAC"}