






















@@ -28,6 +28,17 @@ vi.mock("./server-chat.load-gateway-session-row.runtime.js", () => ({
2828loadGatewaySessionRow: vi.fn(),
2929}));
303031+vi.mock("./session-utils.js", () => ({
32+loadSessionEntry: vi.fn(() => ({
33+cfg: {},
34+storePath: "/tmp/sessions.json",
35+store: {},
36+entry: undefined,
37+canonicalKey: "session-1",
38+legacyKey: undefined,
39+})),
40+}));
41+3142import { getRuntimeConfig } from "../config/io.js";
3243import { resolveHeartbeatVisibility } from "../infra/heartbeat-visibility.js";
3344import {
@@ -37,6 +48,7 @@ import {
3748createToolEventRecipientRegistry,
3849} from "./server-chat.js";
3950import { loadGatewaySessionRow } from "./server-chat.load-gateway-session-row.runtime.js";
51+import { loadSessionEntry } from "./session-utils.js";
40524153describe("agent event handler", () => {
4254beforeEach(() => {
@@ -46,6 +58,14 @@ describe("agent event handler", () => {
4658showAlerts: true,
4759useIndicator: true,
4860});
61+vi.mocked(loadSessionEntry).mockReset().mockReturnValue({
62+cfg: {},
63+storePath: "/tmp/sessions.json",
64+store: {},
65+entry: undefined,
66+canonicalKey: "session-1",
67+legacyKey: undefined,
68+});
4969vi.mocked(loadGatewaySessionRow).mockReset().mockReturnValue(null);
5070persistGatewaySessionLifecycleEventMock.mockReset().mockResolvedValue(undefined);
5171resetAgentRunContextForTest();
@@ -760,6 +780,79 @@ describe("agent event handler", () => {
760780resetAgentRunContextForTest();
761781});
762782783+it("uses newer session verbose state for in-flight tool events", () => {
784+const { nodeSendToSession, handler } = createHarness({
785+now: 1_000,
786+resolveSessionKeyForRun: () => "session-1",
787+});
788+vi.mocked(loadSessionEntry).mockReturnValue({
789+cfg: {},
790+storePath: "/tmp/sessions.json",
791+store: {},
792+entry: { sessionId: "session-1", verboseLevel: "on", updatedAt: 1_500 },
793+canonicalKey: "session-1",
794+legacyKey: undefined,
795+});
796+797+registerAgentRunContext("run-tool-toggle", {
798+sessionKey: "session-1",
799+verboseLevel: "off",
800+});
801+802+handler({
803+runId: "run-tool-toggle",
804+seq: 1,
805+stream: "tool",
806+ts: Date.now(),
807+data: { phase: "start", name: "read", toolCallId: "t-toggle" },
808+});
809+810+const nodeToolCalls = nodeSendToSession.mock.calls.filter(([, event]) => event === "agent");
811+expect(nodeToolCalls).toHaveLength(1);
812+expect(nodeToolCalls[0]?.[2]).toEqual(
813+expect.objectContaining({
814+stream: "tool",
815+data: expect.objectContaining({
816+phase: "start",
817+name: "read",
818+}),
819+}),
820+);
821+resetAgentRunContextForTest();
822+});
823+824+it("keeps one-shot run verbose over older session state", () => {
825+const { nodeSendToSession, handler } = createHarness({
826+now: 2_000,
827+resolveSessionKeyForRun: () => "session-1",
828+});
829+vi.mocked(loadSessionEntry).mockReturnValue({
830+cfg: {},
831+storePath: "/tmp/sessions.json",
832+store: {},
833+entry: { sessionId: "session-1", verboseLevel: "off", updatedAt: 1_500 },
834+canonicalKey: "session-1",
835+legacyKey: undefined,
836+});
837+838+registerAgentRunContext("run-tool-inline", {
839+sessionKey: "session-1",
840+verboseLevel: "on",
841+});
842+843+handler({
844+runId: "run-tool-inline",
845+seq: 1,
846+stream: "tool",
847+ts: Date.now(),
848+data: { phase: "start", name: "read", toolCallId: "t-inline" },
849+});
850+851+const nodeToolCalls = nodeSendToSession.mock.calls.filter(([, event]) => event === "agent");
852+expect(nodeToolCalls).toHaveLength(1);
853+resetAgentRunContextForTest();
854+});
855+763856it("mirrors tool events to session subscribers so late-joining operator UIs can render them", () => {
764857const { broadcastToConnIds, sessionEventSubscribers, handler } = createHarness({
765858resolveSessionKeyForRun: () => "session-1",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。