{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../../src/core/routing/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,WAAW,EAAE,YAAY,CAmE5D,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,qBAAqB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,sBAAsB,EAAE,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAmB/E;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAC1B,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EACpE,MAAM,EAAE,YAAY,GAClB,OAAO,CAQT","sourcesContent":["/**\n * Budget-class selection.\n *\n * Budget classes define bounded dimensions. Operator ceilings remain\n * authoritative. Unused budget is not interpreted as failure. A finalization\n * reserve is mandatory. Escalation requires available reserve. Candidate\n * scores are normalized for the budget class so comparison avoids rewarding\n * unlimited budgets.\n */\n\nimport type { BudgetClass } from \"./types.js\";\n\nexport interface BudgetBounds {\n\tmaxModelCalls: number;\n\tmaxToolCalls: number;\n\tmaxSubagents: number;\n\tmaxRetrievalQueries: number;\n\tmaxContextTokens: number;\n\tmaxOutputTokens: number;\n\tmaxWallTimeMs: number;\n\tmaxCostUsd: number;\n\tfinalizationReserveRatio: number;\n}\n\nexport const BUDGET_CLASSES: Record<BudgetClass, BudgetBounds> = {\n\ttiny: {\n\t\tmaxModelCalls: 2,\n\t\tmaxToolCalls: 8,\n\t\tmaxSubagents: 0,\n\t\tmaxRetrievalQueries: 5,\n\t\tmaxContextTokens: 20_000,\n\t\tmaxOutputTokens: 4_000,\n\t\tmaxWallTimeMs: 30_000,\n\t\tmaxCostUsd: 0.1,\n\t\tfinalizationReserveRatio: 0.2,\n\t},\n\tsmall: {\n\t\tmaxModelCalls: 5,\n\t\tmaxToolCalls: 24,\n\t\tmaxSubagents: 1,\n\t\tmaxRetrievalQueries: 12,\n\t\tmaxContextTokens: 40_000,\n\t\tmaxOutputTokens: 8_000,\n\t\tmaxWallTimeMs: 120_000,\n\t\tmaxCostUsd: 0.5,\n\t\tfinalizationReserveRatio: 0.2,\n\t},\n\tstandard: {\n\t\tmaxModelCalls: 15,\n\t\tmaxToolCalls: 100,\n\t\tmaxSubagents: 3,\n\t\tmaxRetrievalQueries: 30,\n\t\tmaxContextTokens: 128_000,\n\t\tmaxOutputTokens: 16_000,\n\t\tmaxWallTimeMs: 300_000,\n\t\tmaxCostUsd: 2,\n\t\tfinalizationReserveRatio: 0.2,\n\t},\n\tlarge: {\n\t\tmaxModelCalls: 40,\n\t\tmaxToolCalls: 300,\n\t\tmaxSubagents: 6,\n\t\tmaxRetrievalQueries: 80,\n\t\tmaxContextTokens: 256_000,\n\t\tmaxOutputTokens: 32_000,\n\t\tmaxWallTimeMs: 900_000,\n\t\tmaxCostUsd: 8,\n\t\tfinalizationReserveRatio: 0.2,\n\t},\n\thigh_assurance: {\n\t\tmaxModelCalls: 60,\n\t\tmaxToolCalls: 400,\n\t\tmaxSubagents: 8,\n\t\tmaxRetrievalQueries: 120,\n\t\tmaxContextTokens: 256_000,\n\t\tmaxOutputTokens: 32_000,\n\t\tmaxWallTimeMs: 1_800_000,\n\t\tmaxCostUsd: 15,\n\t\tfinalizationReserveRatio: 0.3,\n\t},\n\trelease: {\n\t\tmaxModelCalls: 100,\n\t\tmaxToolCalls: 600,\n\t\tmaxSubagents: 12,\n\t\tmaxRetrievalQueries: 200,\n\t\tmaxContextTokens: 512_000,\n\t\tmaxOutputTokens: 64_000,\n\t\tmaxWallTimeMs: 3_600_000,\n\t\tmaxCostUsd: 30,\n\t\tfinalizationReserveRatio: 0.3,\n\t},\n};\n\nexport interface BudgetSelectionInput {\n\tbudgetClass: string;\n\toperatorCeiling?: Partial<BudgetBounds>;\n}\n\nexport interface BudgetSelectionResult {\n\tbudgetClass: string;\n\tbounds: BudgetBounds;\n\tfinalizationReserve: Record<string, number>;\n\toperatorCeilingApplied: boolean;\n}\n\n/**\n * Select budget bounds for a class, intersecting with operator ceilings.\n * Operator ceilings are authoritative and always win.\n */\nexport function selectBudget(input: BudgetSelectionInput): BudgetSelectionResult {\n\tconst base = BUDGET_CLASSES[input.budgetClass as BudgetClass] ?? BUDGET_CLASSES.standard;\n\tconst bounds: BudgetBounds = { ...base };\n\tconst operator = input.operatorCeiling ?? {};\n\tconst operatorCeilingApplied = Object.keys(operator).length > 0;\n\n\tfor (const key of Object.keys(bounds) as (keyof BudgetBounds)[]) {\n\t\tif (key === \"finalizationReserveRatio\") continue;\n\t\tif (operator[key] !== undefined && (operator[key] as number) < bounds[key]) bounds[key] = operator[key] as number;\n\t}\n\n\tconst reserve: Record<string, number> = {};\n\t(reserve as Record<string, number>).maxCostUsd = bounds.maxCostUsd * bounds.finalizationReserveRatio;\n\t(reserve as Record<string, number>).maxWallTimeMs = bounds.maxWallTimeMs * bounds.finalizationReserveRatio;\n\t(reserve as Record<string, number>).maxModelCalls = Math.round(\n\t\tbounds.maxModelCalls * bounds.finalizationReserveRatio,\n\t);\n\n\treturn { budgetClass: input.budgetClass, bounds, finalizationReserve: reserve, operatorCeilingApplied };\n}\n\n/** Whether escalation is permitted given budget usage and the mandatory reserve. */\nexport function canEscalate(\n\tused: { costUsd?: number; wallTimeMs?: number; modelCalls?: number },\n\tbounds: BudgetBounds,\n): boolean {\n\tconst reserveCost = bounds.maxCostUsd * bounds.finalizationReserveRatio;\n\tconst reserveTime = bounds.maxWallTimeMs * bounds.finalizationReserveRatio;\n\tconst reserveCalls = Math.round(bounds.maxModelCalls * bounds.finalizationReserveRatio);\n\tif (used.costUsd !== undefined && bounds.maxCostUsd - used.costUsd < reserveCost) return false;\n\tif (used.wallTimeMs !== undefined && bounds.maxWallTimeMs - used.wallTimeMs < reserveTime) return false;\n\tif (used.modelCalls !== undefined && bounds.maxModelCalls - used.modelCalls < reserveCalls) return false;\n\treturn true;\n}\n"]}