184 lines · 6.5 KB · TypeScript
RawDownload
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"; const transientProtocolUpdates = new Set(["agent_message_chunk", "agent_thought_chunk"]); 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<Record<string, unknown>> {  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<string, unknown>;    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),  };} function isTransientProtocolMessage(message: unknown): boolean {  if (typeof message !== "object" || message === null) return false;  const method = Reflect.get(message, "tag") ?? Reflect.get(message, "method");  if (method !== "session/update") return false;   const payload = Reflect.get(message, "payload") ?? Reflect.get(message, "params");  if (typeof payload !== "object" || payload === null) return false;  const update = Reflect.get(payload, "update");  if (typeof update !== "object" || update === null) return false;  const updateType = Reflect.get(update, "sessionUpdate");  return typeof updateType === "string" && transientProtocolUpdates.has(updateType);} function rawChunkContainsOnlyTransientMessages(payload: string): boolean {  const lines = payload.split("\n");  const remainder = lines.pop() ?? "";  if (remainder.trim().length > 0) return false;   const messages: Array<unknown> = [];  for (const line of lines) {    if (line.trim().length === 0) continue;    try {      messages.push(JSON.parse(line));    } catch {      return false;    }  }  return messages.length > 0 && messages.every(isTransientProtocolMessage);} function filterTransientProtocolLog(  event: EffectAcpProtocol.AcpProtocolLogEvent,): EffectAcpProtocol.AcpProtocolLogEvent | undefined {  if (event.direction !== "incoming") return event;   if (event.stage === "raw" && typeof event.payload === "string") {    return rawChunkContainsOnlyTransientMessages(event.payload) ? undefined : event;  }   if (event.stage !== "decoded") return event;  if (!Array.isArray(event.payload)) {    return isTransientProtocolMessage(event.payload) ? undefined : event;  }   const payload = event.payload.filter((message) => !isTransientProtocolMessage(message));  return payload.length === 0 ? undefined : { ...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;    readonly verboseProtocolLogging?: boolean;  }): Pick<AcpSessionRuntime.AcpSessionRuntimeOptions, "requestLogger" | "protocolLogging"> => {    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 && input.verboseProtocolLogging        ? {            protocolLogging: {              logIncoming: true,              logOutgoing: true,              logger: (event: EffectAcpProtocol.AcpProtocolLogEvent) => {                const filtered = filterTransientProtocolLog(event);                return filtered                  ? writeNativeAcpLog({                      kind: "protocol",                      payload: formatProtocolLogPayload(filtered),                    })                  : Effect.void;              },            } satisfies NonNullable<AcpSessionRuntime.AcpSessionRuntimeOptions["protocolLogging"]>,          }        : {}),    };  };});