import type { ProviderDriverKind, ThreadId } from "@t3tools/contracts"; import { causeErrorTag, errorTag } from "@t3tools/shared/observability"; import * as Cause from "effect/Cause"; import * as Crypto from "effect/Crypto"; import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import type * as EffectAcpProtocol from "effect-acp/protocol"; import type { EventNdjsonLogger } from "../Layers/EventNdjsonLogger.ts"; import type * as AcpSessionRuntime from "./AcpSessionRuntime.ts"; function structuralMethod(value: string): string { return value.length <= 128 && /^[A-Za-z][A-Za-z0-9._:/-]*$/.test(value) ? value : "unknown"; } function summarizePayload(payload: unknown): Readonly> { if (payload === null) return { valueType: "null" }; if (typeof payload === "string") { return { valueType: "string", byteLength: new TextEncoder().encode(payload).byteLength }; } if (payload instanceof Uint8Array) { return { valueType: "bytes", byteLength: payload.byteLength }; } if (Array.isArray(payload)) { return { valueType: "array", itemCount: payload.length }; } if (typeof payload !== "object") { return { valueType: typeof payload }; } try { const record = payload as Record; return { valueType: "object", fieldCount: Object.keys(record).length, ...(typeof record._tag === "string" ? { messageTag: errorTag(record) } : {}), ...(typeof record.tag === "string" ? { method: structuralMethod(record.tag) } : {}), }; } catch { return { valueType: "object" }; } } function formatRequestLogPayload(event: AcpSessionRuntime.AcpSessionRequestLogEvent) { return { method: structuralMethod(event.method), status: event.status, request: summarizePayload(event.payload), ...(event.result !== undefined ? { result: summarizePayload(event.result) } : {}), ...(event.cause !== undefined ? { errorTag: causeErrorTag(event.cause), reasonCount: event.cause.reasons.length, } : {}), }; } function formatProtocolLogPayload(event: EffectAcpProtocol.AcpProtocolLogEvent) { return { direction: event.direction, stage: event.stage, payload: summarizePayload(event.payload), }; } export const makeAcpNativeLoggerFactory = Effect.fn("makeAcpNativeLoggerFactory")(function* () { const crypto = yield* Crypto.Crypto; return (input: { readonly nativeEventLogger: EventNdjsonLogger | undefined; readonly provider: ProviderDriverKind; readonly threadId: ThreadId; }): Pick => { const writeNativeAcpLog = (logInput: { readonly kind: "request" | "protocol"; readonly payload: unknown; }) => Effect.gen(function* () { if (!input.nativeEventLogger) return; const observedAt = DateTime.formatIso(yield* DateTime.now); yield* input.nativeEventLogger.write( { observedAt, event: { id: yield* crypto.randomUUIDv4, kind: logInput.kind, provider: input.provider, createdAt: observedAt, threadId: input.threadId, payload: logInput.payload, }, }, input.threadId, ); }).pipe( Effect.catchCause((cause) => Cause.hasInterrupts(cause) ? Effect.interrupt : Effect.logWarning("Failed to write native ACP event log.", { errorTag: causeErrorTag(cause), reasonCount: cause.reasons.length, provider: input.provider, threadId: input.threadId, }), ), ); return { requestLogger: (event: AcpSessionRuntime.AcpSessionRequestLogEvent) => writeNativeAcpLog({ kind: "request", payload: formatRequestLogPayload(event), }), ...(input.nativeEventLogger ? { protocolLogging: { logIncoming: true, logOutgoing: true, logger: (event: EffectAcpProtocol.AcpProtocolLogEvent) => writeNativeAcpLog({ kind: "protocol", payload: formatProtocolLogPayload(event), }), } satisfies NonNullable, } : {}), }; }; });