Skip to content

Commit f6fef3b

Browse files
committed
fix(session): auto-continue when model stops with reasoning-only response
Detect pattern where the model finishes with 'stop' after only producing reasoning text, without making any tool calls. Inject a synthetic user message to prompt the model to continue, allowing up to 10 auto-continuations per session to prevent infinite loops.
1 parent a75d4ad commit f6fef3b

1 file changed

Lines changed: 43 additions & 0 deletions

File tree

packages/opencode/src/session/prompt.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,7 @@ export const layer = Layer.effect(
11361136
const ctx = yield* InstanceState.context
11371137
let structured: unknown
11381138
let step = 0
1139+
let autoContinueCount = 0
11391140
const session = yield* sessions.get(sessionID).pipe(Effect.orDie)
11401141

11411142
while (true) {
@@ -1178,6 +1179,48 @@ export const layer = Layer.effect(
11781179
callID: orphan.callID,
11791180
})
11801181
}
1182+
// AUTO-CONTINUE: Detect reasoning-only + stop without any text response or tool calls.
1183+
// This catches cases where the model stops mid-thought without producing output.
1184+
const isReasoningOnly = lastAssistantMsg && lastAssistant.finish === "stop" && lastAssistantMsg.parts.length > 0 &&
1185+
lastAssistantMsg.parts.every(
1186+
(p) => p.type === "reasoning" || p.type === "step-start" || p.type === "step-finish",
1187+
)
1188+
1189+
if (isReasoningOnly && autoContinueCount < 10) {
1190+
autoContinueCount++
1191+
yield* Effect.logInfo("auto-continue: reasoning-only stop, injecting continue message", {
1192+
"session.id": sessionID,
1193+
attempt: autoContinueCount,
1194+
})
1195+
const contMsgID = MessageID.ascending()
1196+
const contMsg = yield* sessions.updateMessage({
1197+
id: contMsgID,
1198+
role: "user",
1199+
sessionID,
1200+
time: { created: Date.now() },
1201+
agent: lastUser.agent,
1202+
model: lastUser.model,
1203+
})
1204+
yield* sessions.updatePart({
1205+
id: PartID.ascending(),
1206+
messageID: contMsg.id,
1207+
sessionID,
1208+
type: "text",
1209+
metadata: { auto_continue: true },
1210+
synthetic: true,
1211+
text: "[Auto-continue] Your response was cut off. Please continue from where you left off.",
1212+
time: {
1213+
start: Date.now(),
1214+
end: Date.now(),
1215+
},
1216+
})
1217+
continue
1218+
} else if (isReasoningOnly) {
1219+
yield* Effect.logWarning("auto-continue: max attempts (10) reached, exiting loop", {
1220+
"session.id": sessionID,
1221+
})
1222+
}
1223+
11811224
yield* Effect.logInfo("exiting loop", { "session.id": sessionID })
11821225
break
11831226
}

0 commit comments

Comments
 (0)