hermes-agent/apps/desktop/src/store/compaction.ts
Brooklyn Nicholson 003ff53fb4 fix(desktop): scope composer and transcript state to their own session
`$activeSessionId` only ever holds the primary chat's session, but surfaces
that render once per transcript were reading it as if it meant "the session
on screen." A preview produced inside a session tile was recorded under the
main chat's key and surfaced in the main chat's composer, which is what
prompted this.

The tool row now records under its own `SessionView`, and the same fix
applies to the other readers of that atom that render per surface:
attachment pills and inline preview links resolve relative paths against
their session's cwd, composer voice and auto-speak read and subscribe to
their own transcript, and the thread's compaction label, prompt-wait gate
and turn timer follow the session that mounted them. `ComposerScope` now
carries a `$messages` atom rather than a read closure so both the
imperative read and the subscription come from one place.
2026-07-27 17:55:46 -05:00

37 lines
1.1 KiB
TypeScript

import { atom, computed } from 'nanostores'
// Per-session flag while auto-compaction runs mid-turn. Without it the
// transcript looks like it reset; per-session so a background chat can't
// clobber the foreground view.
const keyFor = (sessionId: string | null | undefined): string => sessionId ?? ''
export const $compactingSessions = atom<Record<string, true>>({})
/** Is `sessionId` compacting? Per-session because a transcript may be a tile,
* and a tile must never wear the primary chat's compaction state. */
export function sessionCompacting(sessionId: null | string) {
return computed($compactingSessions, sessions => keyFor(sessionId) in sessions)
}
export function setSessionCompacting(sessionId: string | null | undefined, active: boolean): void {
const key = keyFor(sessionId)
const sessions = $compactingSessions.get()
if (active) {
if (key in sessions) {
return
}
$compactingSessions.set({ ...sessions, [key]: true })
return
}
if (!(key in sessions)) {
return
}
const next = { ...sessions }
delete next[key]
$compactingSessions.set(next)
}