From 79b54473fb1260d34599a04bdabca5aec4a776bb Mon Sep 17 00:00:00 2001 From: Jeff Dafoe Date: Wed, 10 Jun 2026 15:43:11 -0400 Subject: [PATCH] ZBBS-WORK-391: until param on /v1/sim/raw-turns (walk-back cursor) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route returns newest-first with no offset pagination, so an older episode buried behind 50+ newer turns was unreachable (hit investigating the Prudence over-buy: the 06-07 farm episode sat behind a wall of 06-08/09 turns). until is an EXCLUSIVE created_at upper bound mirroring the existing since block — bound param, validated timestamp; exclusive so the oldest row's created_at can be passed verbatim as a cursor without repeating the boundary row. The salem umbilical /turns proxy passes it through (shipped in salem #390). --- node/api/src/routes/sim.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/node/api/src/routes/sim.js b/node/api/src/routes/sim.js index dbc7d0fa..d08f4869 100644 --- a/node/api/src/routes/sim.js +++ b/node/api/src/routes/sim.js @@ -66,6 +66,12 @@ router.post('/sim/conversation-day', apiRoute('sim', 'conversation_day', async ( // shared "salem-vendor"/"salem-visitor"); 1:1 for a stateful NPC, // many-to-one for a shared VA // since — ISO timestamp lower bound on created_at +// until — EXCLUSIVE upper bound on created_at (strictly earlier-than). +// The route returns the newest rows first with no offset +// pagination, so without this an older episode buried behind 50+ +// newer turns is unreachable. Exclusive so it works as a cursor: +// pass the oldest row's created_at verbatim to fetch the next +// page back without repeating the boundary row. // status — 'success' | 'error' // limit — default 5, capped at 50 (each turn carries ~5k+14k chars of // prompt, so the default stays small; raise it deliberately) @@ -118,6 +124,15 @@ router.post('/sim/raw-turns', requirePerm('plugins', 'administer'), apiRoute('si params.push(since.toISOString()); } + if (body.until !== undefined && body.until !== null && body.until !== '') { + const until = new Date(body.until); + if (Number.isNaN(until.getTime())) { + return res.status(400).json({ error: { code: 'BAD_REQUEST', message: 'until must be a valid timestamp' } }); + } + conditions.push(`c.created_at < $${idx++}`); + params.push(until.toISOString()); + } + if (body.status !== undefined && body.status !== null && body.status !== '') { if (body.status !== 'success' && body.status !== 'error') { return res.status(400).json({ error: { code: 'BAD_REQUEST', message: "status must be 'success' or 'error'" } });