Skip to content

Commit 97685d5

Browse files
committed
core: enable real-time tool progress updates during execution
Add session.next.tool.progress event so users can see live status from long-running tools instead of waiting for completion. Consolidate tool state metadata into a unified 'details' field for consistent display.
1 parent 91938e2 commit 97685d5

4 files changed

Lines changed: 51 additions & 47 deletions

File tree

packages/opencode/src/session/processor.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,6 @@ export const layer: Layer.Layer<
380380
SyncEvent.run(SessionEvent.Tool.Success.Sync, {
381381
sessionID: ctx.sessionID,
382382
callID: value.toolCallId,
383-
title: value.output.title,
384383
output: value.output.output,
385384
attachments: value.output.attachments?.map((item: MessageV2.FilePart) => ({
386385
uri: item.url,
@@ -396,9 +395,9 @@ export const layer: Layer.Layer<
396395
}
397396
: {}),
398397
})),
398+
details: value.output.metadata,
399399
provider: {
400400
executed: toolCall?.part.metadata?.providerExecuted === true,
401-
metadata: value.output.metadata,
402401
},
403402
timestamp: DateTime.makeUnsafe(Date.now()),
404403
})

packages/opencode/src/v2/session-entry-stepper.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ export function stepWith<Result>(adapter: Adapter<Result>, event: SessionEvent.E
169169
)
170170
}
171171
},
172+
"session.next.tool.progress": (event) => {
173+
if (currentAssistant) {
174+
adapter.updateAssistant(
175+
produce(currentAssistant, (draft) => {
176+
const match = latestTool(draft, event.data.callID)
177+
if (match && match.state.status === "running") match.state.details = event.data.details
178+
}),
179+
)
180+
}
181+
},
172182
"session.next.tool.success": (event) => {
173183
if (currentAssistant) {
174184
adapter.updateAssistant(
@@ -179,8 +189,7 @@ export function stepWith<Result>(adapter: Adapter<Result>, event: SessionEvent.E
179189
status: "completed",
180190
input: match.state.input,
181191
output: event.data.output ?? "",
182-
title: event.data.title,
183-
metadata: event.metadata ?? {},
192+
details: event.data.details,
184193
attachments: [...(event.data.attachments ?? [])],
185194
}
186195
}
@@ -198,7 +207,6 @@ export function stepWith<Result>(adapter: Adapter<Result>, event: SessionEvent.E
198207
status: "error",
199208
error: event.data.error,
200209
input: match.state.input,
201-
metadata: event.metadata ?? {},
202210
}
203211
}
204212
}),

packages/opencode/src/v2/session-entry.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,22 @@ export class ToolStatePending extends Schema.Class<ToolStatePending>("Session.En
6262
export class ToolStateRunning extends Schema.Class<ToolStateRunning>("Session.Entry.ToolState.Running")({
6363
status: Schema.Literal("running"),
6464
input: Schema.Record(Schema.String, Schema.Unknown),
65-
title: Schema.String.pipe(Schema.optional),
66-
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
65+
details: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
6766
}) {}
6867

6968
export class ToolStateCompleted extends Schema.Class<ToolStateCompleted>("Session.Entry.ToolState.Completed")({
7069
status: Schema.Literal("completed"),
7170
input: Schema.Record(Schema.String, Schema.Unknown),
7271
output: Schema.String,
73-
title: Schema.String,
74-
metadata: Schema.Record(Schema.String, Schema.Unknown),
72+
details: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
7573
attachments: SessionEvent.FileAttachment.pipe(Schema.Array, Schema.optional),
7674
}) {}
7775

7876
export class ToolStateError extends Schema.Class<ToolStateError>("Session.Entry.ToolState.Error")({
7977
status: Schema.Literal("error"),
8078
input: Schema.Record(Schema.String, Schema.Unknown),
8179
error: Schema.String,
82-
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
80+
details: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
8381
}) {}
8482

8583
export const ToolState = Schema.Union([ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError]).pipe(

packages/opencode/src/v2/session-event.ts

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ export const Source = Schema.Struct({
1616
})
1717
export type Source = Schema.Schema.Type<typeof Source>
1818

19+
const Base = {
20+
timestamp: Schema.DateTimeUtcFromMillis,
21+
sessionID: SessionID,
22+
}
23+
1924
export const Prompted = Event.define({
2025
type: "session.next.prompted",
2126
aggregate: "sessionID",
2227
schema: {
23-
timestamp: Schema.DateTimeUtcFromMillis,
24-
sessionID: SessionID,
28+
...Base,
2529
prompt: Prompt,
2630
},
2731
})
@@ -31,8 +35,7 @@ export const Synthetic = Event.define({
3135
type: "session.next.synthetic",
3236
aggregate: "sessionID",
3337
schema: {
34-
timestamp: Schema.DateTimeUtcFromMillis,
35-
sessionID: SessionID,
38+
...Base,
3639
text: Schema.String,
3740
},
3841
})
@@ -43,8 +46,7 @@ export namespace Step {
4346
type: "session.next.step.started",
4447
aggregate: "sessionID",
4548
schema: {
46-
timestamp: Schema.DateTimeUtcFromMillis,
47-
sessionID: SessionID,
49+
...Base,
4850
model: Schema.Struct({
4951
id: Schema.String,
5052
providerID: Schema.String,
@@ -58,8 +60,7 @@ export namespace Step {
5860
type: "session.next.step.ended",
5961
aggregate: "sessionID",
6062
schema: {
61-
timestamp: Schema.DateTimeUtcFromMillis,
62-
sessionID: SessionID,
63+
...Base,
6364
reason: Schema.String,
6465
cost: Schema.Number,
6566
tokens: Schema.Struct({
@@ -81,8 +82,7 @@ export namespace Text {
8182
type: "session.next.text.started",
8283
aggregate: "sessionID",
8384
schema: {
84-
timestamp: Schema.DateTimeUtcFromMillis,
85-
sessionID: SessionID,
85+
...Base,
8686
},
8787
})
8888
export type Started = Schema.Schema.Type<typeof Started>
@@ -91,8 +91,7 @@ export namespace Text {
9191
type: "session.next.text.delta",
9292
aggregate: "sessionID",
9393
schema: {
94-
timestamp: Schema.DateTimeUtcFromMillis,
95-
sessionID: SessionID,
94+
...Base,
9695
delta: Schema.String,
9796
},
9897
})
@@ -102,8 +101,7 @@ export namespace Text {
102101
type: "session.next.text.ended",
103102
aggregate: "sessionID",
104103
schema: {
105-
timestamp: Schema.DateTimeUtcFromMillis,
106-
sessionID: SessionID,
104+
...Base,
107105
text: Schema.String,
108106
},
109107
})
@@ -115,8 +113,7 @@ export namespace Reasoning {
115113
type: "session.next.reasoning.started",
116114
aggregate: "sessionID",
117115
schema: {
118-
timestamp: Schema.DateTimeUtcFromMillis,
119-
sessionID: SessionID,
116+
...Base,
120117
reasoningID: Schema.String,
121118
},
122119
})
@@ -126,8 +123,7 @@ export namespace Reasoning {
126123
type: "session.next.reasoning.delta",
127124
aggregate: "sessionID",
128125
schema: {
129-
timestamp: Schema.DateTimeUtcFromMillis,
130-
sessionID: SessionID,
126+
...Base,
131127
reasoningID: Schema.String,
132128
delta: Schema.String,
133129
},
@@ -138,8 +134,7 @@ export namespace Reasoning {
138134
type: "session.next.reasoning.ended",
139135
aggregate: "sessionID",
140136
schema: {
141-
timestamp: Schema.DateTimeUtcFromMillis,
142-
sessionID: SessionID,
137+
...Base,
143138
reasoningID: Schema.String,
144139
text: Schema.String,
145140
},
@@ -153,8 +148,7 @@ export namespace Tool {
153148
type: "session.next.tool.input.started",
154149
aggregate: "sessionID",
155150
schema: {
156-
timestamp: Schema.DateTimeUtcFromMillis,
157-
sessionID: SessionID,
151+
...Base,
158152
callID: Schema.String,
159153
name: Schema.String,
160154
},
@@ -165,8 +159,7 @@ export namespace Tool {
165159
type: "session.next.tool.input.delta",
166160
aggregate: "sessionID",
167161
schema: {
168-
timestamp: Schema.DateTimeUtcFromMillis,
169-
sessionID: SessionID,
162+
...Base,
170163
callID: Schema.String,
171164
delta: Schema.String,
172165
},
@@ -177,8 +170,7 @@ export namespace Tool {
177170
type: "session.next.tool.input.ended",
178171
aggregate: "sessionID",
179172
schema: {
180-
timestamp: Schema.DateTimeUtcFromMillis,
181-
sessionID: SessionID,
173+
...Base,
182174
callID: Schema.String,
183175
text: Schema.String,
184176
},
@@ -190,8 +182,7 @@ export namespace Tool {
190182
type: "session.next.tool.called",
191183
aggregate: "sessionID",
192184
schema: {
193-
timestamp: Schema.DateTimeUtcFromMillis,
194-
sessionID: SessionID,
185+
...Base,
195186
callID: Schema.String,
196187
tool: Schema.String,
197188
input: Schema.Record(Schema.String, Schema.Unknown),
@@ -203,16 +194,26 @@ export namespace Tool {
203194
})
204195
export type Called = Schema.Schema.Type<typeof Called>
205196

197+
export const Progress = Event.define({
198+
type: "session.next.tool.progress",
199+
aggregate: "sessionID",
200+
schema: {
201+
...Base,
202+
callID: Schema.String,
203+
details: Schema.Record(Schema.String, Schema.Unknown),
204+
},
205+
})
206+
export type Progress = Schema.Schema.Type<typeof Progress>
207+
206208
export const Success = Event.define({
207209
type: "session.next.tool.success",
208210
aggregate: "sessionID",
209211
schema: {
210-
timestamp: Schema.DateTimeUtcFromMillis,
211-
sessionID: SessionID,
212+
...Base,
212213
callID: Schema.String,
213-
title: Schema.String,
214214
output: Schema.String.pipe(Schema.optional),
215215
attachments: Schema.Array(FileAttachment).pipe(Schema.optional),
216+
details: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
216217
provider: Schema.Struct({
217218
executed: Schema.Boolean,
218219
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
@@ -225,8 +226,7 @@ export namespace Tool {
225226
type: "session.next.tool.error",
226227
aggregate: "sessionID",
227228
schema: {
228-
timestamp: Schema.DateTimeUtcFromMillis,
229-
sessionID: SessionID,
229+
...Base,
230230
callID: Schema.String,
231231
error: Schema.String,
232232
provider: Schema.Struct({
@@ -254,8 +254,7 @@ export const Retried = Event.define({
254254
type: "session.next.retried",
255255
aggregate: "sessionID",
256256
schema: {
257-
timestamp: Schema.DateTimeUtcFromMillis,
258-
sessionID: SessionID,
257+
...Base,
259258
attempt: Schema.Number,
260259
error: RetryError,
261260
},
@@ -266,8 +265,7 @@ export const Compacted = Event.define({
266265
type: "session.next.compacted",
267266
aggregate: "sessionID",
268267
schema: {
269-
timestamp: Schema.DateTimeUtcFromMillis,
270-
sessionID: SessionID,
268+
...Base,
271269
auto: Schema.Boolean,
272270
overflow: Schema.Boolean.pipe(Schema.optional),
273271
},
@@ -287,6 +285,7 @@ export const All = Schema.Union(
287285
Tool.Input.Delta,
288286
Tool.Input.Ended,
289287
Tool.Called,
288+
Tool.Progress,
290289
Tool.Success,
291290
Tool.Error,
292291
Reasoning.Started,

0 commit comments

Comments
 (0)