| 1 | export type WorkspaceMeta = { |
| 2 | workspacePath: string; |
| 3 | sessionId: string; |
| 4 | stage: string; |
| 5 | compactionUsed: number; |
| 6 | compactionTarget: number; |
| 7 | }; |
| 8 | |
| 9 | export function compactTargetFromEnv(env: Record<string, string | undefined>): number { |
| 10 | const contextWindow = parsePositiveInt(env.VIMAX_CONTEXT_WINDOW_TOKENS, 200000); |
| 11 | const ratio = parsePositiveFloat(env.VIMAX_AUTO_COMPACT_RATIO, 0.9); |
| 12 | const threshold = parsePositiveInt(env.VIMAX_AUTO_COMPACT_TOKEN_THRESHOLD, Math.round(contextWindow * Math.min(1, Math.max(0, ratio)))); |
| 13 | const buffer = parsePositiveInt(env.VIMAX_AUTO_COMPACT_BUFFER_TOKENS, 20000); |
| 14 | return Math.max(0, threshold - buffer); |
| 15 | } |
| 16 | |
| 17 | export function compactionBar(used: number, target: number, width = 18): string { |
| 18 | const safeWidth = Math.max(4, width); |
| 19 | if (target <= 0) return '░'.repeat(safeWidth); |
| 20 | const ratio = Math.min(1, Math.max(0, used / target)); |
| 21 | const filled = Math.round(ratio * safeWidth); |
| 22 | return '█'.repeat(filled) + '░'.repeat(safeWidth - filled); |
| 23 | } |
| 24 | |
| 25 | export function compactionLabel(used: number, target: number): string { |
| 26 | if (target <= 0) return 'Compaction disabled'; |
| 27 | const safeUsed = Math.max(0, Math.round(used)); |
| 28 | const percent = Math.min(999, Math.max(0, Math.round((safeUsed / target) * 100))); |
| 29 | return `Compaction [${compactionBar(safeUsed, target)}] ${safeUsed}/${target} (${percent}%)`; |
| 30 | } |
| 31 | |
| 32 | export function resolveWorkspacePath(_repoRoot: string, workingDir?: string): string { |
| 33 | const normalized = String(workingDir ?? '').trim(); |
| 34 | if (!normalized) return '.working_dir'; |
| 35 | const relative = normalized.replace(/^.*\/\.working_dir\//, '.working_dir/').replace(/^\.\//, ''); |
| 36 | if (relative.startsWith('.working_dir/')) return relative; |
| 37 | if (relative === '.working_dir') return relative; |
| 38 | return `.working_dir/${relative.replace(/^\/+/, '')}`; |
| 39 | } |
| 40 | |
| 41 | function parsePositiveInt(value: string | undefined, fallback: number): number { |
| 42 | if (!value) return fallback; |
| 43 | const parsed = Number.parseInt(value, 10); |
| 44 | if (!Number.isFinite(parsed) || parsed < 0) return fallback; |
| 45 | return parsed; |
| 46 | } |
| 47 | |
| 48 | function parsePositiveFloat(value: string | undefined, fallback: number): number { |
| 49 | if (!value) return fallback; |
| 50 | const parsed = Number.parseFloat(value); |
| 51 | if (!Number.isFinite(parsed) || parsed < 0) return fallback; |
| 52 | return parsed; |
| 53 | } |
| 54 |