import { describe, expect, it } from "@effect/vitest"; import type { DesktopHostTelemetrySnapshot, ResourceMonitorSnapshotEvent, } from "@t3tools/contracts"; import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; import * as DesktopTelemetryReceiver from "../resourceTelemetry/DesktopTelemetryReceiver.ts"; import * as NativeTelemetryClient from "../resourceTelemetry/NativeTelemetryClient.ts"; import * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts"; import * as ResourceTelemetry from "../resourceTelemetry/ResourceTelemetry.ts"; import * as ProcessDiagnostics from "./ProcessDiagnostics.ts"; function makeNativeSnapshot( processes: ResourceMonitorSnapshotEvent["processes"], ): ResourceMonitorSnapshotEvent { return { version: 3, type: "snapshot", sequence: 1, sampledAtUnixMs: DateTime.toEpochMillis(DateTime.makeUnsafe("2026-05-05T10:00:00.000Z")), collectionDurationMicros: 250, scannedProcessCount: processes.length, retainedProcessCount: processes.length, inaccessibleProcessCount: 0, processes, }; } function makeTelemetryLayer( snapshot: ResourceMonitorSnapshotEvent, desktopSnapshot?: DesktopHostTelemetrySnapshot, ) { const nativeLayer = NativeTelemetryClient.layerTest({ sampleNow: Effect.succeed({ generation: 0, snapshot }), health: Effect.succeed({ status: "healthy", hello: Option.none(), lastSampleAt: Option.some(DateTime.makeUnsafe(snapshot.sampledAtUnixMs)), lastError: Option.none(), restartCount: 0, sampleIntervalMs: 1_000, }), }); const desktopLayer = desktopSnapshot ? DesktopTelemetryReceiver.layerTest({ latest: Effect.succeedSome(desktopSnapshot), health: Effect.succeed({ status: "healthy", lastSampleAt: Option.some(DateTime.makeUnsafe(desktopSnapshot.sampledAtUnixMs)), lastError: Option.none(), }), }) : DesktopTelemetryReceiver.layerTest(); return ResourceTelemetry.layer.pipe( Layer.provide(Layer.mergeAll(nativeLayer, desktopLayer, ResourceAttribution.layer)), ); } describe("ProcessDiagnostics", () => { it.effect("projects live process data from resource telemetry", () => Effect.gen(function* () { const snapshot = makeNativeSnapshot([ { pid: process.pid, ppid: 1, startTimeMs: 1_000, runTimeMs: 60_000, name: "node", command: "t3 server", status: "Running", cpuPercent: 0, cpuTimeMs: 100, residentBytes: 1_024, virtualBytes: 2_048, ioReadBytes: 100, ioWriteBytes: 200, ioSemantics: "storage", }, { pid: 4_242, ppid: process.pid, startTimeMs: 2_000, runTimeMs: 4_000, name: "agent", command: "codex app-server", status: "Running", cpuPercent: 1.5, cpuTimeMs: 60, residentBytes: 2_048, virtualBytes: 4_096, ioReadBytes: 300, ioWriteBytes: 400, ioSemantics: "storage", }, ]); const telemetryLayer = makeTelemetryLayer(snapshot); const layer = ProcessDiagnostics.layer.pipe(Layer.provideMerge(telemetryLayer)); const diagnostics = yield* Effect.gen(function* () { const processDiagnostics = yield* ProcessDiagnostics.ProcessDiagnostics; return yield* processDiagnostics.read; }).pipe(Effect.provide(layer)); expect(diagnostics.processes.map((process) => process.pid)).toEqual([4242]); expect(diagnostics.processes[0]?.startTimeMs).toBe(2_000); expect(diagnostics.processes[0]?.cpuPercent).toBe(1.5); expect(diagnostics.processes[0]?.rssBytes).toBe(2_048); }), ); it.effect("rejects stale process identities before signaling", () => Effect.gen(function* () { const snapshot = makeNativeSnapshot([]); const telemetryLayer = makeTelemetryLayer(snapshot); const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer)); const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( Effect.flatMap((processDiagnostics) => processDiagnostics.signal({ pid: 4_242, startTimeMs: 2_000, signal: "SIGINT", }), ), Effect.provide(layer), ); expect(result).toEqual({ pid: 4242, signal: "SIGINT", signaled: false, message: Option.some("Process 4242 no longer matches the selected process identity."), }); }), ); it.effect("refuses to signal when a fresh identity check cannot be collected", () => Effect.gen(function* () { const snapshot = makeNativeSnapshot([ { pid: 4_242, ppid: process.pid, startTimeMs: 2_000, runTimeMs: 4_000, name: "agent", command: "codex app-server", status: "Running", cpuPercent: 1.5, cpuTimeMs: 60, residentBytes: 2_048, virtualBytes: 4_096, ioReadBytes: 300, ioWriteBytes: 400, ioSemantics: "storage", }, ]); const staleTelemetry = yield* Effect.service(ResourceTelemetry.ResourceTelemetry).pipe( Effect.flatMap((telemetry) => telemetry.latest), Effect.provide(makeTelemetryLayer(snapshot)), ); const telemetryLayer = Layer.succeed( ResourceTelemetry.ResourceTelemetry, ResourceTelemetry.ResourceTelemetry.of({ latest: Effect.succeed(staleTelemetry), changes: Stream.empty, subscribe: Effect.die("unused"), readHistory: () => Effect.die("unused"), refresh: Effect.fail( new ResourceTelemetry.ResourceTelemetryRefreshFailed({ operation: "refresh", cause: new Error("collector unavailable"), }), ), validateProcessIdentity: () => Effect.die("unused"), retry: Effect.die("unused"), }), ); const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer)); const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( Effect.flatMap((processDiagnostics) => processDiagnostics.signal({ pid: 4_242, startTimeMs: 2_000, signal: "SIGINT", }), ), Effect.provide(layer), ); expect(result).toEqual({ pid: 4_242, signal: "SIGINT", signaled: false, message: Option.some( "Could not refresh process 4242; refusing to signal a stale identity.", ), }); }), ); it.effect("rejects Electron processes as signal targets", () => Effect.gen(function* () { const sampledAtUnixMs = DateTime.toEpochMillis( DateTime.makeUnsafe("2026-05-05T10:00:00.000Z"), ); const snapshot = makeNativeSnapshot([ { pid: 4_242, ppid: 1, startTimeMs: 2_000, runTimeMs: 4_000, name: "electron", command: "electron", status: "Running", cpuPercent: 1.5, cpuTimeMs: 60, residentBytes: 2_048, virtualBytes: 4_096, ioReadBytes: 300, ioWriteBytes: 400, ioSemantics: "storage", }, ]); const sampledAt = DateTime.makeUnsafe(sampledAtUnixMs); const telemetryLayer = makeTelemetryLayer(snapshot, { version: 1, type: "desktopTelemetry", sequence: 1, sampledAtUnixMs, electronPid: 4_242, power: { source: "electron-main", idle: "false", idleSeconds: 0, locked: "false", suspended: false, onBattery: "false", lowPowerMode: "unknown", thermalState: "nominal", stale: false, updatedAt: sampledAt, }, speedLimitPercent: Option.none(), electronProcesses: [ { pid: 4_242, creationTimeMs: 2_000, type: "Browser", name: "electron", cpuPercent: 1.5, idleWakeupsPerSecond: 0, workingSetBytes: 2_048, peakWorkingSetBytes: 2_048, }, ], }); const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer)); const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( Effect.flatMap((processDiagnostics) => processDiagnostics.signal({ pid: 4_242, startTimeMs: 2_000, signal: "SIGKILL", }), ), Effect.provide(layer), ); expect(result).toEqual({ pid: 4_242, signal: "SIGKILL", signaled: false, message: Option.some("Process 4242 is not a signalable T3 backend descendant."), }); const diagnostics = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( Effect.flatMap((processDiagnostics) => processDiagnostics.read), Effect.provide(layer), ); expect(diagnostics.processes).toEqual([]); expect(diagnostics.processCount).toBe(0); expect(diagnostics.totalCpuPercent).toBe(0); expect(diagnostics.totalRssBytes).toBe(0); }), ); });