perf(rate-limits): throttle statusline usage posts to one curl per pane per 15s (#9829)

* perf(rate-limits): throttle statusline usage posts to one curl per pane per 15s

The managed Claude statusline script posted on every statusLine tick
carrying rate_limits — ~3 curl spawns/sec per streaming pane, multiplied
across concurrent panes. The service drops same-value posts inside its
30s dedupe window anyway, so most spawns bought nothing.

Gate the post on a per-pane stamp file: POSIX compares date +%s against
the stamp; Windows uses an all-builtin seconds-of-day parse of %TIME%
(octal-safe, no extra process). Both fail open — unparseable time,
garbage stamp, or midnight wrap posts rather than darkening the live
usage feed — and the stamp only advances when a post actually fires, so
skipped ticks never defer the next one.

Measured: 30 rapid rate-limit ticks spawn 1 curl (was 30).

* perf(rate-limits): keep throttled statusline ticks process-free

Use Claude's monotonic session duration for the POSIX throttle so skipped ticks do not replace curl churn with date churn, while retaining a fail-open date fallback. Key temp files by the stable leaf UUID so path-like or long host tab IDs cannot disable the throttle.

* fix(rate-limits): preserve throttle isolation across upgrades

* fix(rate-limits): reject leading-zero stamp values before arithmetic

All-digits validation still admitted values like 008, which are invalid
octal inside $(( )) — and dash treats that expansion error as fatal, so
the script died before rewriting the stamp and the pane's live usage
feed stayed dark until the file was deleted. Allow-list canonical
decimals (same pattern as the duration parse) on both the stamp and the
computed clock so malformed values fail open to posting.

Verified under dash: the old digits-only check aborts at the arithmetic
(Illegal number: 008); the allow-list survives and posts.

* perf(rate-limits): eliminate POSIX statusline cat churn

* test(rate-limits): exercise overlapping statusline ticks

The stamp check/write is deliberately lock-free (a lock could wedge the
feed closed; fail-open is the contract), so a truly concurrent burst may
post more than once, bounded by overlap width — an exact at-most-one
assertion would be flaky by design. Assert the invariants that do hold:
every overlapping run exits 0, the raced stamp lands valid, and it
throttles the following ticks.
This commit is contained in:
Brennan Benson
2026-07-21 21:00:57 -07:00
parent bc4cc20d21
commit f81413ab21
3 changed files with 357 additions and 7 deletions
+266 -3
View File
@@ -1,4 +1,9 @@
import { spawn } from 'node:child_process'
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS } from '../../shared/claude-statusline-rate-limits'
import { getManagedStatusLineScript } from './statusline-script'
const ORIGINAL_PLATFORM = process.platform
@@ -53,10 +58,12 @@ describe('getManagedStatusLineScript (win32 local)', () => {
it('posts the buffered payload file and deletes it afterwards', () => {
stubPlatform('win32')
const script = getManagedStatusLineScript('local')
// Why: the temp file is per-pane (pane key with ":" mapped to "_") because %RANDOM%
// collides across cmd instances spawned in the same second.
// Why: the stable leaf UUID stays filename-safe even when a host-supplied tab id does not,
// while the delimiter replacement keeps a surviving legacy numeric key valid on Windows.
expect(script).toContain('set "ORCA_STATUSLINE_PANE_ID=%ORCA_PANE_KEY:~-36%"')
expect(script).toContain('set "ORCA_STATUSLINE_PANE_ID=%ORCA_STATUSLINE_PANE_ID::=_%"')
expect(script).toContain(
'set "ORCA_STATUSLINE_PAYLOAD_FILE=%TEMP%\\orca-claude-statusline-%ORCA_PANE_KEY::=_%.tmp"'
'set "ORCA_STATUSLINE_PAYLOAD_FILE=%TEMP%\\orca-claude-statusline-%ORCA_STATUSLINE_PANE_ID%.tmp"'
)
expect(script).toContain('--data-urlencode "payload@%ORCA_STATUSLINE_PAYLOAD_FILE%"')
expect(script).not.toContain('payload@-')
@@ -89,4 +96,260 @@ describe('getManagedStatusLineScript (win32 local)', () => {
expect(paneGuardIndex).toBeLessThan(captureIndex)
expect(script).toContain(':orca_agent_hook_drain_stdin')
})
it('throttles with an all-builtin seconds-of-day stamp that fails open to posting', () => {
stubPlatform('win32')
const script = getManagedStatusLineScript('local')
const captureIndex = script.indexOf('more.com')
const stampIndex = script.indexOf(
'set "ORCA_STATUSLINE_STAMP_FILE=%TEMP%\\orca-claude-statusline-last-%ORCA_STATUSLINE_PANE_ID%.tmp"'
)
const throttleIndex = script.indexOf(
`if %ORCA_STATUSLINE_ELAPSED% GEQ 0 if %ORCA_STATUSLINE_ELAPSED% LSS ${CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS} goto :orca_statusline_cleanup`
)
const findstrIndex = script.indexOf('findstr.exe')
const stampWriteIndex = script.indexOf(
'if defined ORCA_STATUSLINE_NOW (>"%ORCA_STATUSLINE_STAMP_FILE%" echo %ORCA_STATUSLINE_NOW%)'
)
const tokenGuardIndex = script.indexOf('if "%ORCA_AGENT_HOOK_TOKEN%"=="" goto')
const curlIndex = script.indexOf('curl.exe')
// Why: the check precedes findstr so throttled ticks skip that spawn too, but the stamp
// only advances after every post guard passes — skipped ticks must not defer the next post.
expect(stampIndex).toBeGreaterThan(captureIndex)
expect(throttleIndex).toBeGreaterThan(stampIndex)
expect(throttleIndex).toBeLessThan(findstrIndex)
expect(stampWriteIndex).toBeGreaterThan(tokenGuardIndex)
expect(stampWriteIndex).toBeLessThan(curlIndex)
// Fail-open shape: undefined elapsed (unparseable time/stamp) proceeds to the probe.
expect(script).toContain('if not defined ORCA_STATUSLINE_ELAPSED goto :orca_statusline_probe')
expect(script).toContain(
'for /f "delims=0123456789" %%d in ("%ORCA_STATUSLINE_LAST%") do set "ORCA_STATUSLINE_LAST="'
)
expect(script).toContain(
'if defined ORCA_STATUSLINE_NOW if defined ORCA_STATUSLINE_LAST set /a "ORCA_STATUSLINE_ELAPSED=ORCA_STATUSLINE_NOW-ORCA_STATUSLINE_LAST" 2>nul'
)
// cmd parses leading-zero numbers as octal; 1%%x %% 100 defuses 08/09.
expect(script).toContain('(1%%a %% 100)*3600+(1%%b %% 100)*60+(1%%c %% 100)')
expect(script).toContain('set "ORCA_STATUSLINE_TIME=%TIME: =0%"')
})
})
describe('statusline curl throttle (posix)', () => {
it('checks the per-pane stamp after the env guards and before curl', () => {
stubPlatform('darwin')
const script = getManagedStatusLineScript('local')
const envGuardIndex = script.indexOf('-z "$ORCA_AGENT_HOOK_PORT"')
const durationIndex = script.indexOf('"total_duration_ms"')
const stampIndex = script.indexOf('orca-claude-statusline-last-${orca_statusline_pane_id}')
const intervalIndex = script.indexOf(`-lt ${CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS}`)
const curlIndex = script.indexOf('curl -sS')
expect(envGuardIndex).toBeLessThan(stampIndex)
expect(stampIndex).toBeLessThan(durationIndex)
expect(durationIndex).toBeLessThan(intervalIndex)
expect(intervalIndex).toBeLessThan(curlIndex)
// Fail-open shape: non-numeric date output or stamp content must never suppress the post.
// Why: the allow-list (not a mere digits check) matters — leading-zero values like 008 are
// invalid octal inside $(( )) and abort the whole script under dash, wedging the stamp.
expect(script).toContain(
'case "$orca_statusline_now" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_now='
)
expect(script).toContain(
'case "$orca_statusline_last" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_last='
)
})
})
describe.skipIf(process.platform === 'win32')('statusline curl throttle (posix behavioral)', () => {
const LEAF_ID = '00000000-0000-4000-8000-000000000000'
const PANE_KEY = 'tab-1:00000000-0000-4000-8000-000000000000'
const dirs: string[] = []
afterEach(() => {
for (const dir of dirs.splice(0)) {
rmSync(dir, { recursive: true, force: true })
}
})
function rateLimitPayload(durationMs: number): string {
return JSON.stringify({
cost: { total_duration_ms: durationMs },
rate_limits: { five_hour: { used_percentage: 12 } }
})
}
function makeHarness(): {
scriptPath: string
dir: string
curlLog: string
payloadLog: string
dateLog: string
catLog: string
} {
const dir = mkdtempSync(join(tmpdir(), 'orca-statusline-throttle-'))
dirs.push(dir)
const curlLog = join(dir, 'curl.log')
const payloadLog = join(dir, 'payload.log')
const dateLog = join(dir, 'date.log')
const catLog = join(dir, 'cat.log')
const scriptPath = join(dir, 'statusline.sh')
writeFileSync(scriptPath, getManagedStatusLineScript('posix'))
const binDir = join(dir, 'stub-bin')
mkdirSync(binDir, { recursive: true })
writeFileSync(
join(binDir, 'curl'),
`#!/bin/sh\n/bin/cat > "${payloadLog}"\nprintf 'x\\n' >> "${curlLog}"\nexit 0\n`,
{ mode: 0o755 }
)
writeFileSync(
join(binDir, 'date'),
`#!/bin/sh\nprintf 'x\\n' >> "${dateLog}"\nprintf '2000000000\\n'\n`,
{ mode: 0o755 }
)
writeFileSync(join(binDir, 'cat'), `#!/bin/sh\nprintf 'x\\n' >> "${catLog}"\n/bin/cat "$@"\n`, {
mode: 0o755
})
return { scriptPath, dir, curlLog, payloadLog, dateLog, catLog }
}
function runScript(
scriptPath: string,
dir: string,
payload: string,
paneKey = PANE_KEY
): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn('sh', [scriptPath], {
env: {
PATH: `${join(dir, 'stub-bin')}:${process.env.PATH ?? ''}`,
TMPDIR: dir,
ORCA_AGENT_HOOK_PORT: '65535',
ORCA_AGENT_HOOK_TOKEN: 'test-token',
ORCA_PANE_KEY: paneKey
},
stdio: ['pipe', 'ignore', 'pipe']
})
let stderr = ''
child.stderr.on('data', (chunk: Buffer) => {
stderr += chunk.toString()
})
child.on('error', reject)
child.on('close', (code) => {
if (code === 0) {
resolve()
} else {
reject(new Error(`statusline script exited ${code}: ${stderr}`))
}
})
child.stdin.write(payload)
child.stdin.end()
})
}
function lineCount(logPath: string): number {
try {
return readFileSync(logPath, 'utf8').split('\n').filter(Boolean).length
} catch {
return 0
}
}
function stampPathFor(dir: string, leafId = LEAF_ID): string {
return join(dir, `orca-claude-statusline-last-${leafId}`)
}
it('spawns one curl and no capture or clock subprocesses across 30 rapid ticks', async () => {
const { scriptPath, dir, curlLog, dateLog, catLog } = makeHarness()
for (let index = 0; index < 30; index += 1) {
await runScript(scriptPath, dir, rateLimitPayload(1_000 + index * 100))
}
expect(lineCount(curlLog)).toBe(1)
expect(lineCount(dateLog)).toBe(0)
expect(lineCount(catLog)).toBe(0)
expect(readFileSync(stampPathFor(dir), 'utf8')).toMatch(/^[0-9]+$/)
})
it('posts again once the interval has elapsed', async () => {
const { scriptPath, dir, curlLog } = makeHarness()
await runScript(scriptPath, dir, rateLimitPayload(1_000))
await runScript(scriptPath, dir, rateLimitPayload(16_000))
expect(lineCount(curlLog)).toBe(2)
})
it('stays bounded and keeps a valid stamp under overlapping same-pane ticks', async () => {
const { scriptPath, dir, curlLog } = makeHarness()
// Why: the stamp check/write is deliberately lock-free — a lock could wedge the feed closed,
// and fail-open is the contract — so a truly concurrent burst may post more than once,
// bounded by the overlap width. The deterministic invariants are: every overlapping run
// exits 0, the raced stamp still lands valid, and it throttles the very next ticks.
await Promise.all(
Array.from({ length: 10 }, () => runScript(scriptPath, dir, rateLimitPayload(1_000)))
)
const burstPosts = lineCount(curlLog)
expect(burstPosts).toBeGreaterThanOrEqual(1)
expect(readFileSync(stampPathFor(dir), 'utf8')).toBe('1')
for (let index = 0; index < 5; index += 1) {
await runScript(scriptPath, dir, rateLimitPayload(2_000 + index * 100))
}
expect(lineCount(curlLog)).toBe(burstPosts)
})
it('preserves multiline payloads while capturing stdin with shell builtins', async () => {
const { scriptPath, dir, payloadLog } = makeHarness()
const payload = JSON.stringify(JSON.parse(rateLimitPayload(1_000)), null, 2)
await runScript(scriptPath, dir, payload)
expect(readFileSync(payloadLog, 'utf8')).toBe(payload)
})
it('fails open on a garbage stamp even early in a session', async () => {
const { scriptPath, dir, curlLog } = makeHarness()
writeFileSync(stampPathFor(dir), 'not-a-number')
await runScript(scriptPath, dir, rateLimitPayload(1_000))
expect(lineCount(curlLog)).toBe(1)
expect(readFileSync(stampPathFor(dir), 'utf8')).toMatch(/^[0-9]+$/)
})
it('fails open and repairs a leading-zero stamp instead of dying in arithmetic', async () => {
const { scriptPath, dir, curlLog } = makeHarness()
// Why: 008 is all-digits but invalid octal inside $(( )) — under dash the old digits-only
// check made the script abort before rewriting the stamp, wedging this pane's feed dark.
writeFileSync(stampPathFor(dir), '008')
await runScript(scriptPath, dir, rateLimitPayload(1_000))
expect(lineCount(curlLog)).toBe(1)
expect(readFileSync(stampPathFor(dir), 'utf8')).toBe('1')
})
it('uses the clock fallback when the payload omits session duration', async () => {
const { scriptPath, dir, curlLog, dateLog } = makeHarness()
const payload = '{"rate_limits":{"five_hour":{"used_percentage":12}}}'
await runScript(scriptPath, dir, payload)
await runScript(scriptPath, dir, payload)
expect(lineCount(curlLog)).toBe(1)
expect(lineCount(dateLog)).toBe(2)
})
it('uses only the stable leaf id for temp files', async () => {
const { scriptPath, dir, curlLog } = makeHarness()
const paneKey = `${'path/segment/'.repeat(30)}tab:${LEAF_ID}`
await runScript(scriptPath, dir, rateLimitPayload(1_000), paneKey)
await runScript(scriptPath, dir, rateLimitPayload(2_000), paneKey)
expect(lineCount(curlLog)).toBe(1)
expect(readFileSync(stampPathFor(dir), 'utf8')).toBe('1')
})
it('keeps legacy numeric pane ids isolated by tab', async () => {
const { scriptPath, dir, curlLog } = makeHarness()
await runScript(scriptPath, dir, rateLimitPayload(1_000), 'legacy-tab-a:1')
await runScript(scriptPath, dir, rateLimitPayload(1_000), 'legacy-tab-b:1')
expect(lineCount(curlLog)).toBe(2)
expect(readFileSync(stampPathFor(dir, 'legacy-tab-a_1'), 'utf8')).toBe('1')
expect(readFileSync(stampPathFor(dir, 'legacy-tab-b_1'), 'utf8')).toBe('1')
})
it('never touches curl or the stamp for payloads without rate_limits', async () => {
const { scriptPath, dir, curlLog, dateLog } = makeHarness()
await runScript(scriptPath, dir, '{"model":{"id":"claude-fable-5"}}')
expect(lineCount(curlLog)).toBe(0)
expect(lineCount(dateLog)).toBe(0)
expect(() => readFileSync(stampPathFor(dir), 'utf8')).toThrow()
})
})
+86 -4
View File
@@ -1,12 +1,15 @@
import {
buildPosixHookPayloadCapture,
buildWindowsHookStdinDrainEpilogue,
WINDOWS_HOOK_STDIN_DRAIN_LABEL,
WINDOWS_HOOK_STDIN_READER
} from '../agent-hooks/hook-stdin-contract'
import { CLAUDE_STATUSLINE_PATHNAME } from '../../shared/claude-statusline-rate-limits'
import {
CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS,
CLAUDE_STATUSLINE_PATHNAME
} from '../../shared/claude-statusline-rate-limits'
const STATUSLINE_CLEANUP_LABEL = 'orca_statusline_cleanup'
const STATUSLINE_PROBE_LABEL = 'orca_statusline_probe'
// Why: Claude Code pipes `rate_limits` to the statusLine command on every turn; forwarding
// it gives Orca live usage without spending the OAuth usage endpoint's tight budget.
@@ -18,10 +21,26 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
'setlocal',
// Why: pane key is static PTY env (the endpoint file never sets it), so it can gate before stdin is consumed.
`if "%ORCA_PANE_KEY%"=="" goto :${WINDOWS_HOOK_STDIN_DRAIN_LABEL}`,
// Why: current keys end in a UUID; replacing the legacy delimiter also keeps surviving numeric-pane keys filename-safe.
'set "ORCA_STATUSLINE_PANE_ID=%ORCA_PANE_KEY:~-36%"',
'set "ORCA_STATUSLINE_PANE_ID=%ORCA_STATUSLINE_PANE_ID::=_%"',
// Why: cmd has no builtin stdin capture, so buffer the payload in a per-pane temp file
// (%RANDOM% collides across same-second cmd spawns) to guard before any curl spawn.
'set "ORCA_STATUSLINE_PAYLOAD_FILE=%TEMP%\\orca-claude-statusline-%ORCA_PANE_KEY::=_%.tmp"',
'set "ORCA_STATUSLINE_PAYLOAD_FILE=%TEMP%\\orca-claude-statusline-%ORCA_STATUSLINE_PANE_ID%.tmp"',
`${WINDOWS_HOOK_STDIN_READER} >"%ORCA_STATUSLINE_PAYLOAD_FILE%" 2>nul`,
// Why: an all-builtin seconds-of-day throttle avoids spawning findstr+curl on every streaming tick.
'set "ORCA_STATUSLINE_STAMP_FILE=%TEMP%\\orca-claude-statusline-last-%ORCA_STATUSLINE_PANE_ID%.tmp"',
'set "ORCA_STATUSLINE_NOW="',
'set "ORCA_STATUSLINE_TIME=%TIME: =0%"',
'for /f "tokens=1-3 delims=:.," %%a in ("%ORCA_STATUSLINE_TIME%") do set /a "ORCA_STATUSLINE_NOW=(1%%a %% 100)*3600+(1%%b %% 100)*60+(1%%c %% 100)" 2>nul',
'set "ORCA_STATUSLINE_LAST="',
'set "ORCA_STATUSLINE_ELAPSED="',
'if exist "%ORCA_STATUSLINE_STAMP_FILE%" set /p ORCA_STATUSLINE_LAST=<"%ORCA_STATUSLINE_STAMP_FILE%"',
'if defined ORCA_STATUSLINE_LAST for /f "delims=0123456789" %%d in ("%ORCA_STATUSLINE_LAST%") do set "ORCA_STATUSLINE_LAST="',
'if defined ORCA_STATUSLINE_NOW if defined ORCA_STATUSLINE_LAST set /a "ORCA_STATUSLINE_ELAPSED=ORCA_STATUSLINE_NOW-ORCA_STATUSLINE_LAST" 2>nul',
`if not defined ORCA_STATUSLINE_ELAPSED goto :${STATUSLINE_PROBE_LABEL}`,
`if %ORCA_STATUSLINE_ELAPSED% GEQ 0 if %ORCA_STATUSLINE_ELAPSED% LSS ${CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS} goto :${STATUSLINE_CLEANUP_LABEL}`,
`:${STATUSLINE_PROBE_LABEL}`,
// Why: rate_limits appears only for Claude.ai-subscriber sessions after the first API response; the
// statusline ticks ~3x/sec during streaming, so skip the endpoint call and curl spawn otherwise.
// Why: \" is the MSVC argv escape — findstr sees the quoted JSON key, so a cwd containing rate_limits can't false-match (POSIX guard parity).
@@ -31,6 +50,8 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
`if "%ORCA_AGENT_HOOK_PORT%"=="" goto :${STATUSLINE_CLEANUP_LABEL}`,
`if "%ORCA_AGENT_HOOK_TOKEN%"=="" goto :${STATUSLINE_CLEANUP_LABEL}`,
// Why: stamp only when a post is certain, so skipped ticks (no rate_limits, missing port/token) never push the next allowed post out.
'if defined ORCA_STATUSLINE_NOW (>"%ORCA_STATUSLINE_STAMP_FILE%" echo %ORCA_STATUSLINE_NOW%)',
// Why: pre-build the field from an always-defined variable so an unset CLAUDE_CONFIG_DIR posts
// empty (matching POSIX and the null attribution snapshot), never a literal %VAR% token.
'set "ORCA_STATUSLINE_CONFIG_DIR_FIELD=configDir="',
@@ -58,7 +79,15 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
return [
'#!/bin/sh',
...buildPosixHookPayloadCapture(),
// Why: this runs on every statusline tick; builtin capture avoids replacing curl churn with cat churn.
'payload=',
'while IFS= read -r orca_statusline_line || [ -n "$orca_statusline_line" ]; do',
' payload="${payload}${orca_statusline_line}\n"',
'done',
'payload=${payload%?}',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: rate_limits appears only for Claude.ai-subscriber sessions after the first API response; skip the post (and its curl spawn) otherwise.
'case "$payload" in',
' *\'"rate_limits"\'*) ;;',
@@ -70,6 +99,59 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
// Why: the stable leaf UUID avoids path-unsafe and overlong user-supplied tab ids.
'orca_statusline_pane_id=${ORCA_PANE_KEY##*:}',
// Why: pre-migration numeric leaf ids were tab-local, so include a safe tab id to avoid cross-pane throttle collisions after upgrade.
'case "$orca_statusline_pane_id" in',
" ''|*[!0-9]*) ;;",
' *)',
' orca_statusline_tab_id=${ORCA_PANE_KEY%:*}',
' case "$orca_statusline_tab_id" in',
" ''|*[!A-Za-z0-9._-]*) ;;",
' *) orca_statusline_pane_id="${orca_statusline_tab_id}_${orca_statusline_pane_id}" ;;',
' esac',
' ;;',
'esac',
'orca_statusline_stamp="${TMPDIR:-/tmp}/orca-claude-statusline-last-${orca_statusline_pane_id}"',
// Why: the payload clock keeps throttled ticks free of subprocesses; date is only a schema-drift fallback.
'orca_statusline_now=',
'case "$payload" in',
' *\'"total_duration_ms"\'*)',
' orca_statusline_duration=${payload#*\'"total_duration_ms"\'}',
' orca_statusline_duration=${orca_statusline_duration#*:}',
' orca_statusline_duration=${orca_statusline_duration#"${orca_statusline_duration%%[![:space:]]*}"}',
' orca_statusline_duration=${orca_statusline_duration%%[!0-9]*}',
' case "$orca_statusline_duration" in',
' 0|[1-9]|[1-9][0-9]*)',
' if [ "${#orca_statusline_duration}" -le 15 ]; then',
' orca_statusline_now=$((orca_statusline_duration / 1000))',
' fi',
' ;;',
' esac',
' ;;',
'esac',
'if [ -z "$orca_statusline_now" ]; then',
' orca_statusline_now=$(date +%s 2>/dev/null) || orca_statusline_now=',
'fi',
// Why: leading zeros read as octal inside $(( )), and a bad constant (008) is FATAL in dash —
// the script would die before rewriting the stamp, wedging the pane dark. Allow-list canonical
// decimals so any malformed value fails open to posting instead.
'case "$orca_statusline_now" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_now= ;; esac',
'if [ -n "$orca_statusline_now" ] && [ -f "$orca_statusline_stamp" ]; then',
' orca_statusline_last=',
' IFS= read -r orca_statusline_last <"$orca_statusline_stamp" 2>/dev/null || :',
' case "$orca_statusline_last" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_last= ;; esac',
' if [ "${#orca_statusline_last}" -gt 15 ]; then orca_statusline_last=; fi',
' if [ -n "$orca_statusline_last" ]; then',
' orca_statusline_elapsed=$((orca_statusline_now - orca_statusline_last))',
` if [ "$orca_statusline_elapsed" -ge 0 ] && [ "$orca_statusline_elapsed" -lt ${CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS} ]; then`,
' exit 0',
' fi',
' fi',
'fi',
'if [ -n "$orca_statusline_now" ]; then',
' printf \'%s\' "$orca_statusline_now" >"$orca_statusline_stamp" 2>/dev/null || :',
'fi',
`printf '%s' "$payload" | curl -sS -X POST "http://127.0.0.1:\${ORCA_AGENT_HOOK_PORT}${CLAUDE_STATUSLINE_PATHNAME}" \\`,
' --connect-timeout 0.5 --max-time 1.5 \\',
' -H "Content-Type: application/x-www-form-urlencoded" \\',
@@ -4,6 +4,11 @@
export const CLAUDE_STATUSLINE_PATHNAME = '/statusline/claude'
// Why: the statusline ticks ~3x/sec while streaming and the service drops same-value posts
// inside LIVE_CLAUDE_INGEST_DEDUPE_MS (30s) anyway; a per-pane client floor below that bound
// keeps the usage bar live while capping curl spawns at one per pane per interval.
export const CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS = 15
export type ClaudeStatusLineWindow = {
used_percentage?: number
/** OAuth-usage-shaped sibling field (0-100); accepted so a CLI schema drift degrades instead of going dark. */