Bläddra i källkod

fix(core): preserve session failure causes

Kit Langton 3 månader sedan
förälder
incheckning
53ff1b57c9

+ 2 - 4
packages/core/src/session.ts

@@ -25,6 +25,7 @@ import { fromRow } from "./session/info"
 import { SessionRunner } from "./session/runner/index"
 import { SessionStore } from "./session/store"
 import { SessionExecution } from "./session/execution"
+import { logFailure } from "./session/logging"
 import { MessageDecodeError } from "./session/error"
 import { SessionEvent } from "./session/event"
 import { SessionInput } from "./session/input"
@@ -177,10 +178,7 @@ export const layer = Layer.effect(
         Effect.tapCause((cause) =>
           Cause.hasInterruptsOnly(cause)
             ? Effect.void
-            : Effect.logError("Failed to wake Session").pipe(
-                Effect.annotateLogs("sessionID", admitted.sessionID),
-                Effect.annotateLogs("cause", cause),
-              ),
+            : logFailure("Failed to wake Session", admitted.sessionID, cause),
         ),
         Effect.ignore,
         Effect.forkIn(scope, { startImmediately: true }),

+ 2 - 5
packages/core/src/session/execution/local.ts

@@ -5,6 +5,7 @@ import { SessionRunner } from "../runner"
 import { SessionSchema } from "../schema"
 import { SessionStore } from "../store"
 import { SessionExecution } from "../execution"
+import { logFailure } from "../logging"
 
 /** Current-process routing for implicit-local Locations. Future remote placement belongs here. */
 export const layer = Layer.effect(
@@ -20,11 +21,7 @@ export const layer = Layer.effect(
           Effect.provide(locations.get(session.location)),
         )
       }),
-      onFailure: (sessionID, cause) =>
-        Effect.logError("Failed to drain Session").pipe(
-          Effect.annotateLogs("sessionID", sessionID),
-          Effect.annotateLogs("cause", cause),
-        ),
+      onFailure: (sessionID, cause) => logFailure("Failed to drain Session", sessionID, cause),
     })
 
     return SessionExecution.Service.of({

+ 8 - 0
packages/core/src/session/logging.ts

@@ -0,0 +1,8 @@
+import { Cause, Effect } from "effect"
+import { SessionSchema } from "./schema"
+
+export const logFailure = (
+  message: "Failed to drain Session" | "Failed to wake Session",
+  sessionID: SessionSchema.ID,
+  cause: Cause.Cause<unknown>,
+) => Effect.logError(message, cause).pipe(Effect.annotateLogs("sessionID", sessionID))

+ 2 - 5
packages/core/src/session/run-coordinator.ts

@@ -2,6 +2,7 @@ export * as SessionRunCoordinator from "./run-coordinator"
 
 import { Cause, Context, Deferred, Effect, Exit, Fiber, FiberSet, Layer, Scope } from "effect"
 import { SessionRunner } from "./runner"
+import { logFailure } from "./logging"
 import { SessionSchema } from "./schema"
 
 export type Mode = "run" | "wake"
@@ -275,11 +276,7 @@ export const layer = Layer.effect(
     Effect.flatMap((runner) =>
       make<SessionSchema.ID, void, SessionRunner.RunError>({
         drain: (sessionID, mode) => runner.run({ sessionID, force: mode === "run" }),
-        onFailure: (sessionID, cause) =>
-          Effect.logError("Failed to drain Session").pipe(
-            Effect.annotateLogs("sessionID", sessionID),
-            Effect.annotateLogs("cause", cause),
-          ),
+        onFailure: (sessionID, cause) => logFailure("Failed to drain Session", sessionID, cause),
       }),
     ),
     Effect.map(Service.of),

+ 30 - 0
packages/core/test/session-logging.test.ts

@@ -0,0 +1,30 @@
+import { describe, expect, test } from "bun:test"
+import { Cause, Effect, Logger } from "effect"
+import { logFailure } from "@opencode-ai/core/session/logging"
+import { SessionSchema } from "@opencode-ai/core/session/schema"
+
+describe("Session logging", () => {
+  for (const message of ["Failed to drain Session", "Failed to wake Session"] as const) {
+    test(`renders the cause for ${message}`, async () => {
+      const entries: Array<ReturnType<typeof Logger.formatStructured.log>> = []
+      const logger = Logger.formatStructured.pipe(
+        Logger.map((entry): void => {
+          entries.push(entry)
+        }),
+      )
+
+      await logFailure(
+        message,
+        SessionSchema.ID.make("session-123"),
+        Cause.fail({ _tag: "SessionFailure", detail: { code: "nested-code" } }),
+      ).pipe(Effect.provide(Logger.layer([logger])), Effect.runPromise)
+
+      expect(entries).toHaveLength(1)
+      expect(entries[0]?.message).toBe(message)
+      expect(entries[0]?.annotations).toEqual({ sessionID: "session-123" })
+      expect(entries[0]?.cause).toContain("SessionFailure")
+      expect(entries[0]?.cause).toContain("nested-code")
+      expect(entries[0]?.cause).not.toContain("[Object")
+    })
+  }
+})