






















1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
14import type { AssistantMessage } from "openclaw/plugin-sdk/llm";
25import { describe, expect, it, vi } from "vitest";
36import { HEARTBEAT_RESPONSE_TOOL_NAME } from "../auto-reply/heartbeat-tool-response.js";
47import * as agentEvents from "../infra/agent-events.js";
8+import { resetLogger, setLoggerOverride } from "../logging/logger.js";
9+import { parseLogLine } from "../logging/parse-log-line.js";
510import {
611THINKING_TAG_CASES,
712createSubscribedSessionHarness,
@@ -114,6 +119,45 @@ describe("subscribeEmbeddedAgentSession", () => {
114119});
115120}
116121122+async function captureToolLifecycleLogSubsystems(messageChannel?: string): Promise<string[]> {
123+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-tool-log-attribution-"));
124+const logFile = path.join(tempDir, "openclaw.log");
125+try {
126+setLoggerOverride({
127+level: "debug",
128+consoleLevel: "silent",
129+file: logFile,
130+});
131+const { emit } = createSubscribedHarness({
132+runId: "run-log-attribution",
133+ messageChannel,
134+});
135+136+emitToolRun({
137+ emit,
138+toolName: "exec",
139+toolCallId: "tool-log-attribution",
140+args: { command: "echo ok" },
141+isError: false,
142+result: { ok: true },
143+});
144+145+const logText = await fs.readFile(logFile, "utf8");
146+const subsystems: string[] = [];
147+for (const line of logText.trim().split(/\n+/)) {
148+const parsed = parseLogLine(line);
149+if (parsed?.message.includes("embedded run tool")) {
150+subsystems.push(parsed.subsystem ?? "");
151+}
152+}
153+return subsystems;
154+} finally {
155+resetLogger();
156+setLoggerOverride(null);
157+await fs.rm(tempDir, { recursive: true, force: true });
158+}
159+}
160+117161function findBlockReplyPayload(
118162onBlockReply: { mock: { calls: unknown[][] } },
119163text: string,
@@ -195,6 +239,21 @@ describe("subscribeEmbeddedAgentSession", () => {
195239});
196240});
197241242+it.each([
243+["telegram", "gateway/channels/telegram"],
244+[undefined, "agent/embedded"],
245+["openclaw", "agent/embedded"],
246+["not a channel", "agent/embedded"],
247+] as const)(
248+"attributes tool lifecycle logs for channel=%s",
249+async (messageChannel, subsystem) => {
250+await expect(captureToolLifecycleLogSubsystems(messageChannel)).resolves.toEqual([
251+subsystem,
252+subsystem,
253+]);
254+},
255+);
256+198257it("does not double-count usage when done and message_end carry the same snapshot", () => {
199258const { emit, subscription } = createSubscribedSessionHarness({ runId: "run" });
200259const usage = {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。