clawconnect-agent 0.2.13 → 0.2.15
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 +2 -2
- package/README.zh-CN.md +1 -1
- package/dist/commands/install.js +5 -3
- package/dist/commands/install.js.map +1 -1
- 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/config/profile-log-rotation.d.ts +1 -1
- package/dist/config/profile-log-rotation.js +1 -1
- package/dist/config/profile-log-rotation.js.map +1 -1
- 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/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/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-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 +57 -0
- package/dist/hermes/runtime/hermes-runtime-chat-file-transfer.js +321 -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 +41 -910
- package/dist/hermes/runtime/hermes-runtime-chat.js.map +1 -1
- package/dist/hermes/runtime/hermes-runtime-history-turn-output.d.ts +30 -0
- package/dist/hermes/runtime/hermes-runtime-history-turn-output.js +61 -0
- package/dist/hermes/runtime/hermes-runtime-history-turn-output.js.map +1 -0
- package/dist/hermes/runtime/hermes-runtime-history.js +24 -3
- package/dist/hermes/runtime/hermes-runtime-history.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-state-db.js +28 -0
- package/dist/hermes/runtime/hermes-runtime-state-db.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/openclaw/handlers/local-handlers.js +1 -1
- package/dist/openclaw/handlers/local-handlers.js.map +1 -1
- package/dist/openclaw/relay/chat-history.d.ts +5 -1
- package/dist/openclaw/relay/chat-history.js +4 -7
- package/dist/openclaw/relay/chat-history.js.map +1 -1
- package/dist/openclaw/relay/openclaw-source-commit-watcher.d.ts +7 -4
- package/dist/openclaw/relay/openclaw-source-commit-watcher.js +2 -0
- package/dist/openclaw/relay/openclaw-source-commit-watcher.js.map +1 -1
- package/dist/openclaw/relay/openclaw-source-cursor-connections.d.ts +2 -0
- package/dist/openclaw/relay/openclaw-source-cursor-connections.js +12 -0
- package/dist/openclaw/relay/openclaw-source-cursor-connections.js.map +1 -1
- 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 +44 -22
- package/dist/openclaw/relay-manager.js.map +1 -1
- package/dist/platform/service-manager-windows.d.ts +39 -29
- package/dist/platform/service-manager-windows.js +256 -448
- package/dist/platform/service-manager-windows.js.map +1 -1
- package/dist/platform/windows-process-control.d.ts +16 -0
- package/dist/platform/windows-process-control.js +91 -0
- package/dist/platform/windows-process-control.js.map +1 -0
- package/dist/platform/windows-service-host-source.d.ts +23 -0
- package/dist/platform/windows-service-host-source.js +357 -0
- package/dist/platform/windows-service-host-source.js.map +1 -0
- package/dist/platform/windows-service-legacy-startup.d.ts +27 -0
- package/dist/platform/windows-service-legacy-startup.js +101 -0
- package/dist/platform/windows-service-legacy-startup.js.map +1 -0
- package/dist/platform/windows-service-paths.d.ts +15 -0
- package/dist/platform/windows-service-paths.js +30 -0
- package/dist/platform/windows-service-paths.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync, watch } from "node:fs";
|
|
3
|
+
import { basename, dirname } from "node:path";
|
|
4
|
+
/** jobs.json 不存在时的内容摘要哨兵;与任何真实文件内容的 sha256 都不可能相同。 */
|
|
5
|
+
export const HERMES_CRON_JOBS_MISSING_DIGEST = "missing";
|
|
6
|
+
/**
|
|
7
|
+
* 监听 Hermes cron 任务库的内容变化。
|
|
8
|
+
*
|
|
9
|
+
* 正确性只由“文件内容 sha256 摘要是否与基线不同”决定:fs.watch 通知仅是唤醒信号,
|
|
10
|
+
* 重复、合并或额外的通知(例如同目录 ticker_heartbeat、executions.db 的写入)都只会
|
|
11
|
+
* 触发一次重新读取,摘要相同即不发事件,因此同一份内容永远不会重复通知。
|
|
12
|
+
*
|
|
13
|
+
* 监听的是 jobs.json 所在目录而非文件本身:Hermes 以“写临时文件再 rename”的方式原子替换
|
|
14
|
+
* jobs.json,文件级 watch 会停留在被替换掉的旧 inode 上,目录级 watch 才能持续看到新文件。
|
|
15
|
+
* 同时监听上一级目录(Hermes home),当 cron 目录稍后被创建或被删除重建时重新挂载目录监听。
|
|
16
|
+
* 若 Hermes home 本身尚不存在,则只记录日志;下次 Relay 连接会重新创建本监听器。
|
|
17
|
+
*/
|
|
18
|
+
export function watchHermesCronJobs(options) {
|
|
19
|
+
const cronDirectory = dirname(options.jobsFile);
|
|
20
|
+
const cronDirectoryName = basename(cronDirectory);
|
|
21
|
+
const hermesHomeDirectory = dirname(cronDirectory);
|
|
22
|
+
const logWarning = options.logWarning ?? ((message) => console.warn(`[hermes-cron-watch] ${message}`));
|
|
23
|
+
let baselineDigest = options.baselineDigest;
|
|
24
|
+
let baselineEstablished = false;
|
|
25
|
+
let closed = false;
|
|
26
|
+
let pendingCheck;
|
|
27
|
+
let cronDirectoryWatcher;
|
|
28
|
+
let hermesHomeWatcher;
|
|
29
|
+
const checkNow = () => {
|
|
30
|
+
if (closed)
|
|
31
|
+
return;
|
|
32
|
+
const digest = readJobsDigest(options.jobsFile, logWarning);
|
|
33
|
+
if (digest === undefined)
|
|
34
|
+
return;
|
|
35
|
+
const hadBaseline = baselineEstablished || baselineDigest !== undefined;
|
|
36
|
+
const changed = hadBaseline && digest !== baselineDigest;
|
|
37
|
+
baselineDigest = digest;
|
|
38
|
+
baselineEstablished = true;
|
|
39
|
+
if (!changed)
|
|
40
|
+
return;
|
|
41
|
+
try {
|
|
42
|
+
options.onChange();
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
logWarning(`change listener failed: ${errorMessage(error)}`);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
// 同一事件循环轮次内的多次通知只合并为一次读取;这只是减少重复 I/O,
|
|
49
|
+
// 是否发事件仍完全由摘要比较决定。
|
|
50
|
+
const scheduleCheck = () => {
|
|
51
|
+
if (closed || pendingCheck)
|
|
52
|
+
return;
|
|
53
|
+
pendingCheck = setImmediate(() => {
|
|
54
|
+
pendingCheck = undefined;
|
|
55
|
+
checkNow();
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
const closeCronDirectoryWatcher = () => {
|
|
59
|
+
cronDirectoryWatcher?.close();
|
|
60
|
+
cronDirectoryWatcher = undefined;
|
|
61
|
+
};
|
|
62
|
+
const armCronDirectoryWatcher = () => {
|
|
63
|
+
closeCronDirectoryWatcher();
|
|
64
|
+
try {
|
|
65
|
+
const directoryWatcher = watch(cronDirectory, () => scheduleCheck());
|
|
66
|
+
directoryWatcher.on("error", (error) => {
|
|
67
|
+
logWarning(`cron directory watch failed: ${errorMessage(error)}`);
|
|
68
|
+
if (cronDirectoryWatcher === directoryWatcher)
|
|
69
|
+
closeCronDirectoryWatcher();
|
|
70
|
+
scheduleCheck();
|
|
71
|
+
});
|
|
72
|
+
cronDirectoryWatcher = directoryWatcher;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (!isMissingPathError(error))
|
|
76
|
+
logWarning(`cannot watch cron directory: ${errorMessage(error)}`);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
try {
|
|
80
|
+
hermesHomeWatcher = watch(hermesHomeDirectory, (_eventType, changedName) => {
|
|
81
|
+
// cron 目录被创建、删除或替换时重新挂载目录监听,并重新比较内容。
|
|
82
|
+
if (changedName !== null && String(changedName) !== cronDirectoryName)
|
|
83
|
+
return;
|
|
84
|
+
if (closed)
|
|
85
|
+
return;
|
|
86
|
+
armCronDirectoryWatcher();
|
|
87
|
+
scheduleCheck();
|
|
88
|
+
});
|
|
89
|
+
hermesHomeWatcher.on("error", (error) => {
|
|
90
|
+
logWarning(`Hermes home watch failed: ${errorMessage(error)}`);
|
|
91
|
+
hermesHomeWatcher?.close();
|
|
92
|
+
hermesHomeWatcher = undefined;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logWarning(`cannot watch Hermes home; cron changes resume on next relay connect: ${errorMessage(error)}`);
|
|
97
|
+
}
|
|
98
|
+
armCronDirectoryWatcher();
|
|
99
|
+
checkNow();
|
|
100
|
+
return {
|
|
101
|
+
checkNow,
|
|
102
|
+
currentDigest: () => baselineDigest,
|
|
103
|
+
close() {
|
|
104
|
+
if (closed)
|
|
105
|
+
return;
|
|
106
|
+
closed = true;
|
|
107
|
+
if (pendingCheck)
|
|
108
|
+
clearImmediate(pendingCheck);
|
|
109
|
+
pendingCheck = undefined;
|
|
110
|
+
closeCronDirectoryWatcher();
|
|
111
|
+
hermesHomeWatcher?.close();
|
|
112
|
+
hermesHomeWatcher = undefined;
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** 读取失败(非“不存在”)时返回 undefined,保留原基线,不把未知状态当成变化。 */
|
|
117
|
+
function readJobsDigest(jobsFile, logWarning) {
|
|
118
|
+
try {
|
|
119
|
+
return createHash("sha256").update(readFileSync(jobsFile)).digest("hex");
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (isMissingPathError(error))
|
|
123
|
+
return HERMES_CRON_JOBS_MISSING_DIGEST;
|
|
124
|
+
logWarning(`cannot read cron jobs file: ${errorMessage(error)}`);
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function isMissingPathError(error) {
|
|
129
|
+
const code = error?.code;
|
|
130
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
131
|
+
}
|
|
132
|
+
function errorMessage(error) {
|
|
133
|
+
return error instanceof Error ? error.message : String(error);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=hermes-cron-change-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermes-cron-change-watcher.js","sourceRoot":"","sources":["../../../src/hermes/runtime/hermes-cron-change-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LocalCommandContext } from "../../core/command-types.js";
|
|
2
|
+
declare function runHermesChatOnce(params: {
|
|
3
|
+
message: string;
|
|
4
|
+
sessionKey: string;
|
|
5
|
+
resume?: string;
|
|
6
|
+
context: LocalCommandContext;
|
|
7
|
+
preloadedSkillArgs?: string[];
|
|
8
|
+
reasoningEffort?: string;
|
|
9
|
+
historyCompletion?: () => Promise<string | undefined>;
|
|
10
|
+
}): Promise<string>;
|
|
11
|
+
export declare function runHermesChatOnceWithMobileFileRoute(params: Parameters<typeof runHermesChatOnce>[0] & {
|
|
12
|
+
enableFileTransferRoute: boolean;
|
|
13
|
+
gatewayId: string;
|
|
14
|
+
sourceRunId?: string;
|
|
15
|
+
}): Promise<string>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// Hermes CLI 对话执行:构建 chat 参数、派生子进程、过滤展示输出并把工具/stderr 事件发布给 Relay。
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { buildToolInvocationUpdatedEvent, } from "../../core/relay/timeline-event-builder.js";
|
|
4
|
+
import { CHAT_TIMEOUT_MS, SUBPROCESS_ENV, hermesInvocation, isHermesCommandDeniedTimeoutLine, runHermes, stripAnsi, stripHermesSessionResumeNotices, } from "./hermes-runtime-process.js";
|
|
5
|
+
import { buildHermesCliReasoningArgs } from "./hermes-runtime-reasoning.js";
|
|
6
|
+
import { createHermesToolLogWatcher, hermesToolState, } from "./hermes-runtime-tool-log-watcher.js";
|
|
7
|
+
import { clearHermesMobileFileRoute, registerHermesMobileFileRoute, } from "./hermes-mobile-file-route-store.js";
|
|
8
|
+
const HERMES_HISTORY_COMPLETION_GRACE_MS = 2_000;
|
|
9
|
+
const HERMES_HISTORY_COMPLETION_POLL_MS = 1_000;
|
|
10
|
+
const HERMES_COMMAND_DENIED_TIMEOUT_MESSAGE = "Timeout – denying command";
|
|
11
|
+
async function runHermesChatOnce(params) {
|
|
12
|
+
const args = [
|
|
13
|
+
"chat",
|
|
14
|
+
// Hermes 会按 display.interface 自动切到 TUI;移动端桥接必须强制经典 CLI,
|
|
15
|
+
// 再由 --quiet 只输出最终回答。Tools 过程仍由 agent.log watcher 独立发布。
|
|
16
|
+
"--cli",
|
|
17
|
+
"--query",
|
|
18
|
+
params.message,
|
|
19
|
+
"--quiet",
|
|
20
|
+
"--source",
|
|
21
|
+
"pocketclaw",
|
|
22
|
+
...(params.preloadedSkillArgs ?? []),
|
|
23
|
+
...buildHermesCliReasoningArgs(params.reasoningEffort),
|
|
24
|
+
"--yolo",
|
|
25
|
+
];
|
|
26
|
+
if (params.resume) {
|
|
27
|
+
args.push("--resume", params.resume);
|
|
28
|
+
}
|
|
29
|
+
const runId = params.context.requestId ?? `hermes-${Date.now()}`;
|
|
30
|
+
const env = hermesChatSubprocessEnv(runId, params.sessionKey);
|
|
31
|
+
return params.context.publishEvent
|
|
32
|
+
? await runHermesChatStreaming(args, params.sessionKey, params.context, runId, env, params.historyCompletion)
|
|
33
|
+
: runHermes(args, CHAT_TIMEOUT_MS, env);
|
|
34
|
+
}
|
|
35
|
+
export async function runHermesChatOnceWithMobileFileRoute(params) {
|
|
36
|
+
const hermesSessionId = params.resume?.trim();
|
|
37
|
+
const sourceRunId = params.sourceRunId?.trim();
|
|
38
|
+
if (!params.enableFileTransferRoute || !hermesSessionId || !sourceRunId) {
|
|
39
|
+
return await runHermesChatOnce(params);
|
|
40
|
+
}
|
|
41
|
+
await registerHermesMobileFileRoute({
|
|
42
|
+
hermesSessionId,
|
|
43
|
+
gatewayId: params.gatewayId,
|
|
44
|
+
sessionKey: params.sessionKey,
|
|
45
|
+
sourceRunId,
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
return await runHermesChatOnce(params);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
await clearHermesMobileFileRoute(hermesSessionId, sourceRunId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function runHermesChatStreaming(args, sessionKey, context, runId, env, historyCompletion) {
|
|
55
|
+
const invocation = hermesInvocation(args);
|
|
56
|
+
const child = spawn(invocation.command, invocation.args, {
|
|
57
|
+
env,
|
|
58
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
59
|
+
windowsHide: true,
|
|
60
|
+
});
|
|
61
|
+
let output = "";
|
|
62
|
+
let stderr = "";
|
|
63
|
+
let seq = 0;
|
|
64
|
+
let stdoutLineBuffer = "";
|
|
65
|
+
let inSecurityReview = false;
|
|
66
|
+
let commandDeniedTimeout = false;
|
|
67
|
+
let commandDeniedTimeoutKillTimer;
|
|
68
|
+
const toolCallIdsByName = new Map();
|
|
69
|
+
let toolCallCounter = 0;
|
|
70
|
+
const requestCommandDeniedTimeoutFailure = () => {
|
|
71
|
+
if (commandDeniedTimeout) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
commandDeniedTimeout = true;
|
|
75
|
+
child.kill("SIGTERM");
|
|
76
|
+
commandDeniedTimeoutKillTimer = setTimeout(() => child.kill("SIGKILL"), 1000);
|
|
77
|
+
commandDeniedTimeoutKillTimer.unref?.();
|
|
78
|
+
};
|
|
79
|
+
const publishToolLogEvent = (event) => {
|
|
80
|
+
let toolCallId = toolCallIdsByName.get(event.toolName);
|
|
81
|
+
if (!toolCallId) {
|
|
82
|
+
toolCallCounter += 1;
|
|
83
|
+
toolCallId = `${runId}:hermes-tool-${toolCallCounter}`;
|
|
84
|
+
toolCallIdsByName.set(event.toolName, toolCallId);
|
|
85
|
+
}
|
|
86
|
+
if (event.phase === "completed" || event.phase === "failed") {
|
|
87
|
+
toolCallIdsByName.delete(event.toolName);
|
|
88
|
+
}
|
|
89
|
+
context.publishEvent?.({
|
|
90
|
+
type: "event",
|
|
91
|
+
event: "chat",
|
|
92
|
+
payload: {
|
|
93
|
+
runId,
|
|
94
|
+
sessionKey,
|
|
95
|
+
stream: "tool",
|
|
96
|
+
state: event.phase,
|
|
97
|
+
phase: event.phase,
|
|
98
|
+
role: "tool",
|
|
99
|
+
seq: seq += 1,
|
|
100
|
+
ts: Date.now(),
|
|
101
|
+
data: {
|
|
102
|
+
phase: event.phase,
|
|
103
|
+
tool_call_id: toolCallId,
|
|
104
|
+
tool_name: event.toolName,
|
|
105
|
+
text: event.text,
|
|
106
|
+
is_error: event.isError === true,
|
|
107
|
+
},
|
|
108
|
+
timelineEvents: [
|
|
109
|
+
buildToolInvocationUpdatedEvent({
|
|
110
|
+
gatewayId: context.gatewayId ?? "clawconnect",
|
|
111
|
+
sessionKey,
|
|
112
|
+
turnId: runId,
|
|
113
|
+
runId,
|
|
114
|
+
toolInvocationId: toolCallId,
|
|
115
|
+
toolState: hermesToolState(event),
|
|
116
|
+
seq: seq,
|
|
117
|
+
turnSeq: seq,
|
|
118
|
+
content: [{
|
|
119
|
+
type: event.phase === "completed" || event.phase === "failed" ? "tool_result" : "tool_call",
|
|
120
|
+
toolName: event.toolName,
|
|
121
|
+
text: event.text,
|
|
122
|
+
isError: event.isError === true,
|
|
123
|
+
}],
|
|
124
|
+
}),
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
const toolLogWatcher = createHermesToolLogWatcher(publishToolLogEvent);
|
|
130
|
+
const filterChatLine = (line) => {
|
|
131
|
+
const clean = stripAnsi(line).trim();
|
|
132
|
+
if (isHermesCommandDeniedTimeoutLine(clean)) {
|
|
133
|
+
requestCommandDeniedTimeoutFailure();
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
if (/DANGEROUS COMMAND:\s*Security scan/i.test(clean)) {
|
|
137
|
+
inSecurityReview = true;
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
if (inSecurityReview) {
|
|
141
|
+
if (/Choice\s*\[[^\]]+\]:/i.test(clean) || /(?:^|\s)[✕x]\s*Denied\b/i.test(clean) || /\bDenied\b/i.test(clean)) {
|
|
142
|
+
inSecurityReview = false;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return line;
|
|
147
|
+
};
|
|
148
|
+
const appendStdout = (text) => {
|
|
149
|
+
// Hermes CLI stdout is a presentation stream, not a semantic assistant stream.
|
|
150
|
+
// Some Windows builds emit transient TUI frames (Reasoning boxes, spinners, etc.)
|
|
151
|
+
// even with --cli --quiet. Buffer it for terminal completion, but never expose it
|
|
152
|
+
// as message.part.delta. The API path has typed assistant.delta events and remains
|
|
153
|
+
// the only Hermes path allowed to stream assistant text.
|
|
154
|
+
output += text;
|
|
155
|
+
};
|
|
156
|
+
const flushStdoutLineBuffer = () => {
|
|
157
|
+
if (!stdoutLineBuffer) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const clean = stripHermesSessionResumeNotices(filterChatLine(stdoutLineBuffer) ?? "");
|
|
161
|
+
stdoutLineBuffer = "";
|
|
162
|
+
if (!clean.trim()) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
appendStdout(clean);
|
|
166
|
+
};
|
|
167
|
+
const publishText = (text) => {
|
|
168
|
+
stdoutLineBuffer += text;
|
|
169
|
+
const lines = stdoutLineBuffer.split(/\r?\n/);
|
|
170
|
+
stdoutLineBuffer = lines.pop() ?? "";
|
|
171
|
+
const clean = stripHermesSessionResumeNotices(lines
|
|
172
|
+
.map(filterChatLine)
|
|
173
|
+
.filter((line) => line !== null)
|
|
174
|
+
.join("\n"));
|
|
175
|
+
if (!clean.trim()) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const chunk = `${clean}\n`;
|
|
179
|
+
appendStdout(chunk);
|
|
180
|
+
};
|
|
181
|
+
const publishStderr = (text) => {
|
|
182
|
+
const clean = stripAnsi(text).trimEnd();
|
|
183
|
+
if (!clean) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
stderr += `${clean}\n`;
|
|
187
|
+
context.publishEvent?.({
|
|
188
|
+
type: "event",
|
|
189
|
+
event: "maintenance_log",
|
|
190
|
+
payload: {
|
|
191
|
+
gatewayId: context.gatewayId,
|
|
192
|
+
requestId: context.requestId,
|
|
193
|
+
runId,
|
|
194
|
+
stream: "stderr",
|
|
195
|
+
seq: seq += 1,
|
|
196
|
+
ts: Date.now(),
|
|
197
|
+
text: clean,
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
const publishTypingMarker = () => {
|
|
202
|
+
// The local mobile client already owns the pending placeholder. Do not
|
|
203
|
+
// forward protocol-only typing markers as empty assistant chat events.
|
|
204
|
+
};
|
|
205
|
+
child.stdout?.on("data", (chunk) => publishText(chunk.toString()));
|
|
206
|
+
child.stderr?.on("data", (chunk) => publishStderr(chunk.toString()));
|
|
207
|
+
toolLogWatcher.start();
|
|
208
|
+
publishTypingMarker();
|
|
209
|
+
return await new Promise((resolveOutput, rejectOutput) => {
|
|
210
|
+
const abortSignal = context.abortSignal;
|
|
211
|
+
let abortRequested = abortSignal?.aborted === true;
|
|
212
|
+
let settled = false;
|
|
213
|
+
let historyCompletionTimer;
|
|
214
|
+
let historyCompletionInFlight = false;
|
|
215
|
+
const typingTimer = setInterval(publishTypingMarker, 5000);
|
|
216
|
+
typingTimer.unref?.();
|
|
217
|
+
const cleanup = () => {
|
|
218
|
+
clearInterval(typingTimer);
|
|
219
|
+
if (historyCompletionTimer) {
|
|
220
|
+
clearInterval(historyCompletionTimer);
|
|
221
|
+
historyCompletionTimer = undefined;
|
|
222
|
+
}
|
|
223
|
+
abortSignal?.removeEventListener("abort", abortChat);
|
|
224
|
+
};
|
|
225
|
+
const finishResolve = (value) => {
|
|
226
|
+
if (settled) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
settled = true;
|
|
230
|
+
cleanup();
|
|
231
|
+
clearTimeout(timeout);
|
|
232
|
+
toolLogWatcher.stop();
|
|
233
|
+
resolveOutput(value);
|
|
234
|
+
};
|
|
235
|
+
const finishReject = (error) => {
|
|
236
|
+
if (settled) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
settled = true;
|
|
240
|
+
cleanup();
|
|
241
|
+
clearTimeout(timeout);
|
|
242
|
+
toolLogWatcher.stop();
|
|
243
|
+
rejectOutput(error);
|
|
244
|
+
};
|
|
245
|
+
const timeout = setTimeout(() => {
|
|
246
|
+
child.kill("SIGTERM");
|
|
247
|
+
if (output.trim()) {
|
|
248
|
+
finishResolve(output);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
finishReject(new Error("hermes_chat_timeout"));
|
|
252
|
+
}
|
|
253
|
+
}, CHAT_TIMEOUT_MS);
|
|
254
|
+
timeout.unref?.();
|
|
255
|
+
const abortChat = () => {
|
|
256
|
+
abortRequested = true;
|
|
257
|
+
stdoutLineBuffer = "";
|
|
258
|
+
child.kill("SIGTERM");
|
|
259
|
+
};
|
|
260
|
+
const checkHistoryCompletion = () => {
|
|
261
|
+
if (!historyCompletion || settled || abortRequested || historyCompletionInFlight) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
historyCompletionInFlight = true;
|
|
265
|
+
historyCompletion()
|
|
266
|
+
.then((detectedOutput) => {
|
|
267
|
+
if (!detectedOutput || settled || abortRequested) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
output = detectedOutput;
|
|
271
|
+
stdoutLineBuffer = "";
|
|
272
|
+
child.kill("SIGTERM");
|
|
273
|
+
setTimeout(() => {
|
|
274
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
275
|
+
child.kill("SIGKILL");
|
|
276
|
+
}
|
|
277
|
+
}, 1000).unref?.();
|
|
278
|
+
finishResolve(detectedOutput);
|
|
279
|
+
})
|
|
280
|
+
.catch(() => {
|
|
281
|
+
// History completion is a best-effort escape hatch for Hermes processes
|
|
282
|
+
// that keep running after the assistant turn is already persisted.
|
|
283
|
+
})
|
|
284
|
+
.finally(() => {
|
|
285
|
+
historyCompletionInFlight = false;
|
|
286
|
+
});
|
|
287
|
+
};
|
|
288
|
+
if (abortRequested) {
|
|
289
|
+
abortChat();
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
abortSignal?.addEventListener("abort", abortChat, { once: true });
|
|
293
|
+
}
|
|
294
|
+
if (historyCompletion) {
|
|
295
|
+
historyCompletionTimer = setInterval(checkHistoryCompletion, HERMES_HISTORY_COMPLETION_POLL_MS);
|
|
296
|
+
setTimeout(checkHistoryCompletion, HERMES_HISTORY_COMPLETION_GRACE_MS);
|
|
297
|
+
}
|
|
298
|
+
child.once("error", (error) => {
|
|
299
|
+
if (settled) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (commandDeniedTimeoutKillTimer) {
|
|
303
|
+
clearTimeout(commandDeniedTimeoutKillTimer);
|
|
304
|
+
commandDeniedTimeoutKillTimer = undefined;
|
|
305
|
+
}
|
|
306
|
+
finishReject(error);
|
|
307
|
+
});
|
|
308
|
+
child.once("close", (code, signal) => {
|
|
309
|
+
void (async () => {
|
|
310
|
+
if (settled) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (commandDeniedTimeoutKillTimer) {
|
|
314
|
+
clearTimeout(commandDeniedTimeoutKillTimer);
|
|
315
|
+
commandDeniedTimeoutKillTimer = undefined;
|
|
316
|
+
}
|
|
317
|
+
if (abortRequested) {
|
|
318
|
+
finishReject(new Error("hermes_chat_aborted"));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
flushStdoutLineBuffer();
|
|
322
|
+
if (commandDeniedTimeout) {
|
|
323
|
+
finishReject(new Error(HERMES_COMMAND_DENIED_TIMEOUT_MESSAGE));
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (code && code !== 0) {
|
|
327
|
+
const reason = stderr.trim() || output.trim() || `hermes chat exited with code ${code}`;
|
|
328
|
+
finishReject(new Error(signal ? `${reason} (${signal})` : reason));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (historyCompletion) {
|
|
332
|
+
try {
|
|
333
|
+
// Prefer the persisted assistant message over CLI presentation stdout.
|
|
334
|
+
// Hermes writes the semantic answer before a normal process exit, while
|
|
335
|
+
// stdout may still contain transient TUI frames on Windows.
|
|
336
|
+
const persistedOutput = await historyCompletion();
|
|
337
|
+
if (persistedOutput) {
|
|
338
|
+
output = persistedOutput;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// Older/test Hermes installations may not expose readable history.
|
|
343
|
+
// In that case --cli --quiet stdout remains the compatibility fallback.
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
finishResolve(output);
|
|
347
|
+
})().catch((error) => finishReject(error instanceof Error ? error : new Error(String(error))));
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
function hermesChatSubprocessEnv(runId, sessionKey) {
|
|
352
|
+
return {
|
|
353
|
+
...SUBPROCESS_ENV,
|
|
354
|
+
CLAWCONNECT_SOURCE_RUN_ID: runId,
|
|
355
|
+
CLAWCONNECT_SESSION_KEY: sessionKey,
|
|
356
|
+
CLAWCONNECT_CHAT_SESSION_KEY: sessionKey,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
//# sourceMappingURL=hermes-runtime-chat-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermes-runtime-chat-cli.js","sourceRoot":"","sources":["../../../src/hermes/runtime/hermes-runtime-chat-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type PendingHermesFileTransfer } from "./hermes-file-transfer-state.js";
|
|
2
|
+
import type { HermesFileTransferOutcome } from "../../commands/hermes-file-transfer-outcome.js";
|
|
3
|
+
export type HermesChatPreparationPlan = {
|
|
4
|
+
preloadFileTransferSkill: boolean;
|
|
5
|
+
fileTransferMode: "continuation" | undefined;
|
|
6
|
+
pendingFileTransfer?: PendingHermesFileTransfer;
|
|
7
|
+
};
|
|
8
|
+
export declare function planHermesChatPreparation(params: {
|
|
9
|
+
message: string;
|
|
10
|
+
gatewayId: string;
|
|
11
|
+
sessionKey: string;
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
sourceRunId?: string;
|
|
14
|
+
fileTransferCapability?: "cli";
|
|
15
|
+
}): Promise<HermesChatPreparationPlan>;
|
|
16
|
+
/**
|
|
17
|
+
* 一个移动端轮次最终应该展示的文本,以及它是怎么来的。
|
|
18
|
+
*
|
|
19
|
+
* 这是实时路径(`verifyHermesFileTransferIfRequired`)和历史投影(`runHermesChatHistory`)
|
|
20
|
+
* 共用的唯一规则:同一份 state.db 行只能算出同一个答案,两处不能各算一遍。
|
|
21
|
+
* 模型在 outcome 工具之后补的收尾文字("已发送 ✅"之类)不是可信的最终回答,
|
|
22
|
+
* 只有 typed outcome 的 assistantText、第一方 send-file 回执和宿主生成的失败提示才算。
|
|
23
|
+
*/
|
|
24
|
+
export type HermesTurnOutput = {
|
|
25
|
+
kind: "none";
|
|
26
|
+
} | {
|
|
27
|
+
kind: "file-evidence";
|
|
28
|
+
text: string;
|
|
29
|
+
fileCount: number;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "clarification";
|
|
32
|
+
text: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: "cancelled";
|
|
35
|
+
text: string;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "ordinary";
|
|
38
|
+
text: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "attempted-unverified";
|
|
41
|
+
text: string;
|
|
42
|
+
};
|
|
43
|
+
export declare function resolveHermesTurnOutput(messages: Array<Record<string, unknown>> | undefined, sourceRunId: string): HermesTurnOutput;
|
|
44
|
+
export declare function verifyHermesFileTransferIfRequired(params: {
|
|
45
|
+
plan: HermesChatPreparationPlan;
|
|
46
|
+
gatewayId: string;
|
|
47
|
+
sessionKey: string;
|
|
48
|
+
sessionId?: string;
|
|
49
|
+
sourceRunId?: string;
|
|
50
|
+
output?: string;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
verifiedFileTransferCount?: number;
|
|
53
|
+
output?: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Read only first-party terminal outcome records for the exact current turn. */
|
|
56
|
+
export declare function collectHermesFileTransferOutcome(messages: Array<Record<string, unknown>> | undefined, sourceRunId: string): HermesFileTransferOutcome | undefined;
|
|
57
|
+
export declare function collectHermesFileTransferEvidence(messages: Array<Record<string, unknown>> | undefined, sourceRunId: string): string[];
|