@treeseed/sdk 0.10.11 → 0.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +2 -2
  2. package/dist/api/auth/d1-store.js +20 -1
  3. package/dist/capacity-provider.d.ts +53 -1
  4. package/dist/capacity.d.ts +80 -1
  5. package/dist/capacity.js +687 -8
  6. package/dist/db/d1.d.ts +109 -3227
  7. package/dist/db/index.d.ts +1 -0
  8. package/dist/db/index.js +1 -0
  9. package/dist/db/market-schema.d.ts +43769 -0
  10. package/dist/db/market-schema.js +1878 -0
  11. package/dist/db/node-sqlite.d.ts +109 -3227
  12. package/dist/db/schema.d.ts +226 -5757
  13. package/dist/db/schema.js +35 -226
  14. package/dist/index.d.ts +6 -4
  15. package/dist/index.js +32 -0
  16. package/dist/market-client.d.ts +135 -0
  17. package/dist/market-client.js +134 -1
  18. package/dist/operations/services/commit-message-provider.js +1 -1
  19. package/dist/operations/services/d1-migration.js +0 -59
  20. package/dist/operations/services/deploy.js +5 -1
  21. package/dist/operations/services/github-api.d.ts +83 -0
  22. package/dist/operations/services/github-api.js +167 -0
  23. package/dist/operations/services/local-dev.js +3 -3
  24. package/dist/operations/services/mailpit-runtime.d.ts +13 -2
  25. package/dist/operations/services/mailpit-runtime.js +19 -14
  26. package/dist/operations/services/project-web-monitor.d.ts +15 -0
  27. package/dist/operations/services/project-web-monitor.js +260 -0
  28. package/dist/operations/services/railway-api.js +2 -2
  29. package/dist/operations/services/release-candidate.js +9 -9
  30. package/dist/operations/services/runtime-paths.d.ts +1 -1
  31. package/dist/operations/services/runtime-paths.js +2 -2
  32. package/dist/operations/services/template-registry.js +10 -1
  33. package/dist/operations.d.ts +1 -0
  34. package/dist/operations.js +11 -1
  35. package/dist/platform-operation-store.d.ts +4 -0
  36. package/dist/platform-operation-store.js +29 -3
  37. package/dist/platform-operations.d.ts +8 -0
  38. package/dist/platform-operations.js +19 -0
  39. package/dist/remote.js +6 -6
  40. package/dist/scripts/tenant-d1-migrate-local.js +2 -3
  41. package/dist/scripts/test-cloudflare-local.js +1 -1
  42. package/dist/scripts/workspace-command-e2e.js +3 -1
  43. package/dist/sdk-types.d.ts +281 -3
  44. package/dist/sdk-types.js +5 -1
  45. package/dist/seeds/normalize.js +6 -0
  46. package/dist/seeds/schema.js +61 -1
  47. package/dist/seeds/types.d.ts +32 -0
  48. package/drizzle/d1/0000_treeseed_d1.sql +37 -0
  49. package/drizzle/market/0000_market_control_plane.sql +2929 -0
  50. package/drizzle/market/0001_capacity_budget_mode_default.sql +4 -0
  51. package/drizzle/market/0002_user_email_addresses.sql +26 -0
  52. package/package.json +8 -1
package/dist/capacity.js CHANGED
@@ -1,3 +1,447 @@
1
+ const ACTUAL_CREDIT_FORMULA_VERSION = "treeseed.actual-credits.v1";
2
+ function finiteActualMetric(value) {
3
+ if (typeof value === "number" && Number.isFinite(value)) return value;
4
+ if (typeof value === "string" && value.trim()) {
5
+ const parsed = Number(value);
6
+ if (Number.isFinite(parsed)) return parsed;
7
+ }
8
+ return null;
9
+ }
10
+ function firstActualMetric(...values) {
11
+ for (const value of values) {
12
+ const next = finiteActualMetric(value);
13
+ if (next !== null) return next;
14
+ }
15
+ return null;
16
+ }
17
+ function objectActualValue(value) {
18
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
19
+ }
20
+ function booleanActualFlag(value) {
21
+ return value === true || value === "true" || value === 1 || value === "1";
22
+ }
23
+ function roundActualCredits(value) {
24
+ if (!Number.isFinite(value) || value <= 0) return 0;
25
+ return Math.round(value * 100) / 100;
26
+ }
27
+ function roundUpCreditComponent(value) {
28
+ if (!Number.isFinite(value) || value <= 0) return 0;
29
+ return Math.ceil(value * 100) / 100;
30
+ }
31
+ function stringActualValue(value, fallback = "") {
32
+ return typeof value === "string" && value.trim() ? value.trim() : fallback;
33
+ }
34
+ function nativeUsageUnit(input) {
35
+ const native = objectActualValue(input);
36
+ const explicit = stringActualValue(native.nativeUnit, stringActualValue(native.native_unit));
37
+ if (explicit) return explicit;
38
+ if (firstActualMetric(native.wallMinutes, native.wall_minutes, native.durationMinutes, native.duration_minutes) !== null) return "wall_minute";
39
+ if (firstActualMetric(native.quotaMinutes, native.quota_minutes) !== null) return "quota_minute";
40
+ if (firstActualMetric(native.usd, native.costUsd, native.cost_usd) !== null) return "usd";
41
+ if (firstActualMetric(native.inputTokens, native.input_tokens, native.outputTokens, native.output_tokens) !== null) return "token";
42
+ return null;
43
+ }
44
+ function nativeUsageAmount(input, nativeUnit) {
45
+ const native = objectActualValue(input);
46
+ const unit = nativeUnit?.trim() || nativeUsageUnit(native);
47
+ if (!unit) return null;
48
+ if (unit === "wall_minute") {
49
+ return firstActualMetric(native.wallMinutes, native.wall_minutes, native.durationMinutes, native.duration_minutes);
50
+ }
51
+ if (unit === "quota_minute") {
52
+ return firstActualMetric(native.quotaMinutes, native.quota_minutes);
53
+ }
54
+ if (unit === "usd") {
55
+ return firstActualMetric(native.usd, native.costUsd, native.cost_usd);
56
+ }
57
+ if (unit === "token") {
58
+ const inputTokens = firstActualMetric(native.inputTokens, native.input_tokens) ?? 0;
59
+ const outputTokens = firstActualMetric(native.outputTokens, native.output_tokens) ?? 0;
60
+ const cachedInputTokens = firstActualMetric(native.cachedInputTokens, native.cached_input_tokens) ?? 0;
61
+ const total = Math.max(0, inputTokens + outputTokens - cachedInputTokens);
62
+ return total > 0 ? total : null;
63
+ }
64
+ return firstActualMetric(native.amount, native.value, native.nativeAmount, native.native_amount);
65
+ }
66
+ function conversionConfidence(input) {
67
+ if (input.completedSampleCount < 5) return "low";
68
+ if (input.completedSampleCount < 20) return "medium";
69
+ const p50 = Math.max(1, input.nativeUnitsPerCreditP50 ?? 1);
70
+ const spread = Math.sqrt(Math.max(0, input.ratioVariance)) / p50;
71
+ return spread <= 0.5 ? "high" : "medium";
72
+ }
73
+ function selectCreditConversionProfile(input) {
74
+ const taskSignature = input.taskSignature?.trim();
75
+ const executionProfileId = input.executionProfileId?.trim() || DEFAULT_EXECUTION_PROFILE_ID;
76
+ const executionProviderKind = input.executionProviderKind?.trim();
77
+ const nativeUnit = input.nativeUnit?.trim();
78
+ if (!taskSignature || !executionProviderKind || !nativeUnit) return null;
79
+ return (input.profiles ?? []).find(
80
+ (profile) => profile.taskSignature === taskSignature && (profile.executionProfileId || DEFAULT_EXECUTION_PROFILE_ID) === executionProfileId && profile.executionProviderKind === executionProviderKind && profile.nativeUnit === nativeUnit
81
+ ) ?? null;
82
+ }
83
+ function buildCreditConversionProfileFromActuals(input) {
84
+ const taskSignature = input.taskSignature;
85
+ const executionProfileId = input.executionProfileId?.trim() || DEFAULT_EXECUTION_PROFILE_ID;
86
+ const executionProviderKind = input.executionProviderKind.trim();
87
+ const nativeUnit = input.nativeUnit.trim();
88
+ const matching = input.actuals.filter(
89
+ (actual) => actual.taskSignature === taskSignature && (actual.executionProfileId || DEFAULT_EXECUTION_PROFILE_ID) === executionProfileId
90
+ );
91
+ const completed = matching.filter((actual) => !isInterruptedUsageActual(actual));
92
+ const interrupted = matching.filter((actual) => isInterruptedUsageActual(actual));
93
+ const completedRatios = completed.map((actual) => {
94
+ const amount = nativeUsageAmount(actual.nativeUsage, nativeUnit);
95
+ const credits = finiteOrParsedNumber(actual.actualCredits);
96
+ if (amount === null || amount <= 0 || credits === null || credits <= 0) return null;
97
+ return {
98
+ nativeUnitsPerCredit: amount / credits,
99
+ creditsPerNativeUnit: credits / amount,
100
+ actualCredits: credits,
101
+ nativeAmount: amount
102
+ };
103
+ }).filter((value) => value !== null);
104
+ const ratioP50 = estimateLearningPercentile(completedRatios.map((sample) => sample.nativeUnitsPerCredit), 50);
105
+ const ratioP90 = estimateLearningPercentile(completedRatios.map((sample) => sample.nativeUnitsPerCredit), 90);
106
+ const ratioVariance = estimateLearningVariance(completedRatios.map((sample) => sample.nativeUnitsPerCredit));
107
+ const outlierLimit = ratioP90 === null ? null : Math.max(ratioP90 * 1.5, (ratioP50 ?? ratioP90) + Math.sqrt(ratioVariance));
108
+ const filteredRatios = outlierLimit === null ? completedRatios : completedRatios.filter((sample) => sample.nativeUnitsPerCredit <= outlierLimit);
109
+ const dates = matching.map((actual) => actual.createdAt).filter((value) => typeof value === "string" && value.length > 0).sort();
110
+ const partialCredits = interrupted.reduce((total, actual) => total + Math.max(0, finiteOrParsedNumber(actual.actualCredits) ?? 0), 0);
111
+ const partialNativeAmount = interrupted.reduce((total, actual) => total + Math.max(0, nativeUsageAmount(actual.nativeUsage, nativeUnit) ?? 0), 0);
112
+ const updatedAt = input.now instanceof Date ? input.now.toISOString() : typeof input.now === "string" ? input.now : (/* @__PURE__ */ new Date()).toISOString();
113
+ const nativeUnitsPerCreditP50 = estimateLearningPercentile(filteredRatios.map((sample) => sample.nativeUnitsPerCredit), 50);
114
+ const nativeUnitsPerCreditP90 = estimateLearningPercentile(filteredRatios.map((sample) => sample.nativeUnitsPerCredit), 90);
115
+ return {
116
+ id: input.id ?? `${taskSignature}:${executionProfileId}:${executionProviderKind}:${nativeUnit}`,
117
+ taskSignature,
118
+ executionProfileId,
119
+ executionProviderKind,
120
+ nativeUnit,
121
+ sampleCount: matching.length,
122
+ completedSampleCount: completedRatios.length,
123
+ interruptedSampleCount: interrupted.length,
124
+ nativeUnitsPerCreditP50,
125
+ nativeUnitsPerCreditP90,
126
+ creditsPerNativeUnitP50: estimateLearningPercentile(filteredRatios.map((sample) => sample.creditsPerNativeUnit), 50),
127
+ creditsPerNativeUnitP90: estimateLearningPercentile(filteredRatios.map((sample) => sample.creditsPerNativeUnit), 90),
128
+ actualCreditsP50: estimateLearningPercentile(filteredRatios.map((sample) => sample.actualCredits), 50),
129
+ actualCreditsP90: estimateLearningPercentile(filteredRatios.map((sample) => sample.actualCredits), 90),
130
+ confidence: conversionConfidence({
131
+ completedSampleCount: completedRatios.length,
132
+ ratioVariance,
133
+ nativeUnitsPerCreditP50
134
+ }),
135
+ formulaVersion: input.formulaVersion ?? ACTUAL_CREDIT_FORMULA_VERSION,
136
+ metadata: {
137
+ outlierCount: outlierLimit === null ? 0 : completedRatios.length - filteredRatios.length,
138
+ ratioVariance,
139
+ partialCredits,
140
+ partialNativeAmount,
141
+ firstSampleAt: dates[0] ?? null,
142
+ lastSampleAt: dates.at(-1) ?? null
143
+ },
144
+ updatedAt
145
+ };
146
+ }
147
+ function derivedConfidenceRank(value) {
148
+ if (value === "high") return 3;
149
+ if (value === "medium") return 2;
150
+ return 1;
151
+ }
152
+ function derivedConfidenceFromRank(rank) {
153
+ if (rank >= 3) return "high";
154
+ if (rank >= 2) return "medium";
155
+ return "low";
156
+ }
157
+ function lowerDerivedConfidence(value) {
158
+ return derivedConfidenceFromRank(Math.min(derivedConfidenceRank(value), 2));
159
+ }
160
+ function nativeRemainingAmount(input, nativeUnit) {
161
+ return nativeUsageAmount({
162
+ ...input,
163
+ nativeUnit
164
+ }, nativeUnit);
165
+ }
166
+ function reservationNativeDebit(reservation, executionProvider, nativeUnit) {
167
+ if (!["reserved", "consuming", "consumed", "failed", "overran_pending_approval"].includes(reservation.state)) {
168
+ return { reserved: 0, consumed: 0, inferred: false };
169
+ }
170
+ const metadata = readRecord(reservation.metadata);
171
+ const metadataNativeUnit = typeof metadata.nativeUnit === "string" && metadata.nativeUnit.trim() ? metadata.nativeUnit.trim() : typeof metadata.native_unit === "string" && metadata.native_unit.trim() ? metadata.native_unit.trim() : null;
172
+ const directReserved = finiteOrParsedNumber(reservation.reservedNativeAmount);
173
+ const directConsumed = finiteOrParsedNumber(reservation.consumedNativeAmount);
174
+ if ((reservation.nativeUnit ?? null) === nativeUnit && (directReserved !== null || directConsumed !== null)) {
175
+ return {
176
+ reserved: ["reserved", "consuming"].includes(reservation.state) ? Math.max(directReserved ?? 0, directConsumed ?? 0) : 0,
177
+ consumed: ["consumed", "failed", "overran_pending_approval"].includes(reservation.state) ? Math.max(directConsumed ?? 0, 0) : 0,
178
+ inferred: false
179
+ };
180
+ }
181
+ const metadataReserved = finiteOrParsedNumber(metadata.reservedNativeAmount) ?? finiteOrParsedNumber(metadata.reserved_native_amount);
182
+ const metadataConsumed = finiteOrParsedNumber(metadata.consumedNativeAmount) ?? finiteOrParsedNumber(metadata.consumed_native_amount);
183
+ if (metadataNativeUnit === nativeUnit && (metadataReserved !== null || metadataConsumed !== null)) {
184
+ return {
185
+ reserved: ["reserved", "consuming"].includes(reservation.state) ? Math.max(metadataReserved ?? 0, metadataConsumed ?? 0) : 0,
186
+ consumed: ["consumed", "failed", "overran_pending_approval"].includes(reservation.state) ? Math.max(metadataConsumed ?? 0, 0) : 0,
187
+ inferred: true
188
+ };
189
+ }
190
+ if (nativeUnit === "usd" && reservation.reservedUsd !== null) {
191
+ return {
192
+ reserved: ["reserved", "consuming"].includes(reservation.state) ? Math.max(reservation.reservedUsd ?? 0, reservation.consumedUsd ?? 0) : 0,
193
+ consumed: ["consumed", "failed", "overran_pending_approval"].includes(reservation.state) ? Math.max(reservation.consumedUsd ?? 0, 0) : 0,
194
+ inferred: true
195
+ };
196
+ }
197
+ if (reservation.reservedProviderUnits !== null && executionProvider.nativeUnit === nativeUnit) {
198
+ return {
199
+ reserved: ["reserved", "consuming"].includes(reservation.state) ? Math.max(reservation.reservedProviderUnits ?? 0, reservation.consumedProviderUnits ?? 0) : 0,
200
+ consumed: ["consumed", "failed", "overran_pending_approval"].includes(reservation.state) ? Math.max(reservation.consumedProviderUnits ?? 0, 0) : 0,
201
+ inferred: true
202
+ };
203
+ }
204
+ return { reserved: 0, consumed: 0, inferred: false };
205
+ }
206
+ function roundDownCredits(value) {
207
+ if (!Number.isFinite(value) || value <= 0) return 0;
208
+ return Math.floor(value * 100) / 100;
209
+ }
210
+ function deriveAvailableCredits(input) {
211
+ const executionProvider = input.executionProvider;
212
+ const nativeUnit = input.nativeUnit?.trim() || input.nativeLimit?.nativeUnit || executionProvider.nativeUnit;
213
+ const reasons = [];
214
+ const configuredNativeLimit = finiteOrParsedNumber(input.nativeLimit?.limitAmount);
215
+ const observationRemaining = nativeRemainingAmount(readRecord(input.latestObservation?.nativeRemaining), nativeUnit);
216
+ let nativeBase = null;
217
+ let nativeRemainingSource = "unknown";
218
+ if (observationRemaining !== null) {
219
+ nativeBase = Math.max(0, observationRemaining);
220
+ nativeRemainingSource = "observation";
221
+ reasons.push("observation_remaining");
222
+ } else if (configuredNativeLimit !== null) {
223
+ nativeBase = Math.max(0, configuredNativeLimit);
224
+ nativeRemainingSource = "configured_limit";
225
+ reasons.push(executionProvider.quotaVisibility === "opaque" ? "opaque_limit_fallback" : "configured_limit");
226
+ } else {
227
+ reasons.push("missing_native_limit");
228
+ }
229
+ const scopedReservations = (input.activeReservations ?? []).filter(
230
+ (reservation) => reservation.executionProviderId ? reservation.executionProviderId === executionProvider.id : reservation.capacityProviderId === executionProvider.capacityProviderId
231
+ );
232
+ let activeReservedNativeAmount = 0;
233
+ let activeConsumedNativeAmount = 0;
234
+ let inferredReservationCount = 0;
235
+ for (const reservation of scopedReservations) {
236
+ const debit = reservationNativeDebit(reservation, executionProvider, nativeUnit);
237
+ activeReservedNativeAmount += debit.reserved;
238
+ activeConsumedNativeAmount += debit.consumed;
239
+ if (debit.inferred && (debit.reserved > 0 || debit.consumed > 0)) inferredReservationCount += 1;
240
+ }
241
+ if (activeReservedNativeAmount > 0) reasons.push("active_native_reservations");
242
+ if (inferredReservationCount > 0) reasons.push("inferred_legacy_native_reservations");
243
+ const reserveBufferPercent = Math.max(0, finiteOrParsedNumber(input.nativeLimit?.reserveBufferPercent) ?? 0);
244
+ const reserveBufferNativeAmount = configuredNativeLimit === null ? 0 : configuredNativeLimit * reserveBufferPercent / 100;
245
+ if (reserveBufferNativeAmount > 0) reasons.push("reserve_buffer");
246
+ const availableNativeAmount = Math.max(0, (nativeBase ?? 0) - activeReservedNativeAmount - reserveBufferNativeAmount);
247
+ const profile = input.conversionProfile ?? null;
248
+ let nativeUnitsPerCredit = profile?.nativeUnitsPerCreditP90 ?? null;
249
+ let confidence = profile?.confidence ?? "low";
250
+ if (nativeUnitsPerCredit !== null && nativeUnitsPerCredit > 0) {
251
+ reasons.push("p90_conversion_profile");
252
+ } else if (profile?.nativeUnitsPerCreditP50 !== null && profile?.nativeUnitsPerCreditP50 !== void 0 && profile.nativeUnitsPerCreditP50 > 0) {
253
+ nativeUnitsPerCredit = profile.nativeUnitsPerCreditP50;
254
+ confidence = lowerDerivedConfidence(profile.confidence);
255
+ reasons.push("p50_conversion_fallback");
256
+ } else {
257
+ reasons.push("missing_conversion_profile");
258
+ }
259
+ if (nativeRemainingSource === "unknown") confidence = "low";
260
+ const derivedAvailableCredits = nativeUnitsPerCredit !== null && nativeUnitsPerCredit > 0 ? roundDownCredits(availableNativeAmount / nativeUnitsPerCredit) : null;
261
+ return {
262
+ executionProviderId: executionProvider.id,
263
+ capacityProviderId: executionProvider.capacityProviderId,
264
+ executionProviderKind: executionProvider.kind,
265
+ nativeUnit,
266
+ scope: input.scope ?? input.nativeLimit?.scope ?? null,
267
+ configuredNativeLimit,
268
+ observedNativeRemaining: observationRemaining,
269
+ nativeRemainingSource,
270
+ activeReservedNativeAmount,
271
+ activeConsumedNativeAmount,
272
+ reserveBufferPercent,
273
+ reserveBufferNativeAmount,
274
+ availableNativeAmount,
275
+ nativeUnitsPerCredit,
276
+ conversionProfileId: profile?.id ?? null,
277
+ conversionTaskSignature: profile?.taskSignature ?? null,
278
+ conversionConfidence: profile?.confidence ?? null,
279
+ derivedAvailableCredits,
280
+ confidence,
281
+ resetAt: input.latestObservation?.resetAt ?? input.nativeLimit?.resetAt ?? null,
282
+ reasons: [...new Set(reasons)],
283
+ metadata: {
284
+ quotaVisibility: executionProvider.quotaVisibility,
285
+ latestObservedAt: input.latestObservation?.observedAt ?? null,
286
+ conversionFormulaVersion: profile?.formulaVersion ?? null
287
+ }
288
+ };
289
+ }
290
+ function calculateActualCredits(input) {
291
+ const native = objectActualValue(input.nativeUsage);
292
+ const metadata = objectActualValue(native.metadata);
293
+ const wallMinutes = firstActualMetric(input.wallMinutes, native.wallMinutes, native.wall_minutes, native.durationMinutes, native.duration_minutes);
294
+ const quotaMinutes = firstActualMetric(input.quotaMinutes, native.quotaMinutes, native.quota_minutes);
295
+ const inputTokens = firstActualMetric(input.inputTokens, native.inputTokens, native.input_tokens);
296
+ const outputTokens = firstActualMetric(input.outputTokens, native.outputTokens, native.output_tokens);
297
+ const cachedInputTokens = firstActualMetric(input.cachedInputTokens, native.cachedInputTokens, native.cached_input_tokens);
298
+ const usd = firstActualMetric(input.actualUsd, native.usd, native.costUsd, native.cost_usd);
299
+ const filesOpened = firstActualMetric(input.filesOpened, native.filesOpened, native.files_opened);
300
+ const filesChanged = firstActualMetric(input.filesChanged, native.filesChanged, native.files_changed);
301
+ const diffLinesAdded = firstActualMetric(input.diffLinesAdded, native.diffLinesAdded, native.diff_lines_added);
302
+ const diffLinesRemoved = firstActualMetric(input.diffLinesRemoved, native.diffLinesRemoved, native.diff_lines_removed);
303
+ const testRuns = firstActualMetric(input.testRuns, native.testRuns, native.test_runs);
304
+ const retryCount = firstActualMetric(input.retryCount, native.retryCount, native.retry_count);
305
+ const nativeUnit = typeof native.nativeUnit === "string" ? native.nativeUnit : typeof native.native_unit === "string" ? native.native_unit : nativeUsageUnit(native) ?? (wallMinutes !== null ? "wall_minute" : quotaMinutes !== null ? "quota_minute" : usd !== null ? "usd" : Math.max(0, (inputTokens ?? 0) + (outputTokens ?? 0) - (cachedInputTokens ?? 0)) > 0 ? "token" : null);
306
+ const observedAt = typeof native.observedAt === "string" ? native.observedAt : typeof native.observed_at === "string" ? native.observed_at : null;
307
+ const partial = booleanActualFlag(native.partial) || booleanActualFlag(metadata.partial);
308
+ const interrupted = booleanActualFlag(native.interrupted) || booleanActualFlag(metadata.interrupted);
309
+ const legacyActualCredits = finiteActualMetric(input.legacyActualCredits);
310
+ const reservedCredits = finiteActualMetric(input.reservedCredits);
311
+ const components = {};
312
+ if (wallMinutes !== null && wallMinutes > 0) {
313
+ components.wallMinutes = Math.ceil(wallMinutes / 5);
314
+ } else if (quotaMinutes !== null && quotaMinutes > 0) {
315
+ components.quotaMinutes = Math.ceil(quotaMinutes / 5);
316
+ }
317
+ if (usd !== null && usd > 0) components.usd = roundUpCreditComponent(usd / 0.03);
318
+ const billableTokens = Math.max(0, (inputTokens ?? 0) + (outputTokens ?? 0) - (cachedInputTokens ?? 0));
319
+ if (billableTokens > 0) components.tokens = Math.ceil(billableTokens / 8e3);
320
+ if (filesChanged !== null && filesChanged > 0) components.filesChanged = Math.ceil(filesChanged) * 2;
321
+ if (testRuns !== null && testRuns > 0) components.testRuns = Math.ceil(testRuns);
322
+ if (retryCount !== null && retryCount > 0) components.retryCount = Math.ceil(retryCount) * 3;
323
+ const nativeUsage = {
324
+ ...native,
325
+ nativeUnit,
326
+ wallMinutes,
327
+ quotaMinutes,
328
+ inputTokens,
329
+ outputTokens,
330
+ cachedInputTokens,
331
+ usd,
332
+ filesOpened,
333
+ filesChanged,
334
+ diffLinesAdded,
335
+ diffLinesRemoved,
336
+ testRuns,
337
+ retryCount,
338
+ partial,
339
+ interrupted,
340
+ source: typeof native.source === "string" ? native.source : input.source ?? null,
341
+ observedAt,
342
+ metadata
343
+ };
344
+ const componentTotal = Object.values(components).reduce((total, value) => total + value, 0);
345
+ const conversionProfile = input.conversionProfile ?? null;
346
+ const conversionProfileNativeAmount = conversionProfile ? nativeUsageAmount(nativeUsage, conversionProfile.nativeUnit) : null;
347
+ const conversionProfileCredits = conversionProfileNativeAmount !== null && conversionProfileNativeAmount > 0 && conversionProfile.nativeUnitsPerCreditP50 !== null && conversionProfile.nativeUnitsPerCreditP50 > 0 ? roundActualCredits(conversionProfileNativeAmount / conversionProfile.nativeUnitsPerCreditP50) : null;
348
+ if (legacyActualCredits !== null && input.actualCreditsOverride === true) {
349
+ return {
350
+ actualCredits: roundActualCredits(legacyActualCredits),
351
+ formulaVersion: ACTUAL_CREDIT_FORMULA_VERSION,
352
+ source: "legacy_override",
353
+ nativeUsage,
354
+ components,
355
+ partial,
356
+ interrupted,
357
+ conversionProfileId: conversionProfile?.id ?? null,
358
+ conversionConfidence: conversionProfile?.confidence ?? null
359
+ };
360
+ }
361
+ if (conversionProfile?.confidence === "high" && conversionProfileCredits !== null) {
362
+ return {
363
+ actualCredits: conversionProfileCredits,
364
+ formulaVersion: ACTUAL_CREDIT_FORMULA_VERSION,
365
+ source: "conversion_profile",
366
+ nativeUsage,
367
+ components: {
368
+ ...components,
369
+ conversionProfile: conversionProfileCredits
370
+ },
371
+ partial,
372
+ interrupted,
373
+ conversionProfileId: conversionProfile.id ?? null,
374
+ conversionConfidence: conversionProfile.confidence
375
+ };
376
+ }
377
+ if (conversionProfile?.confidence === "medium" && conversionProfileCredits !== null && componentTotal > 0) {
378
+ return {
379
+ actualCredits: roundActualCredits((conversionProfileCredits + componentTotal) / 2),
380
+ formulaVersion: ACTUAL_CREDIT_FORMULA_VERSION,
381
+ source: "blended_conversion_profile",
382
+ nativeUsage,
383
+ components: {
384
+ ...components,
385
+ conversionProfile: conversionProfileCredits,
386
+ bootstrap: roundActualCredits(componentTotal)
387
+ },
388
+ partial,
389
+ interrupted,
390
+ conversionProfileId: conversionProfile.id ?? null,
391
+ conversionConfidence: conversionProfile.confidence
392
+ };
393
+ }
394
+ if (componentTotal > 0) {
395
+ return {
396
+ actualCredits: roundActualCredits(componentTotal),
397
+ formulaVersion: ACTUAL_CREDIT_FORMULA_VERSION,
398
+ source: "central_calculator",
399
+ nativeUsage,
400
+ components,
401
+ partial,
402
+ interrupted,
403
+ conversionProfileId: conversionProfile?.id ?? null,
404
+ conversionConfidence: conversionProfile?.confidence ?? null
405
+ };
406
+ }
407
+ if (legacyActualCredits !== null) {
408
+ return {
409
+ actualCredits: roundActualCredits(legacyActualCredits),
410
+ formulaVersion: ACTUAL_CREDIT_FORMULA_VERSION,
411
+ source: "legacy_fallback",
412
+ nativeUsage,
413
+ components,
414
+ partial,
415
+ interrupted,
416
+ conversionProfileId: conversionProfile?.id ?? null,
417
+ conversionConfidence: conversionProfile?.confidence ?? null
418
+ };
419
+ }
420
+ if (reservedCredits !== null) {
421
+ return {
422
+ actualCredits: roundActualCredits(reservedCredits),
423
+ formulaVersion: ACTUAL_CREDIT_FORMULA_VERSION,
424
+ source: "reserved_fallback",
425
+ nativeUsage,
426
+ components,
427
+ partial,
428
+ interrupted,
429
+ conversionProfileId: conversionProfile?.id ?? null,
430
+ conversionConfidence: conversionProfile?.confidence ?? null
431
+ };
432
+ }
433
+ return {
434
+ actualCredits: 0,
435
+ formulaVersion: ACTUAL_CREDIT_FORMULA_VERSION,
436
+ source: "zero_fallback",
437
+ nativeUsage,
438
+ components,
439
+ partial,
440
+ interrupted,
441
+ conversionProfileId: conversionProfile?.id ?? null,
442
+ conversionConfidence: conversionProfile?.confidence ?? null
443
+ };
444
+ }
1
445
  const DEFAULT_EXECUTION_PROFILE_ID = "standard-code-model";
2
446
  function finiteNumber(value) {
3
447
  return typeof value === "number" && Number.isFinite(value) ? value : null;
@@ -10,6 +454,9 @@ function finiteOrParsedNumber(value) {
10
454
  }
11
455
  return null;
12
456
  }
457
+ function stringValue(value) {
458
+ return typeof value === "string" && value.trim() ? value.trim() : null;
459
+ }
13
460
  function positiveNumber(value, fallback) {
14
461
  const parsed = finiteNumber(value);
15
462
  return parsed !== null && parsed > 0 ? parsed : fallback;
@@ -649,6 +1096,21 @@ function activeReservationDebit(reservation) {
649
1096
  }
650
1097
  return 0;
651
1098
  }
1099
+ function metadataNumber(metadata, ...keys) {
1100
+ for (const key of keys) {
1101
+ const value = finiteOrParsedNumber(metadata?.[key]);
1102
+ if (value !== null) return value;
1103
+ }
1104
+ return null;
1105
+ }
1106
+ function metadataBoolean(metadata, ...keys) {
1107
+ for (const key of keys) {
1108
+ const value = metadata?.[key];
1109
+ if (value === true || value === "true" || value === 1 || value === "1") return true;
1110
+ if (value === false || value === "false" || value === 0 || value === "0") return false;
1111
+ }
1112
+ return false;
1113
+ }
652
1114
  function grantMatchesReservation(grant, reservation) {
653
1115
  if (grant.teamId !== reservation.teamId) return false;
654
1116
  if (grant.capacityProviderId !== reservation.capacityProviderId) return false;
@@ -656,11 +1118,47 @@ function grantMatchesReservation(grant, reservation) {
656
1118
  if (grant.projectId && grant.projectId !== reservation.projectId) return false;
657
1119
  return true;
658
1120
  }
659
- function grantRemainingCredits(plan, grant) {
660
- const limit = grant.dailyCreditLimit ?? grant.monthlyCreditLimit;
1121
+ function grantPortfolioAllocationPercent(grant) {
1122
+ const metadata = readRecord(grant.metadata);
1123
+ const value = finiteOrParsedNumber(grant.portfolioAllocationPercent) ?? metadataNumber(metadata, "portfolioAllocationPercent", "allocationPercent", "derivedAllocationPercent", "percentOfDerivedCapacity");
1124
+ if (value === null) return null;
1125
+ return Math.max(0, Math.min(100, value));
1126
+ }
1127
+ function grantReservePoolPercent(grant) {
1128
+ const metadata = readRecord(grant.metadata);
1129
+ const value = finiteOrParsedNumber(grant.reservePoolPercent) ?? metadataNumber(metadata, "reservePoolPercent", "minimumReservePercent", "reservePercent");
1130
+ if (value === null) return 0;
1131
+ return Math.max(0, Math.min(100, value));
1132
+ }
1133
+ function grantMaxDailyProjectCredits(grant) {
1134
+ const metadata = readRecord(grant.metadata);
1135
+ const value = finiteOrParsedNumber(grant.maxDailyProjectCredits) ?? metadataNumber(metadata, "maxDailyProjectCredits", "dailyProjectCreditCap");
1136
+ if (value === null) return null;
1137
+ return Math.max(0, value);
1138
+ }
1139
+ function grantEmergencyOverrideEnabled(grant) {
1140
+ return grant.emergencyOverride === true || metadataBoolean(readRecord(grant.metadata), "emergencyOverride", "emergencyOverrideEnabled");
1141
+ }
1142
+ function routeEmergencyOverrideRequested(input) {
1143
+ return metadataBoolean(readRecord(input.metadata), "emergencyOverride", "emergencyOverrideRequested");
1144
+ }
1145
+ function grantPortfolioCreditLimit(grant, derivedCapacity) {
1146
+ const percent = grantPortfolioAllocationPercent(grant);
1147
+ if (percent === null || !derivedCapacity || derivedCapacity.derivedAvailableCredits === null) return null;
1148
+ return Math.max(0, derivedCapacity.derivedAvailableCredits * percent / 100);
1149
+ }
1150
+ function grantRemainingCredits(plan, grant, derivedCapacity, input) {
1151
+ const staticLimit = grant.dailyCreditLimit ?? grant.monthlyCreditLimit;
1152
+ const portfolioLimit = grantPortfolioCreditLimit(grant, derivedCapacity);
1153
+ const maxDailyProjectCredits = grantMaxDailyProjectCredits(grant);
1154
+ const limits = [staticLimit, portfolioLimit, maxDailyProjectCredits].filter((value) => value !== null && value !== void 0);
1155
+ const limit = limits.length > 0 ? Math.min(...limits.map((value) => Number(value))) : null;
661
1156
  if (limit === null || limit === void 0) return null;
1157
+ const reservePoolPercent = portfolioLimit !== null ? grantReservePoolPercent(grant) : 0;
1158
+ const emergencyOverride = input ? routeEmergencyOverrideRequested(input) && grantEmergencyOverrideEnabled(grant) : false;
1159
+ const reservePoolCredits = emergencyOverride ? 0 : limit * reservePoolPercent / 100;
662
1160
  const debits = plan.activeReservations.filter((reservation) => grantMatchesReservation(grant, reservation)).reduce((total, reservation) => total + reservationDebit(reservation), 0);
663
- return Math.max(0, Number(limit) - debits);
1161
+ return Math.max(0, Number(limit) - reservePoolCredits - debits);
664
1162
  }
665
1163
  function providerIsEligible(provider, input) {
666
1164
  if (provider.status === "active") return true;
@@ -1239,6 +1737,120 @@ function routePriceMultiplier(provider, lane, profile) {
1239
1737
  const profileMetadata = readRecord(profile.metadata);
1240
1738
  return Math.max(0.1, finiteOrParsedNumber(laneMetadata.priceMultiplier) ?? finiteOrParsedNumber(providerMetadata.priceMultiplier) ?? finiteOrParsedNumber(profileMetadata.priceMultiplier) ?? 1);
1241
1739
  }
1740
+ function capacityBudgetMode(provider) {
1741
+ const mode = String(provider.creditBudgetMode ?? "derived").toLowerCase();
1742
+ if (mode === "static" || mode === "hybrid" || mode === "derived") return mode;
1743
+ return "derived";
1744
+ }
1745
+ function routeDerivedEntries(plan) {
1746
+ const seen = /* @__PURE__ */ new Set();
1747
+ const entries = [];
1748
+ const add = (entry) => {
1749
+ if (!entry) return;
1750
+ const key = `${entry.executionProviderId}:${entry.nativeUnit}:${entry.scope ?? ""}`;
1751
+ if (seen.has(key)) return;
1752
+ seen.add(key);
1753
+ entries.push(entry);
1754
+ };
1755
+ for (const entry of plan.derivedCapacity?.entries ?? []) add(entry);
1756
+ for (const summary of plan.derivedCapacity?.providers ?? []) {
1757
+ for (const entry of summary.entries ?? []) add(entry);
1758
+ }
1759
+ return entries;
1760
+ }
1761
+ function routeDerivedHints(provider, lane, profile) {
1762
+ const providerMetadata = readRecord(provider.metadata);
1763
+ const laneMetadata = readRecord(lane.metadata);
1764
+ const profileMetadata = readRecord(profile.metadata);
1765
+ return {
1766
+ executionProviderId: stringValue(laneMetadata.executionProviderId) ?? stringValue(providerMetadata.executionProviderId) ?? stringValue(profileMetadata.executionProviderId),
1767
+ executionProviderKind: stringValue(laneMetadata.executionProviderKind) ?? stringValue(laneMetadata.providerKind) ?? stringValue(providerMetadata.executionProviderKind) ?? stringValue(providerMetadata.providerKind) ?? stringValue(profileMetadata.executionProviderKind),
1768
+ nativeUnit: stringValue(laneMetadata.nativeUnit) ?? stringValue(providerMetadata.nativeUnit) ?? stringValue(profileMetadata.nativeUnit)
1769
+ };
1770
+ }
1771
+ function selectDerivedCapacityForRoute(plan, provider, lane, profile) {
1772
+ const hints = routeDerivedHints(provider, lane, profile);
1773
+ const entries = routeDerivedEntries(plan).filter((entry) => entry.capacityProviderId === provider.id || entry.capacityProviderId === null).filter((entry) => !hints.executionProviderId || entry.executionProviderId === hints.executionProviderId).filter((entry) => !hints.executionProviderKind || entry.executionProviderKind === hints.executionProviderKind).filter((entry) => !hints.nativeUnit || entry.nativeUnit === hints.nativeUnit);
1774
+ return entries.sort((left, right) => {
1775
+ const leftExact = (hints.executionProviderId && left.executionProviderId === hints.executionProviderId ? 20 : 0) + (hints.executionProviderKind && left.executionProviderKind === hints.executionProviderKind ? 10 : 0) + (hints.nativeUnit && left.nativeUnit === hints.nativeUnit ? 5 : 0);
1776
+ const rightExact = (hints.executionProviderId && right.executionProviderId === hints.executionProviderId ? 20 : 0) + (hints.executionProviderKind && right.executionProviderKind === hints.executionProviderKind ? 10 : 0) + (hints.nativeUnit && right.nativeUnit === hints.nativeUnit ? 5 : 0);
1777
+ return rightExact - leftExact || derivedConfidenceRank(right.confidence) - derivedConfidenceRank(left.confidence) || Number(left.derivedAvailableCredits ?? Number.POSITIVE_INFINITY) - Number(right.derivedAvailableCredits ?? Number.POSITIVE_INFINITY) || Number(left.availableNativeAmount ?? Number.POSITIVE_INFINITY) - Number(right.availableNativeAmount ?? Number.POSITIVE_INFINITY) || `${left.executionProviderId}:${left.nativeUnit}`.localeCompare(`${right.executionProviderId}:${right.nativeUnit}`);
1778
+ })[0] ?? null;
1779
+ }
1780
+ function roundUpNativeAmount(value) {
1781
+ if (!Number.isFinite(value) || value <= 0) return 0;
1782
+ return Math.ceil(value * 1e3) / 1e3;
1783
+ }
1784
+ function nativePressureForRoute(entry, estimate) {
1785
+ if (!entry) return null;
1786
+ const reservedNativeAmount = entry.nativeUnitsPerCredit && entry.nativeUnitsPerCredit > 0 ? roundUpNativeAmount(estimate.reservedCredits * entry.nativeUnitsPerCredit) : null;
1787
+ const pressureRatio = reservedNativeAmount !== null && entry.availableNativeAmount > 0 ? reservedNativeAmount / entry.availableNativeAmount : entry.availableNativeAmount <= 0 ? 1 : null;
1788
+ return {
1789
+ executionProviderId: entry.executionProviderId,
1790
+ nativeUnit: entry.nativeUnit,
1791
+ availableNativeAmount: entry.availableNativeAmount,
1792
+ activeReservedNativeAmount: entry.activeReservedNativeAmount,
1793
+ reserveBufferNativeAmount: entry.reserveBufferNativeAmount,
1794
+ reservedNativeAmount,
1795
+ pressureRatio,
1796
+ confidence: entry.confidence,
1797
+ reasons: entry.reasons
1798
+ };
1799
+ }
1800
+ function derivedRouteBudget(input) {
1801
+ const reasons = [];
1802
+ const derivedCredits = input.derivedCapacity?.derivedAvailableCredits ?? null;
1803
+ const confidenceRank = derivedConfidenceRank(input.derivedCapacity?.confidence);
1804
+ const hasUsableDerived = Boolean(input.derivedCapacity && derivedCredits !== null && confidenceRank >= 2);
1805
+ const portfolioPercent = grantPortfolioAllocationPercent(input.grant);
1806
+ let remainingCredits = input.staticRemainingCredits;
1807
+ let appliesDerived = false;
1808
+ if (input.mode === "derived") {
1809
+ appliesDerived = true;
1810
+ if (!input.derivedCapacity) {
1811
+ reasons.push("missing_derived_capacity", "insufficient_budget");
1812
+ return { remainingCredits: null, appliesDerived, hasUsableDerived: false, reasons };
1813
+ }
1814
+ if (derivedCredits === null) {
1815
+ reasons.push("missing_conversion_profile", "insufficient_budget");
1816
+ return { remainingCredits: null, appliesDerived, hasUsableDerived: false, reasons };
1817
+ }
1818
+ if (confidenceRank < 2) {
1819
+ reasons.push("derived_capacity_learning", "insufficient_budget");
1820
+ return { remainingCredits: derivedCredits, appliesDerived, hasUsableDerived: false, reasons };
1821
+ }
1822
+ remainingCredits = input.staticRemainingCredits === null ? derivedCredits : Math.min(input.staticRemainingCredits, derivedCredits);
1823
+ reasons.push("derived_capacity_available");
1824
+ if (portfolioPercent !== null) reasons.push("portfolio_allocation_applied");
1825
+ } else if (input.mode === "hybrid") {
1826
+ if (hasUsableDerived) {
1827
+ appliesDerived = true;
1828
+ remainingCredits = input.staticRemainingCredits === null ? derivedCredits : Math.min(input.staticRemainingCredits, derivedCredits);
1829
+ reasons.push("hybrid_derived_capacity_applied");
1830
+ if (portfolioPercent !== null) reasons.push("portfolio_allocation_applied");
1831
+ if (input.staticRemainingCredits !== null && input.staticRemainingCredits <= derivedCredits) {
1832
+ reasons.push("hybrid_static_cap_applied");
1833
+ }
1834
+ } else if (input.derivedCapacity && derivedCredits !== null && confidenceRank < 2) {
1835
+ reasons.push("derived_capacity_learning");
1836
+ } else if (input.derivedCapacity && derivedCredits === null) {
1837
+ reasons.push("missing_conversion_profile");
1838
+ }
1839
+ }
1840
+ if (appliesDerived && hasUsableDerived && derivedCredits !== null && derivedCredits < input.estimate.reservedCredits) {
1841
+ reasons.push("derived_capacity_exhausted");
1842
+ if (input.grant.overflowPolicy === "approval_required") {
1843
+ reasons.push("approval_required");
1844
+ } else if (input.grant.overflowPolicy === "fallback_lane") {
1845
+ reasons.push("fallback_lane_exhausted");
1846
+ } else if (input.grant.overflowPolicy === "deny" || input.grant.overflowPolicy === "hard_grant" || input.mode === "derived") {
1847
+ reasons.push("insufficient_budget");
1848
+ } else {
1849
+ reasons.push("soft_budget_pressure", "derived_capacity_pressure");
1850
+ }
1851
+ }
1852
+ return { remainingCredits, appliesDerived, hasUsableDerived, reasons };
1853
+ }
1242
1854
  function routeScore(input) {
1243
1855
  const reasons = [...input.baseScore.reasons];
1244
1856
  const qualityFit = input.minimumQualityWeight > 0 ? input.profile.qualityWeight / input.minimumQualityWeight : input.profile.qualityWeight;
@@ -1255,12 +1867,14 @@ function routeScore(input) {
1255
1867
  const predictedReserveImpact = input.reservePrediction?.reserveCredits ?? 0;
1256
1868
  const laneModelFit = input.profile.modelClass && input.lane.modelClass === input.profile.modelClass ? 15 : 0;
1257
1869
  const riskBonus = input.minimumQualityWeight >= 1.25 && input.profile.qualityWeight >= input.minimumQualityWeight ? 10 : 0;
1258
- const score = input.baseScore.score + qualityBonus + laneModelFit + riskBonus + utilityBonus + cooperativeBonus - costPenalty - latency - congestion - quota - attention - context - Math.max(0, priceMultiplier - 1) * 8 - (input.reservePrediction && input.reservePrediction.reservePercent > 0 ? Math.min(20, predictedReserveImpact * 0.25) : 0);
1870
+ const nativePressurePenalty = input.nativePressure?.pressureRatio !== null && input.nativePressure?.pressureRatio !== void 0 ? Math.max(0, input.nativePressure.pressureRatio) * 35 : 0;
1871
+ const score = input.baseScore.score + qualityBonus + laneModelFit + riskBonus + utilityBonus + cooperativeBonus - costPenalty - latency - congestion - quota - attention - context - nativePressurePenalty - Math.max(0, priceMultiplier - 1) * 8 - (input.reservePrediction && input.reservePrediction.reservePercent > 0 ? Math.min(20, predictedReserveImpact * 0.25) : 0);
1259
1872
  if (laneModelFit > 0) reasons.push("execution_profile_model_class_match");
1260
1873
  if (congestion > 0) reasons.push("lane_congestion_pressure");
1261
1874
  if (quota > 0) reasons.push("quota_pressure");
1262
1875
  if (attention > 0) reasons.push("attention_pressure");
1263
1876
  if (context > 0) reasons.push("context_pressure");
1877
+ if (nativePressurePenalty > 0) reasons.push("native_capacity_pressure");
1264
1878
  if (utilityBonus > 0) reasons.push("utility_scored");
1265
1879
  if (cooperativeBonus > 0) reasons.push("cooperative_route_scored");
1266
1880
  if (predictedReserveImpact > 0) reasons.push("predictive_reserve_applied");
@@ -1273,6 +1887,7 @@ function routeScore(input) {
1273
1887
  congestionPenalty: congestion,
1274
1888
  attentionPenalty: attention,
1275
1889
  contextPenalty: context,
1890
+ nativePressurePenalty,
1276
1891
  utilityScore: input.utilityEstimate.utilityScore,
1277
1892
  utilityPerCredit: input.utilityEstimate.utilityPerCredit,
1278
1893
  predictedReserveImpact,
@@ -1310,10 +1925,21 @@ function routeAndReserveCapacity(input) {
1310
1925
  (lane2) => lane2.capacityProviderId === provider2.id && (!grant2.laneId || grant2.laneId === lane2.id)
1311
1926
  );
1312
1927
  for (const lane2 of lanes) {
1313
- const remainingCredits = grantRemainingCredits(input.plan, grant2);
1314
1928
  const pressure = capacityRoutePressure(input.plan, provider2, lane2);
1315
1929
  for (const profile of executionProfiles) {
1316
1930
  const estimate = estimateForRouteProfile(input, profile);
1931
+ const derivedCapacityMode = capacityBudgetMode(provider2);
1932
+ const derivedCapacity = derivedCapacityMode === "static" ? null : selectDerivedCapacityForRoute(input.plan, provider2, lane2, profile);
1933
+ const staticRemainingCredits = grantRemainingCredits(input.plan, grant2, derivedCapacity, input);
1934
+ const routeBudget = derivedRouteBudget({
1935
+ mode: derivedCapacityMode,
1936
+ staticRemainingCredits,
1937
+ derivedCapacity,
1938
+ estimate,
1939
+ grant: grant2
1940
+ });
1941
+ const remainingCredits = routeBudget.remainingCredits;
1942
+ const nativePressure = nativePressureForRoute(derivedCapacity, estimate);
1317
1943
  const attentionEstimate = attentionEstimateForRoute(input, profile);
1318
1944
  const utilityEstimate = input.utilityEstimate ?? estimateUtilityForTask({
1319
1945
  classification: input.classification,
@@ -1343,7 +1969,7 @@ function routeAndReserveCapacity(input) {
1343
1969
  utilityEstimate
1344
1970
  });
1345
1971
  const estimateInput = { ...input, estimate };
1346
- const reasons = lanePolicyReasons(lane2, estimateInput);
1972
+ const reasons = [...routeBudget.reasons, ...lanePolicyReasons(lane2, estimateInput)];
1347
1973
  let spilloverReason = null;
1348
1974
  if (trustRequirement !== null && trustScore < trustRequirement) reasons.push("trust_below_requirement");
1349
1975
  if (utilityPolicy.minimumUtilityScore !== null && utilityEstimate.utilityScore < utilityPolicy.minimumUtilityScore) reasons.push("utility_below_minimum");
@@ -1380,6 +2006,7 @@ function routeAndReserveCapacity(input) {
1380
2006
  if (pressure.quotaRemainingPercent !== null && pressure.quotaRemainingPercent <= 0) reasons.push("quota_exhausted");
1381
2007
  if (pressure.sessionRemainingMinutes !== null && pressure.sessionRemainingMinutes <= 0) reasons.push("session_exhausted");
1382
2008
  if (remainingCredits !== null && remainingCredits < estimate.reservedCredits) {
2009
+ if (grantPortfolioAllocationPercent(grant2) !== null) reasons.push("portfolio_allocation_exhausted");
1383
2010
  if (grant2.overflowPolicy === "approval_required") {
1384
2011
  reasons.push("approval_required");
1385
2012
  } else if (grant2.overflowPolicy === "fallback_lane") {
@@ -1415,21 +2042,30 @@ function routeAndReserveCapacity(input) {
1415
2042
  trustScore,
1416
2043
  successProbability,
1417
2044
  cooperativeRouting,
1418
- baseScore
2045
+ baseScore,
2046
+ nativePressure
1419
2047
  });
1420
2048
  candidates.push({
1421
2049
  providerId: provider2.id,
1422
2050
  laneId: lane2.id,
1423
2051
  grantId: grant2.id,
2052
+ executionProviderId: routeBudget.appliesDerived && routeBudget.hasUsableDerived ? derivedCapacity?.executionProviderId ?? null : null,
1424
2053
  executionProfileId: profile.id,
2054
+ nativeUnit: routeBudget.appliesDerived && routeBudget.hasUsableDerived ? derivedCapacity?.nativeUnit ?? null : null,
1425
2055
  remainingCredits,
2056
+ staticRemainingCredits,
2057
+ derivedAvailableCredits: derivedCapacity?.derivedAvailableCredits ?? null,
2058
+ reservedNativeAmount: routeBudget.appliesDerived && routeBudget.hasUsableDerived ? nativePressure?.reservedNativeAmount ?? null : null,
2059
+ derivedCapacity: derivedCapacity ?? null,
2060
+ derivedCapacityMode,
1426
2061
  score,
1427
2062
  eligible: reasons.filter(
1428
- (reason) => reason !== "soft_budget_pressure" && reason !== "execution_profile_not_preferred"
2063
+ (reason) => reason !== "soft_budget_pressure" && reason !== "execution_profile_not_preferred" && reason !== "derived_capacity_available" && reason !== "hybrid_derived_capacity_applied" && reason !== "hybrid_static_cap_applied" && reason !== "portfolio_allocation_applied" && reason !== "derived_capacity_learning" && reason !== "missing_conversion_profile"
1429
2064
  ).length === 0,
1430
2065
  reasons: [.../* @__PURE__ */ new Set([...reasons, ...score.reasons])],
1431
2066
  estimate,
1432
2067
  pressure,
2068
+ nativePressure,
1433
2069
  qualityFit: score.qualityFit,
1434
2070
  attentionEstimate,
1435
2071
  utilityEstimate,
@@ -1491,8 +2127,14 @@ function routeAndReserveCapacity(input) {
1491
2127
  providerId: candidate.providerId,
1492
2128
  laneId: candidate.laneId,
1493
2129
  grantId: candidate.grantId,
2130
+ executionProviderId: candidate.executionProviderId ?? null,
1494
2131
  executionProfileId: candidate.executionProfileId ?? null,
2132
+ nativeUnit: candidate.nativeUnit ?? null,
1495
2133
  remainingCredits: candidate.remainingCredits,
2134
+ staticRemainingCredits: candidate.staticRemainingCredits ?? null,
2135
+ derivedAvailableCredits: candidate.derivedAvailableCredits ?? null,
2136
+ reservedNativeAmount: candidate.reservedNativeAmount ?? null,
2137
+ derivedCapacityMode: candidate.derivedCapacityMode ?? "derived",
1496
2138
  eligible: candidate.eligible,
1497
2139
  reasons: candidate.reasons,
1498
2140
  score: candidate.score.score,
@@ -1504,6 +2146,8 @@ function routeAndReserveCapacity(input) {
1504
2146
  trustScore: candidate.trustScore ?? null,
1505
2147
  successProbability: candidate.successProbability ?? null,
1506
2148
  pressure: candidate.pressure ?? null,
2149
+ nativePressure: candidate.nativePressure ?? null,
2150
+ derivedCapacity: candidate.derivedCapacity ?? null,
1507
2151
  spilloverReason: candidate.spilloverReason ?? null
1508
2152
  }));
1509
2153
  const scorePayload = Object.fromEntries(candidates.map((candidate) => [routeCandidateKey(candidate), candidate.score]));
@@ -1516,9 +2160,18 @@ function routeAndReserveCapacity(input) {
1516
2160
  taskId: input.taskId ?? null,
1517
2161
  state: "reserved",
1518
2162
  reservedCredits: selectedEstimate.reservedCredits,
2163
+ executionProviderId: selected.executionProviderId ?? null,
2164
+ nativeUnit: selected.nativeUnit ?? null,
2165
+ reservedNativeAmount: selected.reservedNativeAmount ?? null,
1519
2166
  metadata: {
1520
2167
  ...input.metadata ?? {},
1521
2168
  grantId: grant.id,
2169
+ derivedCapacityMode: selected.derivedCapacityMode ?? "derived",
2170
+ executionProviderId: selected.executionProviderId ?? null,
2171
+ nativeUnit: selected.nativeUnit ?? null,
2172
+ reservedNativeAmount: selected.reservedNativeAmount ?? null,
2173
+ derivedAvailableCredits: selected.derivedAvailableCredits ?? null,
2174
+ nativePressure: selected.nativePressure ?? null,
1522
2175
  executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
1523
2176
  taskSignature: selectedEstimate.taskSignature,
1524
2177
  estimatedCreditsP50: selectedEstimate.estimatedCreditsP50,
@@ -1547,6 +2200,13 @@ function routeAndReserveCapacity(input) {
1547
2200
  grantId: grant.id,
1548
2201
  executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
1549
2202
  remainingCreditsBefore: selected.remainingCredits,
2203
+ staticRemainingCreditsBefore: selected.staticRemainingCredits ?? null,
2204
+ derivedCapacityMode: selected.derivedCapacityMode ?? "derived",
2205
+ executionProviderId: selected.executionProviderId ?? null,
2206
+ nativeUnit: selected.nativeUnit ?? null,
2207
+ reservedNativeAmount: selected.reservedNativeAmount ?? null,
2208
+ derivedAvailableCreditsBefore: selected.derivedAvailableCredits ?? null,
2209
+ nativePressure: selected.nativePressure ?? null,
1550
2210
  reservedCredits: selectedEstimate.reservedCredits,
1551
2211
  attentionEstimate: selected.attentionEstimate ?? null,
1552
2212
  utilityEstimate: selected.utilityEstimate ?? null,
@@ -1569,6 +2229,12 @@ function routeAndReserveCapacity(input) {
1569
2229
  ...input.metadata ?? {},
1570
2230
  grantId: grant.id,
1571
2231
  executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
2232
+ derivedCapacityMode: selected.derivedCapacityMode ?? "derived",
2233
+ executionProviderId: selected.executionProviderId ?? null,
2234
+ nativeUnit: selected.nativeUnit ?? null,
2235
+ reservedNativeAmount: selected.reservedNativeAmount ?? null,
2236
+ derivedAvailableCredits: selected.derivedAvailableCredits ?? null,
2237
+ nativePressure: selected.nativePressure ?? null,
1572
2238
  taskSignature: selectedEstimate.taskSignature,
1573
2239
  attentionEstimate: selected.attentionEstimate ?? null,
1574
2240
  utilityEstimate: selected.utilityEstimate ?? null,
@@ -1596,6 +2262,12 @@ function routeAndReserveCapacity(input) {
1596
2262
  estimatedCreditsP90: selectedEstimate.estimatedCreditsP90,
1597
2263
  reservedCredits: selectedEstimate.reservedCredits,
1598
2264
  executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
2265
+ executionProviderId: selected.executionProviderId ?? null,
2266
+ nativeUnit: selected.nativeUnit ?? null,
2267
+ reservedNativeAmount: selected.reservedNativeAmount ?? null,
2268
+ derivedAvailableCredits: selected.derivedAvailableCredits ?? null,
2269
+ derivedCapacityMode: selected.derivedCapacityMode ?? "derived",
2270
+ nativePressure: selected.nativePressure ?? null,
1599
2271
  costMultiplier: selectedEstimate.costMultiplier ?? null,
1600
2272
  score: selected.score.score,
1601
2273
  attentionEstimate: selected.attentionEstimate ?? null,
@@ -1608,14 +2280,18 @@ function routeAndReserveCapacity(input) {
1608
2280
  };
1609
2281
  }
1610
2282
  export {
2283
+ ACTUAL_CREDIT_FORMULA_VERSION,
1611
2284
  DEFAULT_EXECUTION_PROFILES,
1612
2285
  DEFAULT_EXECUTION_PROFILE_ID,
1613
2286
  DEFAULT_TASK_ADMISSION_POLICY,
2287
+ buildCreditConversionProfileFromActuals,
1614
2288
  buildTaskEstimateProfileFromActuals,
2289
+ calculateActualCredits,
1615
2290
  capacityRoutePressure,
1616
2291
  computeWorkdayBudgetEnvelope,
1617
2292
  createReservationReleaseEntry,
1618
2293
  decideTaskAdmission,
2294
+ deriveAvailableCredits,
1619
2295
  estimateAttentionForTask,
1620
2296
  estimateConfidenceFromProfile,
1621
2297
  estimateForClassification,
@@ -1625,6 +2301,8 @@ export {
1625
2301
  estimateUtilityForTask,
1626
2302
  isInterruptedUsageActual,
1627
2303
  mutationRequiresRepositoryClaim,
2304
+ nativeUsageAmount,
2305
+ nativeUsageUnit,
1628
2306
  normalizeAttentionPolicy,
1629
2307
  normalizeExecutionProfile,
1630
2308
  normalizeHybridExecutionPlan,
@@ -1641,6 +2319,7 @@ export {
1641
2319
  routeAndReserveCapacity,
1642
2320
  scoreCapacityLane,
1643
2321
  selectBestCapacityLane,
2322
+ selectCreditConversionProfile,
1644
2323
  selectTaskEstimateProfile,
1645
2324
  settleCapacityActuals,
1646
2325
  shouldInterruptForCapacity,