






















@@ -3,11 +3,14 @@ import {
33emitDiagnosticEvent,
44emitTrustedDiagnosticEvent,
55formatDiagnosticTraceparentForPropagation,
6+hasPendingInternalDiagnosticEvent,
67isDiagnosticsEnabled,
78onInternalDiagnosticEvent,
89onDiagnosticEvent,
910resetDiagnosticEventsForTest,
1011setDiagnosticsEnabledForProcess,
12+waitForDiagnosticEventsDrained,
13+type DiagnosticEventPayload,
1114} from "./diagnostic-events.js";
1215import {
1316createDiagnosticTraceContext,
@@ -415,6 +418,224 @@ describe("diagnostic-events", () => {
415418expect(events).toEqual(["tool.execution.started", "model.call.started"]);
416419});
417420421+it("yields between large high-frequency diagnostic event bursts", async () => {
422+const events: string[] = [];
423+onDiagnosticEvent((event) => {
424+events.push(event.type);
425+});
426+427+for (let index = 0; index < 250; index += 1) {
428+emitDiagnosticEvent({
429+type: "model.call.started",
430+runId: `run-${index}`,
431+callId: `call-${index}`,
432+provider: "openai",
433+model: "gpt-5.4",
434+});
435+}
436+437+expect(events).toStrictEqual([]);
438+await new Promise<void>((resolve) => setImmediate(resolve));
439+expect(events).toHaveLength(100);
440+await new Promise<void>((resolve) => setImmediate(resolve));
441+expect(events).toHaveLength(200);
442+await new Promise<void>((resolve) => setImmediate(resolve));
443+expect(events).toHaveLength(250);
444+});
445+446+it("waits for all queued high-frequency diagnostic events to drain", async () => {
447+const events: string[] = [];
448+onDiagnosticEvent((event) => {
449+events.push(event.type);
450+});
451+452+for (let index = 0; index < 250; index += 1) {
453+emitDiagnosticEvent({
454+type: "model.call.started",
455+runId: `run-${index}`,
456+callId: `call-${index}`,
457+provider: "openai",
458+model: "gpt-5.4",
459+});
460+}
461+462+await waitForDiagnosticEventsDrained();
463+464+expect(events).toHaveLength(250);
465+});
466+467+it("reports pending async diagnostic events before they drain", async () => {
468+emitTrustedDiagnosticEvent({
469+type: "tool.execution.error",
470+runId: "run-pending",
471+toolName: "exec",
472+toolCallId: "call-pending",
473+durationMs: 1,
474+errorCategory: "test",
475+});
476+477+expect(
478+hasPendingInternalDiagnosticEvent(
479+(event, metadata) =>
480+metadata.trusted &&
481+event.type === "tool.execution.error" &&
482+event.toolCallId === "call-pending",
483+),
484+).toBe(true);
485+486+await waitForDiagnosticEventsDrained();
487+488+expect(
489+hasPendingInternalDiagnosticEvent((event) => event.type === "tool.execution.error"),
490+).toBe(false);
491+});
492+493+it("passes immutable pending diagnostic copies to queue inspectors", async () => {
494+const events: DiagnosticEventPayload[] = [];
495+onInternalDiagnosticEvent((event) => {
496+events.push(event);
497+});
498+499+emitTrustedDiagnosticEvent({
500+type: "tool.execution.error",
501+runId: "run-immutable",
502+toolName: "exec",
503+toolCallId: "call-immutable",
504+durationMs: 1,
505+errorCategory: "test",
506+});
507+508+let mutationErrors = 0;
509+expect(
510+hasPendingInternalDiagnosticEvent((event, metadata) => {
511+try {
512+(event as { type: string }).type = "model.usage";
513+} catch {
514+mutationErrors += 1;
515+}
516+try {
517+(metadata as { trusted: boolean }).trusted = false;
518+} catch {
519+mutationErrors += 1;
520+}
521+return (
522+metadata.trusted &&
523+event.type === "tool.execution.error" &&
524+event.toolCallId === "call-immutable"
525+);
526+}),
527+).toBe(true);
528+expect(mutationErrors).toBe(2);
529+530+await waitForDiagnosticEventsDrained();
531+532+expect(events).toMatchObject([
533+{
534+type: "tool.execution.error",
535+toolCallId: "call-immutable",
536+},
537+]);
538+});
539+540+it("skips uncloneable pending diagnostics during queue inspection", async () => {
541+emitDiagnosticEvent({
542+type: "model.call.started",
543+runId: "run-uncloneable",
544+callId: "call-uncloneable",
545+provider: "openai",
546+model: "gpt-5.4",
547+badValue: () => undefined,
548+} as never);
549+emitTrustedDiagnosticEvent({
550+type: "tool.execution.error",
551+runId: "run-cloneable",
552+toolName: "exec",
553+toolCallId: "call-cloneable",
554+durationMs: 1,
555+errorCategory: "test",
556+});
557+558+expect(
559+hasPendingInternalDiagnosticEvent(
560+(event, metadata) =>
561+metadata.trusted &&
562+event.type === "tool.execution.error" &&
563+event.toolCallId === "call-cloneable",
564+),
565+).toBe(true);
566+});
567+568+it("preserves trusted terminal tool diagnostics when the async queue is full", async () => {
569+const events: DiagnosticEventPayload[] = [];
570+onInternalDiagnosticEvent((event) => {
571+events.push(event);
572+});
573+574+emitTrustedDiagnosticEvent({
575+type: "tool.execution.completed",
576+runId: "run-saturation-first",
577+toolName: "exec",
578+toolCallId: "call-saturation-first",
579+durationMs: 1,
580+});
581+582+for (let index = 0; index < 9_999; index += 1) {
583+emitDiagnosticEvent({
584+type: "model.call.started",
585+runId: `saturation-run-${index}`,
586+callId: `saturation-call-${index}`,
587+provider: "openai",
588+model: "gpt-5.4",
589+});
590+}
591+592+emitTrustedDiagnosticEvent({
593+type: "tool.execution.error",
594+runId: "run-saturation-second",
595+toolName: "exec",
596+toolCallId: "call-saturation-second",
597+durationMs: 1,
598+errorCategory: "test",
599+});
600+601+expect(
602+hasPendingInternalDiagnosticEvent(
603+(event, metadata) =>
604+metadata.trusted &&
605+event.type === "tool.execution.error" &&
606+event.toolCallId === "call-saturation-second",
607+),
608+).toBe(true);
609+610+await waitForDiagnosticEventsDrained();
611+612+expect(
613+events
614+.filter(
615+(
616+event,
617+): event is Extract<
618+DiagnosticEventPayload,
619+{ type: "tool.execution.completed" | "tool.execution.error" }
620+> => event.type === "tool.execution.completed" || event.type === "tool.execution.error",
621+)
622+.map((event) => ({
623+type: event.type,
624+toolCallId: event.toolCallId,
625+})),
626+).toEqual([
627+{
628+type: "tool.execution.completed",
629+toolCallId: "call-saturation-first",
630+},
631+{
632+type: "tool.execution.error",
633+toolCallId: "call-saturation-second",
634+},
635+]);
636+expect(events.filter((event) => event.type === "model.call.started")).toHaveLength(9_998);
637+});
638+418639it("keeps log records off the public diagnostic event stream", async () => {
419640const publicEvents: string[] = [];
420641const internalEvents: string[] = [];
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。