fix(opencode): restore effect error logging (#31551)

This commit is contained in:
Dax 2026-06-09 15:09:04 -04:00 committed by GitHub
commit 600e405ba0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 55 additions and 11 deletions

View file

@ -586,7 +586,13 @@ export const layer = Layer.effect(
if (target.type === "remote") {
yield* syncHistory(previous, target.url, target.headers).pipe(
Effect.catch((error) => Effect.sync(() => {})),
Effect.catch((error) =>
Effect.logWarning("session warp final source sync failed", {
workspaceID: previous.id,
sessionID: input.sessionID,
error: errorData(error),
}),
),
)
} else {
yield* prompt.cancel(input.sessionID)
@ -739,9 +745,7 @@ export const layer = Layer.effect(
([type, adapter]) =>
WorkspaceAdapterRuntime.list(adapter).pipe(
Effect.catchCause((error) =>
Effect.sync(() => {
return []
}),
Effect.logWarning("workspace adapter list failed", { type, error }).pipe(Effect.as([])),
),
),
{ concurrency: "unbounded" },
@ -817,7 +821,7 @@ export const layer = Layer.effect(
Effect.gen(function* () {
yield* WorkspaceAdapterRuntime.remove(info)
}),
() => Effect.sync(() => {}),
() => Effect.logError("adapter not available when removing workspace", { type: row.type }),
)
yield* db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run().pipe(Effect.orDie)

View file

@ -215,6 +215,12 @@ function fetchFromClient<T extends { name: string }>(
return e
},
}).pipe(
Effect.tapError((error) =>
Effect.logWarning(`failed to get ${label}`, {
clientName,
error: error instanceof Error ? error.message : String(error),
}),
),
Effect.map((items) => {
const out: Record<string, T & { client: string }> = {}
const sanitizedClient = sanitize(clientName)
@ -529,6 +535,7 @@ export const layer = Layer.effect(
([key, mcp]) =>
Effect.gen(function* () {
if (!isMcpConfigured(mcp)) {
yield* Effect.logError("Ignoring MCP config entry without type", { key })
return
}
@ -679,6 +686,7 @@ export const layer = Layer.effect(
const listed = s.defs[clientName]
if (!listed) {
yield* Effect.logWarning("missing cached tools for connected server", { clientName })
return
}
@ -744,6 +752,7 @@ export const layer = Layer.effect(
const s = yield* InstanceState.get(state)
const client = s.clients[clientName]
if (!client) {
yield* Effect.logWarning(`client not found for ${label}`, { clientName })
return undefined
}
return yield* Effect.tryPromise({
@ -751,7 +760,16 @@ export const layer = Layer.effect(
catch: (e: any) => {
return e
},
}).pipe(Effect.orElseSucceed(() => undefined))
}).pipe(
Effect.tapError((error) =>
Effect.logError(`failed to ${label}`, {
clientName,
...meta,
error: error instanceof Error ? error.message : String(error),
}),
),
Effect.orElseSucceed(() => undefined),
)
})
const getPrompt = Effect.fn("MCP.getPrompt")(function* (

View file

@ -162,8 +162,13 @@ export const layer = Layer.effect(
for (const plugin of flags.disableDefaultPlugins ? [] : internalPlugins(flags)) {
const init = yield* Effect.tryPromise({
try: () => plugin(input),
catch: (err) => {},
}).pipe(Effect.option)
catch: errorMessage,
}).pipe(
Effect.tapError((error) =>
Effect.logError("failed to load internal plugin", { name: plugin.name, error }),
),
Effect.option,
)
if (init._tag === "Some") hooks.push(init.value)
}
@ -217,6 +222,7 @@ export const layer = Layer.effect(
return message
},
}).pipe(
Effect.tapError((error) => Effect.logError("failed to load plugin", { path: load.spec, error })),
Effect.catch(() => {
// TODO: make proper events for this
// events.publish(Session.Event.Error, {
@ -256,8 +262,11 @@ export const layer = Layer.effect(
(hook) =>
Effect.tryPromise({
try: () => Promise.resolve(hook.dispose?.()),
catch: (error) => {},
}).pipe(Effect.ignore),
catch: errorMessage,
}).pipe(
Effect.tapError((error) => Effect.logError("plugin dispose hook failed", { error })),
Effect.ignore,
),
{ discard: true },
),
)

View file

@ -113,6 +113,7 @@ const live: Layer.Layer<
// Wire up toolExecutor for DWS workflow models so that tool calls
// from the workflow service are executed via opencode's tool system
// and results sent back over the WebSocket.
const bridge = yield* EffectBridge.make()
if (language instanceof GitLabWorkflowLanguageModel) {
const workflowModel = language as GitLabWorkflowLanguageModel & {
sessionID?: string
@ -149,7 +150,6 @@ const live: Layer.Layer<
return !match || match.action !== "ask"
})
const bridge = yield* EffectBridge.make()
const approvedToolsForSession = new Set<string>()
workflowModel.approvalHandler = bridge.bind(async (approvalTools) => {
const uniqueNames = [...new Set(approvalTools.map((t: { name: string }) => t.name))] as string[]
@ -276,6 +276,19 @@ const live: Layer.Layer<
return {
type: "ai-sdk" as const,
result: streamText({
onError(error) {
bridge.fork(
Effect.logError("stream error", {
providerID: input.model.providerID,
modelID: input.model.id,
"session.id": input.sessionID,
small: (input.small ?? false).toString(),
agent: input.agent.name,
mode: input.agent.mode,
error,
}),
)
},
// Copilot returns the authoritative billed amount only in provider-specific response fields.
includeRawChunks: input.model.providerID.includes("github-copilot"),
async experimental_repairToolCall(failed) {