summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax <[email protected]>2026-05-03 14:45:48 -0400
committerGitHub <[email protected]>2026-05-03 14:45:48 -0400
commit7749d8e85f2bf4879ee98af90066c167153bb19b (patch)
tree0b4a72dab4167298cce15e1621c54a6af170b4c9
parent28112fbd12d16d21563eead2a188e0ecae11303e (diff)
downloadopencode-7749d8e85f2bf4879ee98af90066c167153bb19b.tar.gz
opencode-7749d8e85f2bf4879ee98af90066c167153bb19b.zip
Add v2 session failure events (#25628)
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx11
-rw-r--r--packages/opencode/src/session/processor.ts13
-rw-r--r--packages/opencode/src/session/projectors-next.ts7
-rw-r--r--packages/opencode/src/v2/session-event.ts29
-rw-r--r--packages/opencode/src/v2/session-message-updater.ts13
-rw-r--r--packages/opencode/src/v2/session-message.ts2
-rw-r--r--packages/sdk/js/src/v2/gen/types.gen.ts51
7 files changed, 104 insertions, 22 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx b/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx
index f82bb4d96..9801f0a2f 100644
--- a/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/sync-v2.tsx
@@ -143,6 +143,15 @@ export const { use: useSyncV2, provider: SyncProviderV2 } = createSimpleContext(
currentAssistant.snapshot = { ...currentAssistant.snapshot, end: event.properties.snapshot }
})
break
+ case "session.next.step.failed":
+ update(event.properties.sessionID, (draft) => {
+ const currentAssistant = activeAssistant(draft)
+ if (!currentAssistant) return
+ currentAssistant.time.completed = event.properties.timestamp
+ currentAssistant.finish = "error"
+ currentAssistant.error = event.properties.error
+ })
+ break
case "session.next.text.started":
update(event.properties.sessionID, (draft) => {
activeAssistant(draft)?.content.push({ type: "text", text: "" })
@@ -210,7 +219,7 @@ export const { use: useSyncV2, provider: SyncProviderV2 } = createSimpleContext(
match.time.completed = event.properties.timestamp
})
break
- case "session.next.tool.error":
+ case "session.next.tool.failed":
update(event.properties.sessionID, (draft) => {
const match = latestTool(activeAssistant(draft), event.properties.callID)
if (match?.state.status !== "running") return
diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts
index e2a47f180..cf1a7e0ae 100644
--- a/packages/opencode/src/session/processor.ts
+++ b/packages/opencode/src/session/processor.ts
@@ -405,7 +405,7 @@ export const layer: Layer.Layer<
case "tool-error": {
const toolCall = yield* readToolCall(value.toolCallId)
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
- EventV2.run(SessionEvent.Tool.Error.Sync, {
+ EventV2.run(SessionEvent.Tool.Failed.Sync, {
sessionID: ctx.sessionID,
callID: value.toolCallId,
error: {
@@ -650,6 +650,17 @@ export const layer: Layer.Layer<
yield* bus.publish(Session.Event.Error, { sessionID: ctx.sessionID, error })
return
}
+ if (!ctx.assistantMessage.summary) {
+ // TODO(v2): Temporary dual-write while migrating session messages to v2 events.
+ EventV2.run(SessionEvent.Step.Failed.Sync, {
+ sessionID: ctx.sessionID,
+ error: {
+ type: error.name,
+ message: errorMessage(e),
+ },
+ timestamp: DateTime.makeUnsafe(Date.now()),
+ })
+ }
ctx.assistantMessage.error = error
yield* bus.publish(Session.Event.Error, {
sessionID: ctx.assistantMessage.sessionID,
diff --git a/packages/opencode/src/session/projectors-next.ts b/packages/opencode/src/session/projectors-next.ts
index 951e3e874..88f73acf1 100644
--- a/packages/opencode/src/session/projectors-next.ts
+++ b/packages/opencode/src/session/projectors-next.ts
@@ -161,6 +161,9 @@ export default [
SyncEvent.project(SessionEvent.Step.Ended.Sync, (db, data, event) => {
update(db, { id: SessionMessage.ID.make(event.id), type: "session.next.step.ended", data })
}),
+ SyncEvent.project(SessionEvent.Step.Failed.Sync, (db, data, event) => {
+ update(db, { id: SessionMessage.ID.make(event.id), type: "session.next.step.failed", data })
+ }),
SyncEvent.project(SessionEvent.Text.Started.Sync, (db, data, event) => {
update(db, { id: SessionMessage.ID.make(event.id), type: "session.next.text.started", data })
}),
@@ -181,8 +184,8 @@ export default [
SyncEvent.project(SessionEvent.Tool.Success.Sync, (db, data, event) => {
update(db, { id: SessionMessage.ID.make(event.id), type: "session.next.tool.success", data })
}),
- SyncEvent.project(SessionEvent.Tool.Error.Sync, (db, data, event) => {
- update(db, { id: SessionMessage.ID.make(event.id), type: "session.next.tool.error", data })
+ SyncEvent.project(SessionEvent.Tool.Failed.Sync, (db, data, event) => {
+ update(db, { id: SessionMessage.ID.make(event.id), type: "session.next.tool.failed", data })
}),
SyncEvent.project(SessionEvent.Reasoning.Started.Sync, (db, data, event) => {
update(db, { id: SessionMessage.ID.make(event.id), type: "session.next.reasoning.started", data })
diff --git a/packages/opencode/src/v2/session-event.ts b/packages/opencode/src/v2/session-event.ts
index 3af5932f0..47938dcbe 100644
--- a/packages/opencode/src/v2/session-event.ts
+++ b/packages/opencode/src/v2/session-event.ts
@@ -22,6 +22,11 @@ const Base = {
sessionID: SessionID,
}
+const Error = Schema.Struct({
+ type: Schema.String,
+ message: Schema.String,
+})
+
export const AgentSwitched = EventV2.define({
type: "session.next.agent.switched",
aggregate: "sessionID",
@@ -128,6 +133,16 @@ export namespace Step {
},
})
export type Ended = Schema.Schema.Type<typeof Ended>
+
+ export const Failed = EventV2.define({
+ type: "session.next.step.failed",
+ aggregate: "sessionID",
+ schema: {
+ ...Base,
+ error: Error,
+ },
+ })
+ export type Failed = Schema.Schema.Type<typeof Failed>
}
export namespace Text {
@@ -275,23 +290,20 @@ export namespace Tool {
})
export type Success = Schema.Schema.Type<typeof Success>
- export const Error = EventV2.define({
- type: "session.next.tool.error",
+ export const Failed = EventV2.define({
+ type: "session.next.tool.failed",
aggregate: "sessionID",
schema: {
...Base,
callID: Schema.String,
- error: Schema.Struct({
- type: Schema.String,
- message: Schema.String,
- }),
+ error: Error,
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
}),
},
})
- export type Error = Schema.Schema.Type<typeof Error>
+ export type Failed = Schema.Schema.Type<typeof Failed>
}
export const RetryError = Schema.Struct({
@@ -359,6 +371,7 @@ export const All = Schema.Union(
Shell.Ended,
Step.Started,
Step.Ended,
+ Step.Failed,
Text.Started,
Text.Delta,
Text.Ended,
@@ -368,7 +381,7 @@ export const All = Schema.Union(
Tool.Called,
Tool.Progress,
Tool.Success,
- Tool.Error,
+ Tool.Failed,
Reasoning.Started,
Reasoning.Delta,
Reasoning.Ended,
diff --git a/packages/opencode/src/v2/session-message-updater.ts b/packages/opencode/src/v2/session-message-updater.ts
index ad1aa32e7..d5d5aac7b 100644
--- a/packages/opencode/src/v2/session-message-updater.ts
+++ b/packages/opencode/src/v2/session-message-updater.ts
@@ -199,6 +199,17 @@ export function update<Result>(adapter: Adapter<Result>, event: SessionEvent.Eve
)
}
},
+ "session.next.step.failed": (event) => {
+ if (currentAssistant) {
+ adapter.updateAssistant(
+ produce(currentAssistant, (draft) => {
+ draft.time.completed = event.data.timestamp
+ draft.finish = "error"
+ draft.error = event.data.error
+ }),
+ )
+ }
+ },
"session.next.text.started": () => {
if (currentAssistant) {
adapter.updateAssistant(
@@ -314,7 +325,7 @@ export function update<Result>(adapter: Adapter<Result>, event: SessionEvent.Eve
)
}
},
- "session.next.tool.error": (event) => {
+ "session.next.tool.failed": (event) => {
if (currentAssistant) {
adapter.updateAssistant(
produce(currentAssistant, (draft) => {
diff --git a/packages/opencode/src/v2/session-message.ts b/packages/opencode/src/v2/session-message.ts
index 8ec99bc20..94f6b1cac 100644
--- a/packages/opencode/src/v2/session-message.ts
+++ b/packages/opencode/src/v2/session-message.ts
@@ -152,7 +152,7 @@ export class Assistant extends Schema.Class<Assistant>("Session.Message.Assistan
write: Schema.Finite,
}),
}).pipe(Schema.optional),
- error: Schema.String.pipe(Schema.optional),
+ error: SessionEvent.Step.Failed.fields.data.fields.error.pipe(Schema.optional),
time: Schema.Struct({
created: V2Schema.DateTimeUtcFromMillis,
completed: V2Schema.DateTimeUtcFromMillis.pipe(Schema.optional),
diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts
index caa3d4c76..79ef42d9e 100644
--- a/packages/sdk/js/src/v2/gen/types.gen.ts
+++ b/packages/sdk/js/src/v2/gen/types.gen.ts
@@ -58,6 +58,7 @@ export type Event =
| EventSessionNextShellEnded
| EventSessionNextStepStarted
| EventSessionNextStepEnded
+ | EventSessionNextStepFailed
| EventSessionNextTextStarted
| EventSessionNextTextDelta
| EventSessionNextTextEnded
@@ -70,7 +71,7 @@ export type Event =
| EventSessionNextToolCalled
| EventSessionNextToolProgress
| EventSessionNextToolSuccess
- | EventSessionNextToolError
+ | EventSessionNextToolFailed
| EventSessionNextRetried
| EventSessionNextCompactionStarted
| EventSessionNextCompactionDelta
@@ -823,6 +824,7 @@ export type GlobalEvent = {
| EventSessionNextShellEnded
| EventSessionNextStepStarted
| EventSessionNextStepEnded
+ | EventSessionNextStepFailed
| EventSessionNextTextStarted
| EventSessionNextTextDelta
| EventSessionNextTextEnded
@@ -835,7 +837,7 @@ export type GlobalEvent = {
| EventSessionNextToolCalled
| EventSessionNextToolProgress
| EventSessionNextToolSuccess
- | EventSessionNextToolError
+ | EventSessionNextToolFailed
| EventSessionNextRetried
| EventSessionNextCompactionStarted
| EventSessionNextCompactionDelta
@@ -857,6 +859,7 @@ export type GlobalEvent = {
| SyncEventSessionNextShellEnded
| SyncEventSessionNextStepStarted
| SyncEventSessionNextStepEnded
+ | SyncEventSessionNextStepFailed
| SyncEventSessionNextTextStarted
| SyncEventSessionNextTextDelta
| SyncEventSessionNextTextEnded
@@ -869,7 +872,7 @@ export type GlobalEvent = {
| SyncEventSessionNextToolCalled
| SyncEventSessionNextToolProgress
| SyncEventSessionNextToolSuccess
- | SyncEventSessionNextToolError
+ | SyncEventSessionNextToolFailed
| SyncEventSessionNextRetried
| SyncEventSessionNextCompactionStarted
| SyncEventSessionNextCompactionDelta
@@ -1973,6 +1976,22 @@ export type SyncEventSessionNextStepEnded = {
}
}
+export type SyncEventSessionNextStepFailed = {
+ type: "sync"
+ name: "session.next.step.failed.1"
+ id: string
+ seq: number
+ aggregateID: "sessionID"
+ data: {
+ timestamp: number
+ sessionID: string
+ error: {
+ type: string
+ message: string
+ }
+ }
+}
+
export type SyncEventSessionNextTextStarted = {
type: "sync"
name: "session.next.text.started.1"
@@ -2157,9 +2176,9 @@ export type SyncEventSessionNextToolSuccess = {
}
}
-export type SyncEventSessionNextToolError = {
+export type SyncEventSessionNextToolFailed = {
type: "sync"
- name: "session.next.tool.error.1"
+ name: "session.next.tool.failed.1"
id: string
seq: number
aggregateID: "sessionID"
@@ -2710,6 +2729,19 @@ export type EventSessionNextStepEnded = {
}
}
+export type EventSessionNextStepFailed = {
+ id: string
+ type: "session.next.step.failed"
+ properties: {
+ timestamp: number
+ sessionID: string
+ error: {
+ type: string
+ message: string
+ }
+ }
+}
+
export type EventSessionNextTextStarted = {
id: string
type: "session.next.text.started"
@@ -2870,9 +2902,9 @@ export type EventSessionNextToolSuccess = {
}
}
-export type EventSessionNextToolError = {
+export type EventSessionNextToolFailed = {
id: string
- type: "session.next.tool.error"
+ type: "session.next.tool.failed"
properties: {
timestamp: number
sessionID: string
@@ -3162,7 +3194,10 @@ export type SessionMessageAssistant = {
write: number
}
}
- error?: string
+ error?: {
+ type: string
+ message: string
+ }
}
export type SessionMessageCompaction = {