


















@@ -4,6 +4,11 @@ import path from "node:path";
44import { SessionManager } from "@mariozechner/pi-coding-agent";
55import type { EmbeddedRunAttemptParams } from "openclaw/plugin-sdk/agent-harness";
66import { resetAgentEventsForTest } from "openclaw/plugin-sdk/agent-harness-runtime";
7+import {
8+onInternalDiagnosticEvent,
9+resetDiagnosticEventsForTest,
10+type DiagnosticEventPayload,
11+} from "openclaw/plugin-sdk/diagnostic-runtime";
712import {
813initializeGlobalHookRunner,
914resetGlobalHookRunner,
@@ -23,6 +28,10 @@ const tempDirs = new Set<string>();
23282429type ProjectorNotification = Parameters<CodexAppServerEventProjector["handleNotification"]>[0];
253031+function flushDiagnosticEvents() {
32+return new Promise<void>((resolve) => setImmediate(resolve));
33+}
34+2635function assistantMessage(text: string, timestamp: number) {
2736return {
2837role: "assistant" as const,
@@ -82,10 +91,12 @@ async function createProjectorWithAssistantHooks() {
82918392beforeEach(() => {
8493resetAgentEventsForTest();
94+resetDiagnosticEventsForTest();
8595});
86968797afterEach(async () => {
8898resetAgentEventsForTest();
99+resetDiagnosticEventsForTest();
89100resetGlobalHookRunner();
90101resetCodexRateLimitCacheForTests();
91102vi.restoreAllMocks();
@@ -780,41 +791,48 @@ describe("CodexAppServerEventProjector", () => {
780791it("synthesizes normalized tool progress for Codex-native tool items", async () => {
781792const onAgentEvent = vi.fn();
782793const projector = await createProjector({ ...(await createParams()), onAgentEvent });
794+const diagnosticEvents: DiagnosticEventPayload[] = [];
795+const unsubscribe = onInternalDiagnosticEvent((event) => diagnosticEvents.push(event));
783796784-await projector.handleNotification(
785-forCurrentTurn("item/started", {
786-item: {
787-type: "commandExecution",
788-id: "cmd-1",
789-command: "pnpm test extensions/codex",
790-cwd: "/workspace",
791-processId: null,
792-source: "agent",
793-status: "inProgress",
794-commandActions: [],
795-aggregatedOutput: null,
796-exitCode: null,
797-durationMs: null,
798-},
799-}),
800-);
801-await projector.handleNotification(
802-forCurrentTurn("item/completed", {
803-item: {
804-type: "commandExecution",
805-id: "cmd-1",
806-command: "pnpm test extensions/codex",
807-cwd: "/workspace",
808-processId: null,
809-source: "agent",
810-status: "completed",
811-commandActions: [],
812-aggregatedOutput: "ok",
813-exitCode: 0,
814-durationMs: 42,
815-},
816-}),
817-);
797+try {
798+await projector.handleNotification(
799+forCurrentTurn("item/started", {
800+item: {
801+type: "commandExecution",
802+id: "cmd-1",
803+command: "pnpm test extensions/codex",
804+cwd: "/workspace",
805+processId: null,
806+source: "agent",
807+status: "inProgress",
808+commandActions: [],
809+aggregatedOutput: null,
810+exitCode: null,
811+durationMs: null,
812+},
813+}),
814+);
815+await projector.handleNotification(
816+forCurrentTurn("item/completed", {
817+item: {
818+type: "commandExecution",
819+id: "cmd-1",
820+command: "pnpm test extensions/codex",
821+cwd: "/workspace",
822+processId: null,
823+source: "agent",
824+status: "completed",
825+commandActions: [],
826+aggregatedOutput: "ok",
827+exitCode: 0,
828+durationMs: 42,
829+},
830+}),
831+);
832+await flushDiagnosticEvents();
833+} finally {
834+unsubscribe();
835+}
818836819837const itemStart = findAgentEvent(onAgentEvent, {
820838stream: "item",
@@ -844,6 +862,41 @@ describe("CodexAppServerEventProjector", () => {
844862const toolResultPayload = requireRecord(toolResult.result, "tool result payload");
845863expect(toolResultPayload.exitCode).toBe(0);
846864expect(toolResultPayload.durationMs).toBe(42);
865+const toolDiagnosticEvents = diagnosticEvents.filter(
866+(
867+event,
868+): event is Extract<
869+DiagnosticEventPayload,
870+{
871+type:
872+| "tool.execution.started"
873+| "tool.execution.completed"
874+| "tool.execution.error"
875+| "tool.execution.blocked";
876+}
877+> => event.type.startsWith("tool.execution."),
878+);
879+expect(
880+toolDiagnosticEvents.map((event) => ({
881+type: event.type,
882+toolName: event.toolName,
883+toolCallId: event.toolCallId,
884+durationMs: "durationMs" in event ? event.durationMs : undefined,
885+})),
886+).toEqual([
887+{
888+type: "tool.execution.started",
889+toolName: "bash",
890+toolCallId: "cmd-1",
891+durationMs: undefined,
892+},
893+{
894+type: "tool.execution.completed",
895+toolName: "bash",
896+toolCallId: "cmd-1",
897+durationMs: 42,
898+},
899+]);
847900const result = projector.buildResult(buildEmptyToolTelemetry());
848901expect(result.messagesSnapshot.map((message) => message.role)).toEqual([
849902"user",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。