Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/opencode/src/server/routes/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export const SessionRoutes = lazy(() =>
}),
),
async (c) => {
SessionPrompt.cancel(c.req.valid("param").sessionID)
SessionPrompt.cancel(c.req.valid("param").sessionID, "aborted")
return c.json(true)
},
)
Expand Down
5 changes: 4 additions & 1 deletion packages/opencode/src/session/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,10 @@ export namespace SessionProcessor {
sessionID: input.assistantMessage.sessionID,
error: input.assistantMessage.error,
})
SessionStatus.set(input.sessionID, { type: "idle" })
SessionStatus.set(input.sessionID, {
type: "idle",
reason: error.name === "MessageAbortedError" ? "aborted" : "error",
})
}
}
if (snapshot) {
Expand Down
33 changes: 26 additions & 7 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,21 @@ export namespace SessionPrompt {
return s[sessionID].abort.signal
}

export function cancel(sessionID: string) {
export function cancel(sessionID: string, reason: SessionStatus.IdleReason = "aborted") {
log.info("cancel", { sessionID })
const idle = () => {
if (SessionStatus.get(sessionID).type === "idle") return
SessionStatus.set(sessionID, { type: "idle", reason })
}
const s = state()
const match = s[sessionID]
if (!match) {
SessionStatus.set(sessionID, { type: "idle" })
idle()
return
}
match.abort.abort()
delete s[sessionID]
SessionStatus.set(sessionID, { type: "idle" })
idle()
return
}

Expand All @@ -283,7 +287,8 @@ export namespace SessionPrompt {
})
}

using _ = defer(() => cancel(sessionID))
let reason: SessionStatus.IdleReason = "completed"
using _ = defer(() => cancel(sessionID, reason))

// Structured output state
// Note: On session resumption, state is reset but outputFormat is preserved
Expand All @@ -295,7 +300,10 @@ export namespace SessionPrompt {
while (true) {
SessionStatus.set(sessionID, { type: "busy" })
log.info("loop", { step, sessionID })
if (abort.aborted) break
if (abort.aborted) {
reason = "aborted"
break
}
let msgs = await MessageV2.filterCompacted(MessageV2.stream(sessionID))

let lastUser: MessageV2.User | undefined
Expand Down Expand Up @@ -536,7 +544,10 @@ export namespace SessionPrompt {
auto: task.auto,
overflow: task.overflow,
})
if (result === "stop") break
if (result === "stop") {
reason = abort.aborted ? "aborted" : "completed"
break
}
continue
}

Expand Down Expand Up @@ -698,11 +709,19 @@ export namespace SessionPrompt {
retries: 0,
}).toObject()
await Session.updateMessage(processor.message)
reason = "error"
break
}
}

if (result === "stop") break
if (result === "stop") {
if (processor.message.error?.name === "MessageAbortedError") {
reason = "aborted"
} else if (processor.message.error) {
reason = "error"
}
break
}
if (result === "compact") {
await SessionCompaction.create({
sessionID,
Expand Down
12 changes: 9 additions & 3 deletions packages/opencode/src/session/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import { Instance } from "@/project/instance"
import z from "zod"

export namespace SessionStatus {
export const IdleReason = z.enum(["completed", "aborted", "error"])
export type IdleReason = z.infer<typeof IdleReason>

export const Info = z
.union([
z.object({
type: z.literal("idle"),
reason: IdleReason.optional(),
}),
z.object({
type: z.literal("retry"),
Expand Down Expand Up @@ -65,9 +69,11 @@ export namespace SessionStatus {
})
if (status.type === "idle") {
// deprecated
Bus.publish(Event.Idle, {
sessionID,
})
if (!status.reason || status.reason === "completed") {
Bus.publish(Event.Idle, {
sessionID,
})
}
delete state()[sessionID]
return
}
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/tool/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const TaskTool = Tool.define("task", async (ctx) => {
const messageID = Identifier.ascending("message")

function cancel() {
SessionPrompt.cancel(session.id)
SessionPrompt.cancel(session.id, "aborted")
}
ctx.abort.addEventListener("abort", cancel)
using _ = defer(() => ctx.abort.removeEventListener("abort", cancel))
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/js/src/gen/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ export type EventPermissionReplied = {
export type SessionStatus =
| {
type: "idle"
reason?: "completed" | "aborted" | "error"
}
| {
type: "retry"
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/js/src/v2/gen/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ export type EventPermissionReplied = {
export type SessionStatus =
| {
type: "idle"
reason?: "completed" | "aborted" | "error"
}
| {
type: "retry"
Expand Down
Loading