thinkpool-pair 0.7.92 → 0.7.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bridge.mjs +13 -3
- package/package.json +1 -1
- package/session-store.mjs +16 -0
package/bridge.mjs
CHANGED
|
@@ -41,7 +41,7 @@ import { z } from 'zod'
|
|
|
41
41
|
import { startClaudeSession } from './claude-session.mjs'
|
|
42
42
|
import { formatPeek, PEEK, siblingsOf, resolveSibling, crossPostDecision, CROSSPOST, spawnDecision } from './cross-terminal.mjs'
|
|
43
43
|
import { turnInFlight } from './update-gate.mjs'
|
|
44
|
-
import { saveSession, flushSession, deleteSession, loadAll, canResume, loadPtyId, savePtyId, loadNames, saveNames, appendDurableEvents, seedDurableEvents, readDurablePage } from './session-store.mjs'
|
|
44
|
+
import { saveSession, flushSession, deleteSession, loadAll, canResume, loadPtyId, savePtyId, loadNames, saveNames, appendDurableEvents, seedDurableEvents, readDurablePage, readDurableOldestSeq } from './session-store.mjs'
|
|
45
45
|
import { stampEvent, makeSeqCounter, maxSeq, seqable, capReplayEvents, chunkReplayEvents, boundEventForBroadcast, inlineImageBlocks } from './event-id.mjs'
|
|
46
46
|
import { makeThrottledTrack } from './presence.mjs'
|
|
47
47
|
|
|
@@ -579,7 +579,7 @@ const announce = () =>
|
|
|
579
579
|
// the chosen label instead of defaulting to Normal / unnamed. `spawned` (set
|
|
580
580
|
// when an agent Dispatched this lane via spawn_terminal) lets the client keep
|
|
581
581
|
// it OUT of the top tab strip and show it only in the Ensemble menu.
|
|
582
|
-
...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', alive: true, commands: s.commands, mode: s.mode || undefined, name: termNames[id] || undefined, ...(s.spawnedBy ? { spawned: true, spawnedBy: s.spawnedBy } : {}) })),
|
|
582
|
+
...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', alive: true, commands: s.commands, mode: s.mode || undefined, name: termNames[id] || undefined, ...(s.archiveOldestSeq != null ? { oldestSeq: s.archiveOldestSeq } : {}), ...(s.spawnedBy ? { spawned: true, spawnedBy: s.spawnedBy } : {}) })),
|
|
583
583
|
],
|
|
584
584
|
})
|
|
585
585
|
|
|
@@ -871,7 +871,13 @@ function pushLog(entry, evt) {
|
|
|
871
871
|
// is skipped. entry.id/entry.room are stamped in openStructured; a PTY entry (no id)
|
|
872
872
|
// is a no-op. This is the single place every new event passes through, so nothing is
|
|
873
873
|
// missed and reloaded events (which don't ride pushLog) never double-append.
|
|
874
|
-
if (entry.id && seqable(evt))
|
|
874
|
+
if (entry.id && seqable(evt)) {
|
|
875
|
+
appendDurableEvents(entry.room, entry.id, [evt])
|
|
876
|
+
// First archived event sets the floor for a fresh lane (no seed ran). Lets the
|
|
877
|
+
// client correctly withhold "load earlier" until eviction pushes the loaded window
|
|
878
|
+
// above this floor — a new lane's first turn at seq>1 is NOT older-history.
|
|
879
|
+
if (entry.archiveOldestSeq == null) entry.archiveOldestSeq = evt.seq
|
|
880
|
+
}
|
|
875
881
|
if (entry.log.length > STRUCTURED_LOG_MAX) entry.log.shift()
|
|
876
882
|
return evt
|
|
877
883
|
}
|
|
@@ -914,6 +920,10 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy }) {
|
|
|
914
920
|
entry.id = id
|
|
915
921
|
entry.room = room
|
|
916
922
|
seedDurableEvents(room, id, entry.log.filter((e) => seqable(e)))
|
|
923
|
+
// Archive floor — the lowest seq durably stored. Drives the client's "load earlier"
|
|
924
|
+
// offer (show only when its oldest loaded seq is BELOW this). null on a brand-new lane
|
|
925
|
+
// with no archive yet; pushLog stamps it from the first appended event.
|
|
926
|
+
entry.archiveOldestSeq = readDurableOldestSeq(room, id)
|
|
917
927
|
sessions.set(id, entry)
|
|
918
928
|
// Close an interrupted turn so a restored terminal comes back IDLE-with-history
|
|
919
929
|
// instead of stuck "thinking" (see restoredTurnOpen). Idempotent: the closer is
|
package/package.json
CHANGED
package/session-store.mjs
CHANGED
|
@@ -91,6 +91,22 @@ export function seedDurableEvents(room, id, events) {
|
|
|
91
91
|
} catch { /* noop */ }
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// The archive FLOOR: the lowest transcript seq durably stored for this terminal.
|
|
95
|
+
// The client compares its oldest loaded seq against this — only OFFER "load earlier"
|
|
96
|
+
// when there genuinely are older events in the archive (vs a fresh lane whose first
|
|
97
|
+
// turn happens to land at seq > 1, or an evicted-but-unarchived gap). null = no archive.
|
|
98
|
+
export function readDurableOldestSeq(room, id) {
|
|
99
|
+
try {
|
|
100
|
+
const f = eventsLog(room, id)
|
|
101
|
+
if (!fs.existsSync(f)) return null
|
|
102
|
+
for (const line of fs.readFileSync(f, 'utf8').split('\n')) {
|
|
103
|
+
if (!line) continue
|
|
104
|
+
try { const e = JSON.parse(line); if (e && e.seq != null) return e.seq } catch { /* skip torn line */ }
|
|
105
|
+
}
|
|
106
|
+
return null
|
|
107
|
+
} catch { return null }
|
|
108
|
+
}
|
|
109
|
+
|
|
94
110
|
// Read a backward page: events with transcript seq < beforeSeq (newest `limit` of
|
|
95
111
|
// them, no-seq chrome interleaved by file order preserved). Returns { events, hasMore }.
|
|
96
112
|
// beforeSeq null → the newest `limit` events (the tail). hasMore = there is older still.
|