Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -539,7 +539,7 @@ export const SessionRoutes = lazy(() =>
},
auto: body.auto,
})
await SessionPrompt.loop(sessionID)
await SessionPrompt.loop({ sessionID })
return c.json(true)
},
)
Expand Down
33 changes: 29 additions & 4 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export namespace SessionPrompt {
return message
}

return loop(input.sessionID)
return loop({sessionID: input.sessionID})
})

export async function resolvePromptParts(template: string): Promise<PromptInput["parts"]> {
Expand Down Expand Up @@ -242,6 +242,13 @@ export namespace SessionPrompt {
return controller.signal
}

function resume(sessionID: string) {
const s = state()
if (!s[sessionID]) return

return s[sessionID].abort.signal
}

export function cancel(sessionID: string) {
log.info("cancel", { sessionID })
const s = state()
Expand All @@ -256,8 +263,14 @@ export namespace SessionPrompt {
return
}

export const loop = fn(Identifier.schema("session"), async (sessionID) => {
const abort = start(sessionID)
export const LoopInput = z.object({
sessionID: Identifier.schema("session"),
resume_existing: z.boolean().optional(),
})
export const loop = fn(LoopInput, async (input) => {
const { sessionID, resume_existing } = input

const abort = resume_existing ? resume(sessionID) : start(sessionID)
if (!abort) {
return new Promise<MessageV2.WithParts>((resolve, reject) => {
const callbacks = state()[sessionID].callbacks
Expand Down Expand Up @@ -1354,7 +1367,19 @@ NOTE: At any point in time through this workflow you should feel free to ask the
if (!abort) {
throw new Session.BusyError(input.sessionID)
}
using _ = defer(() => cancel(input.sessionID))

using _ = defer(() => {
// If no queued callbacks, cancel (the default)
const callbacks = state()[input.sessionID]?.callbacks ?? []
if (callbacks.length === 0) {
cancel(input.sessionID)
} else {
// Otherwise, trigger the session loop to process queued items
loop({sessionID: input.sessionID, resume_existing: true}).catch((error) => {
log.error("session loop failed to resume after shell command", { sessionID: input.sessionID, error })
})
}
})

const session = await Session.get(input.sessionID)
if (session.revert) {
Expand Down