Compacts a quiet session when cache-keepalive reaches its 55-minute mark

hooks/register.ts 59 lines1import type { EngineInterface, ModelUsage, Register } from 'claude-code'
2
3// cache-keepalive decides at its mark (claude/cache-keepalive/README.md); this mod reports quiet + runs the compaction.
4const POLL_MS = 30_000
5const KEEPER = 'http://127.0.0.1:8318'
6// Quiet = no turn running (an AskUserQuestion wait included) + the last main Stop left no background task or cron.
7const running = new Set<string>()
8let background = false
9let compacting = false
10
11async function report($: EngineInterface, path: string, body: object): Promise<unknown> {
12 const url = `${(await $.env.get('CACHE_KEEPALIVE_URL')) || KEEPER}/_keepalive/${path}`
13 const r = await $.http.fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
14 return r.ok ? JSON.parse(r.text) : undefined
15}
16
17async function poll($: EngineInterface): Promise<void> {
18 const session = await $.session.id()
19 const quiet = running.size === 0 && !background && !compacting
20 const asked = (await report($, 'poll', { session, quiet })) as { compact?: unknown } | undefined
21 if (asked?.compact !== true) return
22 let reason = ''
23 let usage: ModelUsage | undefined
24 compacting = true
25 try {
26 const r = await $.session.compact()
27 if (r.skip === undefined) usage = r.usage
28 else reason = r.skip
29 } catch (err) {
30 reason = String(err)
31 } finally {
32 compacting = false
33 }
34 await report($, 'compacted', { session, ok: !reason, reason, usage })
35}
36
37export const register: Register = on => {
38 on('session.start', async ($, e, next) => {
39 const r = await next(e)
40 if (e.isInteractive) $.clock.every(POLL_MS, () => void poll($).catch(() => {}))
41 return r
42 })
43
44 on('turn.start', (_$, e, next) => {
45 running.add(e.turnId)
46 return next(e)
47 })
48
49 on('turn.complete', (_$, e, next) => {
50 running.delete(e.turnId)
51 return next(e)
52 })
53
54 on('classic.Stop', (_$, e, next) => {
55 if (e.agent_id === undefined) background = !!(e.background_tasks?.length || e.session_crons?.length)
56 return next(e)
57 })
58}
59