chore: initialize recovered claude workspace
This commit is contained in:
110
src/bridge/pollConfig.ts
Normal file
110
src/bridge/pollConfig.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { z } from 'zod/v4'
|
||||
import { getFeatureValue_CACHED_WITH_REFRESH } from '../services/analytics/growthbook.js'
|
||||
import { lazySchema } from '../utils/lazySchema.js'
|
||||
import {
|
||||
DEFAULT_POLL_CONFIG,
|
||||
type PollIntervalConfig,
|
||||
} from './pollConfigDefaults.js'
|
||||
|
||||
// .min(100) on the seek-work intervals restores the old Math.max(..., 100)
|
||||
// defense-in-depth floor against fat-fingered GrowthBook values. Unlike a
|
||||
// clamp, Zod rejects the whole object on violation — a config with one bad
|
||||
// field falls back to DEFAULT_POLL_CONFIG entirely rather than being
|
||||
// partially trusted.
|
||||
//
|
||||
// The at_capacity intervals use a 0-or-≥100 refinement: 0 means "disabled"
|
||||
// (heartbeat-only mode), ≥100 is the fat-finger floor. Values 1–99 are
|
||||
// rejected so unit confusion (ops thinks seconds, enters 10) doesn't poll
|
||||
// every 10ms against the VerifyEnvironmentSecretAuth DB path.
|
||||
//
|
||||
// The object-level refines require at least one at-capacity liveness
|
||||
// mechanism enabled: heartbeat OR the relevant poll interval. Without this,
|
||||
// the hb=0, atCapMs=0 drift config (ops disables heartbeat without
|
||||
// restoring at_capacity) falls through every throttle site with no sleep —
|
||||
// tight-looping /poll at HTTP-round-trip speed.
|
||||
const zeroOrAtLeast100 = {
|
||||
message: 'must be 0 (disabled) or ≥100ms',
|
||||
}
|
||||
const pollIntervalConfigSchema = lazySchema(() =>
|
||||
z
|
||||
.object({
|
||||
poll_interval_ms_not_at_capacity: z.number().int().min(100),
|
||||
// 0 = no at-capacity polling. Independent of heartbeat — both can be
|
||||
// enabled (heartbeat runs, periodically breaks out to poll).
|
||||
poll_interval_ms_at_capacity: z
|
||||
.number()
|
||||
.int()
|
||||
.refine(v => v === 0 || v >= 100, zeroOrAtLeast100),
|
||||
// 0 = disabled; positive value = heartbeat at this interval while at
|
||||
// capacity. Runs alongside at-capacity polling, not instead of it.
|
||||
// Named non_exclusive to distinguish from the old heartbeat_interval_ms
|
||||
// (either-or semantics in pre-#22145 clients). .default(0) so existing
|
||||
// GrowthBook configs without this field parse successfully.
|
||||
non_exclusive_heartbeat_interval_ms: z.number().int().min(0).default(0),
|
||||
// Multisession (bridgeMain.ts) intervals. Defaults match the
|
||||
// single-session values so existing configs without these fields
|
||||
// preserve current behavior.
|
||||
multisession_poll_interval_ms_not_at_capacity: z
|
||||
.number()
|
||||
.int()
|
||||
.min(100)
|
||||
.default(
|
||||
DEFAULT_POLL_CONFIG.multisession_poll_interval_ms_not_at_capacity,
|
||||
),
|
||||
multisession_poll_interval_ms_partial_capacity: z
|
||||
.number()
|
||||
.int()
|
||||
.min(100)
|
||||
.default(
|
||||
DEFAULT_POLL_CONFIG.multisession_poll_interval_ms_partial_capacity,
|
||||
),
|
||||
multisession_poll_interval_ms_at_capacity: z
|
||||
.number()
|
||||
.int()
|
||||
.refine(v => v === 0 || v >= 100, zeroOrAtLeast100)
|
||||
.default(DEFAULT_POLL_CONFIG.multisession_poll_interval_ms_at_capacity),
|
||||
// .min(1) matches the server's ge=1 constraint (work_v1.py:230).
|
||||
reclaim_older_than_ms: z.number().int().min(1).default(5000),
|
||||
session_keepalive_interval_v2_ms: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(120_000),
|
||||
})
|
||||
.refine(
|
||||
cfg =>
|
||||
cfg.non_exclusive_heartbeat_interval_ms > 0 ||
|
||||
cfg.poll_interval_ms_at_capacity > 0,
|
||||
{
|
||||
message:
|
||||
'at-capacity liveness requires non_exclusive_heartbeat_interval_ms > 0 or poll_interval_ms_at_capacity > 0',
|
||||
},
|
||||
)
|
||||
.refine(
|
||||
cfg =>
|
||||
cfg.non_exclusive_heartbeat_interval_ms > 0 ||
|
||||
cfg.multisession_poll_interval_ms_at_capacity > 0,
|
||||
{
|
||||
message:
|
||||
'at-capacity liveness requires non_exclusive_heartbeat_interval_ms > 0 or multisession_poll_interval_ms_at_capacity > 0',
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Fetch the bridge poll interval config from GrowthBook with a 5-minute
|
||||
* refresh window. Validates the served JSON against the schema; falls back
|
||||
* to defaults if the flag is absent, malformed, or partially-specified.
|
||||
*
|
||||
* Shared by bridgeMain.ts (standalone) and replBridge.ts (REPL) so ops
|
||||
* can tune both poll rates fleet-wide with a single config push.
|
||||
*/
|
||||
export function getPollIntervalConfig(): PollIntervalConfig {
|
||||
const raw = getFeatureValue_CACHED_WITH_REFRESH<unknown>(
|
||||
'tengu_bridge_poll_interval_config',
|
||||
DEFAULT_POLL_CONFIG,
|
||||
5 * 60 * 1000,
|
||||
)
|
||||
const parsed = pollIntervalConfigSchema().safeParse(raw)
|
||||
return parsed.success ? parsed.data : DEFAULT_POLL_CONFIG
|
||||
}
|
||||
Reference in New Issue
Block a user