Skip to content

Commit 7d8137b

Browse files
Ismet Ufuk AltinokIsmet Ufuk Altinok
authored andcommitted
test(e2e): query opencode session DB during turn 4 hang to see message info.finish
Previous PROBE dumped wire messages, but the finish field lives on msg.info, not in the wire payload. To diagnose why opencode's break condition stays false, we need to see what's actually persisted in opencode's session DB. This dump opens opencode's messages.db read-only, queries the latest 6 messages by ID, and logs role/id-prefix/finish/summary. The newest assistant's finish field should be 'end_turn' (mock returns that with stop_reason: end_turn). If it shows as null or 'tool-calls' or the newest assistant is a still-streaming one, that tells us exactly why OpenCode's loop doesn't break. Diagnostic only; production code unchanged. Test passes locally in 4s.
1 parent f62d544 commit 7d8137b

1 file changed

Lines changed: 19 additions & 14 deletions

File tree

packages/e2e-tests/tests/long-running-session.test.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -256,26 +256,31 @@ async function send(sessionId: string, prompt: string, text: string, usage: Mock
256256
// [LR-DIAG] console.error flushes immediately on CI even when console.log is buffered
257257
console.error(`[LR-DIAG] turn ${turn} START prompt="${prompt.slice(0, 60)}" mockReqs=${reqsBefore}`);
258258

259-
// OC-DIAG: every 50 mock requests, log opencode's view of session state
260-
// to diagnose the CI loop. We tap the mock to count and dump.
259+
// OC-DIAG: every 50 mock requests, query opencode's session DB to see
260+
// what its filterCompacted/latest() actually returns. This tells us why
261+
// OpenCode's loop break condition isn't firing.
261262
let lastDumpAt = 0;
262263
const dumpTimer = setInterval(() => {
263264
const now = Date.now();
264265
const reqs = h.mock.requests().length;
265266
if (reqs > reqsBefore + 50 && now - lastDumpAt > 5000) {
266267
lastDumpAt = now;
267-
const recent = h.mock.requests().slice(-3);
268-
for (const [i, r] of recent.entries()) {
269-
const body = r.body as {
270-
messages?: Array<{ role?: string; content?: unknown; id?: string }>;
271-
};
272-
const msgs = body.messages ?? [];
273-
const lastTwo = msgs.slice(-2).map((m) => {
274-
const role = m?.role ?? "?";
275-
const contentPreview = JSON.stringify(m?.content ?? "").slice(0, 100);
276-
return `${role}|${contentPreview}`;
277-
});
278-
console.error(`[LR-DIAG] PROBE turn=${turn} mockReqs=${reqs} req[-${3 - i}] msgs=${msgs.length} lastTwo=${JSON.stringify(lastTwo)}`);
268+
// Open opencode's session DB directly (Database is imported at top of file)
269+
try {
270+
const ocDbPath = join(h.opencode.env.dataDir, "opencode", "storage", "session", "messages.db");
271+
const ocDb = new Database(ocDbPath, { readonly: true });
272+
ocDb.query("PRAGMA busy_timeout = 1000").run();
273+
// Get latest messages in the session
274+
const rows = ocDb.prepare(
275+
"SELECT id, json_extract(data, '$.role') AS role, json_extract(data, '$.finish') AS finish, json_extract(data, '$.summary') AS summary FROM message WHERE session_id = ? ORDER BY id DESC LIMIT 6",
276+
).all(sessionId) as Array<{ id: string; role: string; finish: string | null; summary: number | null }>;
277+
ocDb.close();
278+
const stateStr = rows
279+
.map((r) => `${r.role}/${r.id.slice(0, 16)}/finish=${r.finish ?? "null"}/summary=${r.summary ?? "null"}`)
280+
.join(" ");
281+
console.error(`[LR-DIAG] OC-STATE turn=${turn} mockReqs=${reqs} topMsgs(newest first): ${stateStr}`);
282+
} catch (e) {
283+
console.error(`[LR-DIAG] OC-STATE turn=${turn} mockReqs=${reqs} DB read failed: ${e instanceof Error ? e.message : String(e)}`);
279284
}
280285
}
281286
}, 2000);

0 commit comments

Comments
 (0)