@treeseed/sdk 0.8.11 → 0.8.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.
- package/README.md +7 -0
- package/dist/capacity.d.ts +233 -1
- package/dist/capacity.js +1227 -44
- package/dist/control-plane.d.ts +2 -1
- package/dist/control-plane.js +7 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +61 -1
- package/dist/operations/services/d1-migration.js +27 -0
- package/dist/operations/services/deploy.js +8 -8
- package/dist/scripts/tenant-d1-migrate-local.js +1 -0
- package/dist/sdk-types.d.ts +231 -0
- package/package.json +1 -1
package/dist/capacity.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
|
+
const DEFAULT_EXECUTION_PROFILE_ID = "standard-code-model";
|
|
1
2
|
function finiteNumber(value) {
|
|
2
3
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
3
4
|
}
|
|
5
|
+
function finiteOrParsedNumber(value) {
|
|
6
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7
|
+
if (typeof value === "string" && value.trim()) {
|
|
8
|
+
const parsed = Number.parseFloat(value);
|
|
9
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function positiveNumber(value, fallback) {
|
|
14
|
+
const parsed = finiteNumber(value);
|
|
15
|
+
return parsed !== null && parsed > 0 ? parsed : fallback;
|
|
16
|
+
}
|
|
17
|
+
function nonNegativeNumber(value, fallback) {
|
|
18
|
+
const parsed = finiteNumber(value);
|
|
19
|
+
return parsed !== null && parsed >= 0 ? parsed : fallback;
|
|
20
|
+
}
|
|
4
21
|
function scarcityPenalty(level) {
|
|
5
22
|
if (level === "high") return 35;
|
|
6
23
|
if (level === "medium") return 15;
|
|
@@ -19,6 +36,601 @@ function booleanValue(value) {
|
|
|
19
36
|
function numberValue(value) {
|
|
20
37
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
21
38
|
}
|
|
39
|
+
function profileIdFrom(input) {
|
|
40
|
+
if (!input) return DEFAULT_EXECUTION_PROFILE_ID;
|
|
41
|
+
if (typeof input === "string") return input.trim() || DEFAULT_EXECUTION_PROFILE_ID;
|
|
42
|
+
return input.id?.trim() || DEFAULT_EXECUTION_PROFILE_ID;
|
|
43
|
+
}
|
|
44
|
+
function metadataFlag(metadata, key) {
|
|
45
|
+
return metadata?.[key] === true || metadata?.[key] === "true";
|
|
46
|
+
}
|
|
47
|
+
function numericActuals(values) {
|
|
48
|
+
return values.map((value) => finiteOrParsedNumber(value)).filter((value) => value !== null && value >= 0).sort((left, right) => left - right);
|
|
49
|
+
}
|
|
50
|
+
function estimateLearningPercentile(values, percentile) {
|
|
51
|
+
const sorted = numericActuals(values);
|
|
52
|
+
if (sorted.length === 0) return null;
|
|
53
|
+
const bounded = Math.min(100, Math.max(0, percentile));
|
|
54
|
+
const index = Math.ceil(bounded / 100 * sorted.length) - 1;
|
|
55
|
+
return sorted[Math.min(sorted.length - 1, Math.max(0, index))] ?? null;
|
|
56
|
+
}
|
|
57
|
+
function estimateLearningVariance(values) {
|
|
58
|
+
const samples = numericActuals(values);
|
|
59
|
+
if (samples.length <= 1) return 0;
|
|
60
|
+
const mean = samples.reduce((total, value) => total + value, 0) / samples.length;
|
|
61
|
+
return samples.reduce((total, value) => total + (value - mean) ** 2, 0) / samples.length;
|
|
62
|
+
}
|
|
63
|
+
function isInterruptedUsageActual(actual) {
|
|
64
|
+
const metadata = actual.metadata ?? {};
|
|
65
|
+
return metadataFlag(metadata, "interrupted") || metadataFlag(metadata, "partial");
|
|
66
|
+
}
|
|
67
|
+
function estimateProfileConfidenceScore(input) {
|
|
68
|
+
const sampleCount = Math.max(0, Math.floor(finiteOrParsedNumber(input.sampleCount) ?? 0));
|
|
69
|
+
const sampleScore = Math.min(1, sampleCount / 20);
|
|
70
|
+
const p50 = Math.max(1, finiteOrParsedNumber(input.creditsP50) ?? 1);
|
|
71
|
+
const variance = Math.max(0, finiteOrParsedNumber(input.creditsVariance) ?? 0);
|
|
72
|
+
const varianceScore = 1 / (1 + Math.sqrt(variance) / p50);
|
|
73
|
+
let ageScore = 1;
|
|
74
|
+
if (input.lastSampleAt) {
|
|
75
|
+
const now = input.now instanceof Date ? input.now : new Date(input.now ?? Date.now());
|
|
76
|
+
const last = new Date(input.lastSampleAt);
|
|
77
|
+
if (Number.isFinite(last.valueOf()) && Number.isFinite(now.valueOf())) {
|
|
78
|
+
const days = Math.max(0, (now.valueOf() - last.valueOf()) / 864e5);
|
|
79
|
+
ageScore = days > 90 ? 0.35 : days > 30 ? 0.7 : 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return Math.max(0, Math.min(1, sampleScore * varianceScore * ageScore));
|
|
83
|
+
}
|
|
84
|
+
function estimateConfidenceFromProfile(profile, now) {
|
|
85
|
+
if (!profile) return "medium";
|
|
86
|
+
const confidenceScore = finiteOrParsedNumber(profile.confidenceScore) ?? estimateProfileConfidenceScore({
|
|
87
|
+
sampleCount: profile.completedSampleCount ?? profile.sampleCount,
|
|
88
|
+
creditsVariance: profile.creditsVariance,
|
|
89
|
+
creditsP50: profile.creditsP50,
|
|
90
|
+
lastSampleAt: profile.lastSampleAt ?? profile.updatedAt,
|
|
91
|
+
now
|
|
92
|
+
});
|
|
93
|
+
if (confidenceScore >= 0.75) return "high";
|
|
94
|
+
if (confidenceScore >= 0.35) return "medium";
|
|
95
|
+
return "low";
|
|
96
|
+
}
|
|
97
|
+
function selectTaskEstimateProfile(input) {
|
|
98
|
+
const taskSignature = input.taskSignature?.trim();
|
|
99
|
+
if (!taskSignature) return null;
|
|
100
|
+
const executionProfileId = input.executionProfileId?.trim() || profileIdFrom(input.executionProfile);
|
|
101
|
+
const profiles = input.profiles ?? [];
|
|
102
|
+
return profiles.find(
|
|
103
|
+
(profile) => profile.taskSignature === taskSignature && (profile.executionProfileId || DEFAULT_EXECUTION_PROFILE_ID) === executionProfileId
|
|
104
|
+
) ?? profiles.find(
|
|
105
|
+
(profile) => profile.taskSignature === taskSignature && (profile.executionProfileId || DEFAULT_EXECUTION_PROFILE_ID) === DEFAULT_EXECUTION_PROFILE_ID
|
|
106
|
+
) ?? null;
|
|
107
|
+
}
|
|
108
|
+
function buildTaskEstimateProfileFromActuals(input) {
|
|
109
|
+
const taskSignature = input.taskSignature;
|
|
110
|
+
const executionProfileId = input.executionProfileId?.trim() || DEFAULT_EXECUTION_PROFILE_ID;
|
|
111
|
+
const matching = input.actuals.filter(
|
|
112
|
+
(actual) => actual.taskSignature === taskSignature && (actual.executionProfileId || DEFAULT_EXECUTION_PROFILE_ID) === executionProfileId
|
|
113
|
+
);
|
|
114
|
+
const completed = matching.filter((actual) => !isInterruptedUsageActual(actual));
|
|
115
|
+
const interrupted = matching.filter((actual) => isInterruptedUsageActual(actual));
|
|
116
|
+
const credits = completed.map((actual) => actual.actualCredits);
|
|
117
|
+
const creditsP50 = estimateLearningPercentile(credits, 50);
|
|
118
|
+
const creditsP90 = estimateLearningPercentile(credits, 90);
|
|
119
|
+
const creditsVariance = estimateLearningVariance(credits);
|
|
120
|
+
const outlierLimit = creditsP90 === null ? null : Math.max(creditsP90 * 1.5, (creditsP50 ?? creditsP90) + Math.sqrt(creditsVariance));
|
|
121
|
+
const lastCreatedAt = matching.map((actual) => actual.createdAt).filter((value) => typeof value === "string" && value.length > 0).sort();
|
|
122
|
+
const partialCredits = interrupted.reduce((total, actual) => total + Math.max(0, finiteOrParsedNumber(actual.actualCredits) ?? 0), 0);
|
|
123
|
+
const updatedAt = input.now instanceof Date ? input.now.toISOString() : typeof input.now === "string" ? input.now : (/* @__PURE__ */ new Date()).toISOString();
|
|
124
|
+
return {
|
|
125
|
+
taskSignature,
|
|
126
|
+
executionProfileId,
|
|
127
|
+
sampleCount: matching.length,
|
|
128
|
+
completedSampleCount: completed.length,
|
|
129
|
+
interruptedSampleCount: interrupted.length,
|
|
130
|
+
inputTokensP50: estimateLearningPercentile(completed.map((actual) => actual.inputTokens), 50),
|
|
131
|
+
inputTokensP90: estimateLearningPercentile(completed.map((actual) => actual.inputTokens), 90),
|
|
132
|
+
outputTokensP50: estimateLearningPercentile(completed.map((actual) => actual.outputTokens), 50),
|
|
133
|
+
outputTokensP90: estimateLearningPercentile(completed.map((actual) => actual.outputTokens), 90),
|
|
134
|
+
quotaMinutesP50: estimateLearningPercentile(completed.map((actual) => actual.quotaMinutes), 50),
|
|
135
|
+
quotaMinutesP90: estimateLearningPercentile(completed.map((actual) => actual.quotaMinutes), 90),
|
|
136
|
+
filesChangedP50: estimateLearningPercentile(completed.map((actual) => actual.filesChanged), 50),
|
|
137
|
+
filesChangedP90: estimateLearningPercentile(completed.map((actual) => actual.filesChanged), 90),
|
|
138
|
+
creditsP50,
|
|
139
|
+
creditsP90,
|
|
140
|
+
creditsVariance,
|
|
141
|
+
confidenceScore: estimateProfileConfidenceScore({
|
|
142
|
+
sampleCount: completed.length,
|
|
143
|
+
creditsVariance,
|
|
144
|
+
creditsP50,
|
|
145
|
+
lastSampleAt: lastCreatedAt.at(-1) ?? null,
|
|
146
|
+
now: input.now
|
|
147
|
+
}),
|
|
148
|
+
outlierCount: outlierLimit === null ? 0 : credits.filter((value) => value > outlierLimit).length,
|
|
149
|
+
partialCredits,
|
|
150
|
+
firstSampleAt: lastCreatedAt[0] ?? null,
|
|
151
|
+
lastSampleAt: lastCreatedAt.at(-1) ?? null,
|
|
152
|
+
updatedAt
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const DEFAULT_EXECUTION_PROFILES = {
|
|
156
|
+
"local-runner": {
|
|
157
|
+
id: "local-runner",
|
|
158
|
+
modelClass: "local",
|
|
159
|
+
qualityWeight: 1,
|
|
160
|
+
costMultiplier: 1,
|
|
161
|
+
latencyClass: "low",
|
|
162
|
+
concurrencyClass: "read_only",
|
|
163
|
+
quotaBehavior: "compute_bound"
|
|
164
|
+
},
|
|
165
|
+
"local-fast-model": {
|
|
166
|
+
id: "local-fast-model",
|
|
167
|
+
modelClass: "local",
|
|
168
|
+
qualityWeight: 0.65,
|
|
169
|
+
costMultiplier: 0.35,
|
|
170
|
+
latencyClass: "low",
|
|
171
|
+
concurrencyClass: "read_only",
|
|
172
|
+
quotaBehavior: "compute_bound"
|
|
173
|
+
},
|
|
174
|
+
"small-code-model": {
|
|
175
|
+
id: "small-code-model",
|
|
176
|
+
modelClass: "coding",
|
|
177
|
+
qualityWeight: 0.75,
|
|
178
|
+
costMultiplier: 0.5,
|
|
179
|
+
latencyClass: "low",
|
|
180
|
+
concurrencyClass: "repository_claim",
|
|
181
|
+
quotaBehavior: "api_metered"
|
|
182
|
+
},
|
|
183
|
+
"standard-code-model": {
|
|
184
|
+
id: "standard-code-model",
|
|
185
|
+
modelClass: "coding",
|
|
186
|
+
qualityWeight: 1,
|
|
187
|
+
costMultiplier: 1,
|
|
188
|
+
latencyClass: "medium",
|
|
189
|
+
concurrencyClass: "repository_claim",
|
|
190
|
+
quotaBehavior: "api_metered"
|
|
191
|
+
},
|
|
192
|
+
"large-reasoning-model": {
|
|
193
|
+
id: "large-reasoning-model",
|
|
194
|
+
modelClass: "reasoning",
|
|
195
|
+
qualityWeight: 1.5,
|
|
196
|
+
costMultiplier: 3,
|
|
197
|
+
latencyClass: "high",
|
|
198
|
+
concurrencyClass: "exclusive_project",
|
|
199
|
+
quotaBehavior: "api_metered"
|
|
200
|
+
},
|
|
201
|
+
"long-context-architect": {
|
|
202
|
+
id: "long-context-architect",
|
|
203
|
+
modelClass: "reasoning",
|
|
204
|
+
contextWindowTokens: 2e5,
|
|
205
|
+
qualityWeight: 1.75,
|
|
206
|
+
costMultiplier: 4,
|
|
207
|
+
latencyClass: "high",
|
|
208
|
+
concurrencyClass: "exclusive_project",
|
|
209
|
+
quotaBehavior: "api_metered"
|
|
210
|
+
},
|
|
211
|
+
"cheap-review-model": {
|
|
212
|
+
id: "cheap-review-model",
|
|
213
|
+
modelClass: "review",
|
|
214
|
+
qualityWeight: 0.8,
|
|
215
|
+
costMultiplier: 0.6,
|
|
216
|
+
latencyClass: "low",
|
|
217
|
+
concurrencyClass: "read_only",
|
|
218
|
+
quotaBehavior: "api_metered"
|
|
219
|
+
},
|
|
220
|
+
"human-review": {
|
|
221
|
+
id: "human-review",
|
|
222
|
+
modelClass: "human",
|
|
223
|
+
qualityWeight: 2,
|
|
224
|
+
costMultiplier: 10,
|
|
225
|
+
latencyClass: "high",
|
|
226
|
+
concurrencyClass: "human_attention",
|
|
227
|
+
quotaBehavior: "attention_bound"
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
const DEFAULT_TASK_ADMISSION_POLICY = {
|
|
231
|
+
planningThresholdCredits: 20,
|
|
232
|
+
approvalThresholdCredits: 50,
|
|
233
|
+
reserveBufferPercent: 15,
|
|
234
|
+
recoveryBudgetCredits: 0,
|
|
235
|
+
maxDownstreamTasks: 4,
|
|
236
|
+
maxPlanningDepth: 2,
|
|
237
|
+
maxAdmittedPlanTasksPerCycle: 4,
|
|
238
|
+
planningTaskSignature: "planner.dag_proposal",
|
|
239
|
+
allowBackfill: true,
|
|
240
|
+
maxAttentionLoad: null,
|
|
241
|
+
reserveAttentionPercent: 0,
|
|
242
|
+
maxContextTokens: null,
|
|
243
|
+
maxContextSaturationPercent: 100,
|
|
244
|
+
coordinationOverheadFactor: 1,
|
|
245
|
+
predictiveReservePolicy: null,
|
|
246
|
+
utilityPolicy: null
|
|
247
|
+
};
|
|
248
|
+
function normalizeExecutionProfile(input) {
|
|
249
|
+
if (!input) return DEFAULT_EXECUTION_PROFILES["standard-code-model"];
|
|
250
|
+
if (typeof input === "string") {
|
|
251
|
+
return DEFAULT_EXECUTION_PROFILES[input] ?? {
|
|
252
|
+
id: input,
|
|
253
|
+
qualityWeight: 1,
|
|
254
|
+
costMultiplier: 1,
|
|
255
|
+
latencyClass: "medium",
|
|
256
|
+
metadata: { source: "ad_hoc" }
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
...input,
|
|
261
|
+
qualityWeight: positiveNumber(input.qualityWeight, 1),
|
|
262
|
+
costMultiplier: positiveNumber(input.costMultiplier, 1),
|
|
263
|
+
latencyClass: input.latencyClass || "medium"
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function normalizeTaskAdmissionPolicy(input = {}) {
|
|
267
|
+
const reserveBufferPercent = nonNegativeNumber(input?.reserveBufferPercent, DEFAULT_TASK_ADMISSION_POLICY.reserveBufferPercent);
|
|
268
|
+
return {
|
|
269
|
+
...DEFAULT_TASK_ADMISSION_POLICY,
|
|
270
|
+
...input ?? {},
|
|
271
|
+
planningThresholdCredits: positiveNumber(input?.planningThresholdCredits, DEFAULT_TASK_ADMISSION_POLICY.planningThresholdCredits),
|
|
272
|
+
approvalThresholdCredits: positiveNumber(input?.approvalThresholdCredits, DEFAULT_TASK_ADMISSION_POLICY.approvalThresholdCredits),
|
|
273
|
+
reserveBufferPercent: Math.min(100, reserveBufferPercent),
|
|
274
|
+
recoveryBudgetCredits: nonNegativeNumber(input?.recoveryBudgetCredits, DEFAULT_TASK_ADMISSION_POLICY.recoveryBudgetCredits),
|
|
275
|
+
maxDownstreamTasks: Math.max(0, Math.floor(nonNegativeNumber(input?.maxDownstreamTasks, DEFAULT_TASK_ADMISSION_POLICY.maxDownstreamTasks))),
|
|
276
|
+
maxPlanningDepth: Math.max(0, Math.floor(nonNegativeNumber(input?.maxPlanningDepth, DEFAULT_TASK_ADMISSION_POLICY.maxPlanningDepth))),
|
|
277
|
+
maxAdmittedPlanTasksPerCycle: Math.max(1, Math.floor(nonNegativeNumber(input?.maxAdmittedPlanTasksPerCycle, DEFAULT_TASK_ADMISSION_POLICY.maxAdmittedPlanTasksPerCycle))),
|
|
278
|
+
planningTaskSignature: typeof input?.planningTaskSignature === "string" && input.planningTaskSignature.trim() ? input.planningTaskSignature.trim() : DEFAULT_TASK_ADMISSION_POLICY.planningTaskSignature,
|
|
279
|
+
allowBackfill: input?.allowBackfill ?? DEFAULT_TASK_ADMISSION_POLICY.allowBackfill,
|
|
280
|
+
maxAttentionLoad: finiteOrParsedNumber(input?.maxAttentionLoad) ?? DEFAULT_TASK_ADMISSION_POLICY.maxAttentionLoad,
|
|
281
|
+
reserveAttentionPercent: Math.min(100, nonNegativeNumber(input?.reserveAttentionPercent, DEFAULT_TASK_ADMISSION_POLICY.reserveAttentionPercent ?? 0)),
|
|
282
|
+
maxContextTokens: finiteOrParsedNumber(input?.maxContextTokens) ?? DEFAULT_TASK_ADMISSION_POLICY.maxContextTokens,
|
|
283
|
+
maxContextSaturationPercent: Math.min(100, positiveNumber(input?.maxContextSaturationPercent, DEFAULT_TASK_ADMISSION_POLICY.maxContextSaturationPercent ?? 100)),
|
|
284
|
+
coordinationOverheadFactor: nonNegativeNumber(input?.coordinationOverheadFactor, DEFAULT_TASK_ADMISSION_POLICY.coordinationOverheadFactor ?? 1)
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function normalizeAttentionPolicy(input = {}) {
|
|
288
|
+
return {
|
|
289
|
+
maxAttentionLoad: finiteOrParsedNumber(input?.maxAttentionLoad) ?? null,
|
|
290
|
+
reserveAttentionPercent: Math.min(100, nonNegativeNumber(input?.reserveAttentionPercent, 0)),
|
|
291
|
+
maxContextTokens: finiteOrParsedNumber(input?.maxContextTokens) ?? null,
|
|
292
|
+
maxContextSaturationPercent: Math.min(100, positiveNumber(input?.maxContextSaturationPercent, 100)),
|
|
293
|
+
coordinationOverheadFactor: nonNegativeNumber(input?.coordinationOverheadFactor, 1)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function normalizeUtilityPolicy(input = {}) {
|
|
297
|
+
return {
|
|
298
|
+
minimumUtilityScore: finiteOrParsedNumber(input?.minimumUtilityScore) ?? null,
|
|
299
|
+
minimumUtilityPerCredit: finiteOrParsedNumber(input?.minimumUtilityPerCredit) ?? null,
|
|
300
|
+
riskPenaltyFactor: nonNegativeNumber(input?.riskPenaltyFactor, 1),
|
|
301
|
+
deadlineWindowHours: positiveNumber(input?.deadlineWindowHours, 72),
|
|
302
|
+
maintenanceWeight: nonNegativeNumber(input?.maintenanceWeight, 1),
|
|
303
|
+
priorityWeight: nonNegativeNumber(input?.priorityWeight, 1)
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function normalizePredictiveReservePolicy(input = {}) {
|
|
307
|
+
const raw = readRecord(input);
|
|
308
|
+
return {
|
|
309
|
+
enabled: raw.enabled === true || raw.enabled === "true",
|
|
310
|
+
baseReservePercent: Math.min(100, nonNegativeNumber(input?.baseReservePercent, 0)),
|
|
311
|
+
maxReservePercent: Math.min(100, positiveNumber(input?.maxReservePercent, 50)),
|
|
312
|
+
incidentReservePercent: Math.min(100, nonNegativeNumber(input?.incidentReservePercent, 15)),
|
|
313
|
+
triggerBurstReservePercent: Math.min(100, nonNegativeNumber(input?.triggerBurstReservePercent, 10)),
|
|
314
|
+
deploymentWindowReservePercent: Math.min(100, nonNegativeNumber(input?.deploymentWindowReservePercent, 10)),
|
|
315
|
+
providerDegradationReservePercent: Math.min(100, nonNegativeNumber(input?.providerDegradationReservePercent, 10)),
|
|
316
|
+
quotaPressureReservePercent: Math.min(100, nonNegativeNumber(input?.quotaPressureReservePercent, 10))
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function isoHoursUntil(value, now) {
|
|
320
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
321
|
+
const target = new Date(value);
|
|
322
|
+
if (!Number.isFinite(target.valueOf())) return null;
|
|
323
|
+
return (target.valueOf() - now.valueOf()) / 36e5;
|
|
324
|
+
}
|
|
325
|
+
function riskPenalty(classification, policy) {
|
|
326
|
+
if (classification?.risk === "high") return 20 * policy.riskPenaltyFactor;
|
|
327
|
+
if (classification?.risk === "medium") return 8 * policy.riskPenaltyFactor;
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
function qualityScoreFromProfile(profile, confidence) {
|
|
331
|
+
const confidenceWeight = confidence === "high" ? 1.1 : confidence === "low" ? 0.75 : 1;
|
|
332
|
+
return Math.max(0, profile.qualityWeight * confidenceWeight);
|
|
333
|
+
}
|
|
334
|
+
function estimateUtilityForTask(input) {
|
|
335
|
+
const policy = normalizeUtilityPolicy(input.utilityPolicy);
|
|
336
|
+
const profile = normalizeExecutionProfile(input.executionProfile);
|
|
337
|
+
const metadata = readRecord(input.metadata);
|
|
338
|
+
const priority = Math.max(0, finiteOrParsedNumber(input.priority) ?? finiteOrParsedNumber(metadata.priority) ?? 0);
|
|
339
|
+
const explicitUtility = finiteOrParsedNumber(input.utilityValue) ?? finiteOrParsedNumber(metadata.utilityValue);
|
|
340
|
+
const maintenanceValue = Math.max(0, finiteOrParsedNumber(input.maintenanceValue) ?? finiteOrParsedNumber(metadata.maintenanceValue) ?? 0);
|
|
341
|
+
const now = input.now instanceof Date ? input.now : new Date(input.now ?? Date.now());
|
|
342
|
+
const hoursUntilDeadline = isoHoursUntil(input.deadlineAt ?? metadata.deadlineAt, now);
|
|
343
|
+
const deadlinePressure = hoursUntilDeadline === null ? 0 : Math.max(0, Math.min(30, (policy.deadlineWindowHours - hoursUntilDeadline) / Math.max(1, policy.deadlineWindowHours) * 30));
|
|
344
|
+
const successProbability = Math.max(0, Math.min(1, finiteOrParsedNumber(input.successProbability) ?? finiteOrParsedNumber(metadata.successProbability) ?? 1));
|
|
345
|
+
const qualityScore = qualityScoreFromProfile(profile, input.classification?.confidence);
|
|
346
|
+
const baseUtility = explicitUtility ?? priority * policy.priorityWeight + maintenanceValue * policy.maintenanceWeight;
|
|
347
|
+
const risk = riskPenalty(input.classification, policy);
|
|
348
|
+
const utilityScore = Math.max(0, (baseUtility + deadlinePressure) * successProbability * Math.max(0.1, qualityScore) - risk);
|
|
349
|
+
const reservedCredits = Math.max(1, finiteOrParsedNumber(input.estimate?.reservedCredits) ?? 1);
|
|
350
|
+
return {
|
|
351
|
+
utilityValue: Math.max(0, baseUtility),
|
|
352
|
+
maintenanceValue,
|
|
353
|
+
deadlinePressure,
|
|
354
|
+
successProbability,
|
|
355
|
+
qualityScore,
|
|
356
|
+
riskPenalty: risk,
|
|
357
|
+
utilityScore,
|
|
358
|
+
utilityPerCredit: utilityScore / reservedCredits,
|
|
359
|
+
source: input.source ?? "capacity_utility_estimator",
|
|
360
|
+
metadata
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function reserveSignalFlag(metadata, ...keys) {
|
|
364
|
+
return keys.some((key) => metadata[key] === true || metadata[key] === "true");
|
|
365
|
+
}
|
|
366
|
+
function predictReserveForCapacityPlan(input) {
|
|
367
|
+
const policy = normalizePredictiveReservePolicy(input.policy);
|
|
368
|
+
const metadata = readRecord(input.metadata);
|
|
369
|
+
const reasons = [];
|
|
370
|
+
const signals = {};
|
|
371
|
+
let reservePercent = policy.enabled ? policy.baseReservePercent : 0;
|
|
372
|
+
const providerDegraded = input.plan?.providers.some((provider) => provider.status === "degraded" || metadataStatus(provider.metadata) === "degraded") ?? false;
|
|
373
|
+
const quotaPressure = input.plan?.providers.some((provider) => {
|
|
374
|
+
const pressure = readRecord(provider.metadata?.pressure);
|
|
375
|
+
const quota = finiteOrParsedNumber(pressure.quotaRemainingPercent) ?? finiteOrParsedNumber(provider.metadata?.quotaRemainingPercent);
|
|
376
|
+
return quota !== null && quota < 20;
|
|
377
|
+
}) ?? false;
|
|
378
|
+
if (policy.enabled && reserveSignalFlag(metadata, "incidentLikely", "likelyIncident")) {
|
|
379
|
+
reservePercent += policy.incidentReservePercent;
|
|
380
|
+
reasons.push("incident_reserve");
|
|
381
|
+
signals.incidentLikely = true;
|
|
382
|
+
}
|
|
383
|
+
if (policy.enabled && reserveSignalFlag(metadata, "triggerBurstLikely", "expectedTriggerBurst")) {
|
|
384
|
+
reservePercent += policy.triggerBurstReservePercent;
|
|
385
|
+
reasons.push("trigger_burst_reserve");
|
|
386
|
+
signals.triggerBurstLikely = true;
|
|
387
|
+
}
|
|
388
|
+
if (policy.enabled && reserveSignalFlag(metadata, "deploymentWindow", "deploymentWindowActive")) {
|
|
389
|
+
reservePercent += policy.deploymentWindowReservePercent;
|
|
390
|
+
reasons.push("deployment_window_reserve");
|
|
391
|
+
signals.deploymentWindow = true;
|
|
392
|
+
}
|
|
393
|
+
if (policy.enabled && providerDegraded) {
|
|
394
|
+
reservePercent += policy.providerDegradationReservePercent;
|
|
395
|
+
reasons.push("provider_degradation_reserve");
|
|
396
|
+
signals.providerDegraded = true;
|
|
397
|
+
}
|
|
398
|
+
if (policy.enabled && quotaPressure) {
|
|
399
|
+
reservePercent += policy.quotaPressureReservePercent;
|
|
400
|
+
reasons.push("quota_pressure_reserve");
|
|
401
|
+
signals.quotaPressure = true;
|
|
402
|
+
}
|
|
403
|
+
const boundedPercent = Math.min(policy.maxReservePercent, Math.max(0, reservePercent));
|
|
404
|
+
const budget = Math.max(0, finiteOrParsedNumber(input.dailyCreditBudget) ?? finiteOrParsedNumber(input.remainingCredits) ?? input.plan?.remaining.dailyCredits ?? 0);
|
|
405
|
+
const reserveCredits = Math.ceil(budget * boundedPercent / 100);
|
|
406
|
+
const remaining = Math.max(0, finiteOrParsedNumber(input.remainingCredits) ?? input.plan?.remaining.dailyCredits ?? budget);
|
|
407
|
+
return {
|
|
408
|
+
reservePercent: boundedPercent,
|
|
409
|
+
reserveCredits,
|
|
410
|
+
activelyAllocatableCredits: Math.max(0, remaining - reserveCredits),
|
|
411
|
+
reasons,
|
|
412
|
+
signals
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function normalizeHybridExecutionPlan(input) {
|
|
416
|
+
const record = readRecord(input);
|
|
417
|
+
const rawPhases = Array.isArray(record.phases) ? record.phases : [];
|
|
418
|
+
const phases = rawPhases.map((phase, index) => {
|
|
419
|
+
const entry = readRecord(phase);
|
|
420
|
+
const kind = typeof entry.kind === "string" && entry.kind.trim() ? entry.kind.trim() : `phase_${index + 1}`;
|
|
421
|
+
const executionProfileId = typeof entry.executionProfileId === "string" && entry.executionProfileId.trim() ? entry.executionProfileId.trim() : typeof entry.executionProfile === "string" && entry.executionProfile.trim() ? entry.executionProfile.trim() : DEFAULT_EXECUTION_PROFILE_ID;
|
|
422
|
+
const mutationAllowed = entry.mutationAllowed === true || kind === "implementation" && entry.mutationAllowed !== false;
|
|
423
|
+
return {
|
|
424
|
+
id: typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : kind,
|
|
425
|
+
kind,
|
|
426
|
+
executionProfileId,
|
|
427
|
+
taskSignature: typeof entry.taskSignature === "string" && entry.taskSignature.trim() ? entry.taskSignature.trim() : null,
|
|
428
|
+
required: entry.required !== false,
|
|
429
|
+
admissionRequired: entry.admissionRequired !== false,
|
|
430
|
+
mutationAllowed,
|
|
431
|
+
metadata: readRecord(entry.metadata)
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
if (phases.length === 0) return null;
|
|
435
|
+
return {
|
|
436
|
+
schemaVersion: 1,
|
|
437
|
+
planId: typeof record.planId === "string" && record.planId.trim() ? record.planId.trim() : "hybrid-execution-plan",
|
|
438
|
+
phases,
|
|
439
|
+
escalationPolicy: readRecord(record.escalationPolicy),
|
|
440
|
+
metadata: readRecord(record.metadata)
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function normalizePlanningPolicy(input = {}) {
|
|
444
|
+
const admissionPolicy = normalizeTaskAdmissionPolicy(input);
|
|
445
|
+
return {
|
|
446
|
+
maxDownstreamTasks: admissionPolicy.maxDownstreamTasks,
|
|
447
|
+
maxPlanningDepth: admissionPolicy.maxPlanningDepth,
|
|
448
|
+
maxAdmittedPlanTasksPerCycle: admissionPolicy.maxAdmittedPlanTasksPerCycle,
|
|
449
|
+
planningTaskSignature: admissionPolicy.planningTaskSignature
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function stablePlanId(input) {
|
|
453
|
+
const source = typeof input.sourceTaskId === "string" && input.sourceTaskId.trim() ? input.sourceTaskId.trim() : typeof input.parentTaskId === "string" && input.parentTaskId.trim() ? input.parentTaskId.trim() : "plan";
|
|
454
|
+
return `${source}:proposal`;
|
|
455
|
+
}
|
|
456
|
+
function readRecord(value) {
|
|
457
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
458
|
+
}
|
|
459
|
+
function readPlanString(value) {
|
|
460
|
+
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
461
|
+
}
|
|
462
|
+
function normalizePlannedTaskNode(value, index) {
|
|
463
|
+
const input = readRecord(value);
|
|
464
|
+
const payload = readRecord(input.payload);
|
|
465
|
+
const type = readPlanString(input.type) || readPlanString(payload.type);
|
|
466
|
+
if (!type) return null;
|
|
467
|
+
const id = readPlanString(input.id) || `node-${index + 1}`;
|
|
468
|
+
const p50 = finiteNumber(input.estimatedCreditsP50) ?? finiteNumber(payload.estimatedCreditsP50) ?? finiteNumber(input.estimatedCredits) ?? finiteNumber(payload.estimatedCredits) ?? null;
|
|
469
|
+
const p90 = finiteNumber(input.estimatedCreditsP90) ?? finiteNumber(payload.estimatedCreditsP90) ?? p50;
|
|
470
|
+
return {
|
|
471
|
+
id,
|
|
472
|
+
type,
|
|
473
|
+
agentId: readPlanString(input.agentId) || readPlanString(payload.agentId) || null,
|
|
474
|
+
title: readPlanString(input.title) || null,
|
|
475
|
+
priority: finiteNumber(input.priority) ?? finiteNumber(payload.priority),
|
|
476
|
+
taskSignature: readPlanString(input.taskSignature) || readPlanString(payload.taskSignature) || null,
|
|
477
|
+
payload,
|
|
478
|
+
estimatedCreditsP50: p50,
|
|
479
|
+
estimatedCreditsP90: p90,
|
|
480
|
+
risk: input.risk === "low" || input.risk === "medium" || input.risk === "high" ? input.risk : null,
|
|
481
|
+
mutationScope: input.mutationScope === "none" || input.mutationScope === "repository_read" || input.mutationScope === "repository_write" || input.mutationScope === "production" ? input.mutationScope : null,
|
|
482
|
+
confidence: input.confidence === "low" || input.confidence === "medium" || input.confidence === "high" ? input.confidence : null,
|
|
483
|
+
expectedFanout: finiteNumber(input.expectedFanout) ?? finiteNumber(payload.expectedFanout),
|
|
484
|
+
requiresApproval: typeof input.requiresApproval === "boolean" ? input.requiresApproval : null,
|
|
485
|
+
requiresPlanning: typeof input.requiresPlanning === "boolean" ? input.requiresPlanning : null,
|
|
486
|
+
dependsOn: Array.isArray(input.dependsOn) ? input.dependsOn.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [],
|
|
487
|
+
metadata: readRecord(input.metadata)
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function synthesizePlanEstimate(tasks) {
|
|
491
|
+
const totalEstimatedCreditsP50 = tasks.reduce((total, task) => total + Math.max(0, Math.ceil(finiteNumber(task.estimatedCreditsP50) ?? finiteNumber(task.estimatedCreditsP90) ?? 1)), 0);
|
|
492
|
+
const totalEstimatedCreditsP90 = tasks.reduce((total, task) => {
|
|
493
|
+
const p50 = Math.max(1, Math.ceil(finiteNumber(task.estimatedCreditsP50) ?? 1));
|
|
494
|
+
return total + Math.max(p50, Math.ceil(finiteNumber(task.estimatedCreditsP90) ?? p50));
|
|
495
|
+
}, 0);
|
|
496
|
+
return {
|
|
497
|
+
totalEstimatedCreditsP50,
|
|
498
|
+
totalEstimatedCreditsP90
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function rankPlannedTaskNodes(tasks) {
|
|
502
|
+
const boundedness = (task) => {
|
|
503
|
+
const fanout = Math.max(0, Math.floor(finiteNumber(task.expectedFanout) ?? 0));
|
|
504
|
+
const riskPenalty2 = task.risk === "high" ? 3 : task.risk === "medium" ? 1 : 0;
|
|
505
|
+
const mutationPenalty = task.mutationScope === "production" ? 4 : task.mutationScope === "repository_write" ? 2 : 0;
|
|
506
|
+
return fanout + riskPenalty2 + mutationPenalty;
|
|
507
|
+
};
|
|
508
|
+
return [...tasks].sort((left, right) => {
|
|
509
|
+
const priorityDelta = (finiteNumber(right.priority) ?? 0) - (finiteNumber(left.priority) ?? 0);
|
|
510
|
+
if (priorityDelta !== 0) return priorityDelta;
|
|
511
|
+
const boundedDelta = boundedness(left) - boundedness(right);
|
|
512
|
+
if (boundedDelta !== 0) return boundedDelta;
|
|
513
|
+
return String(left.id ?? left.type).localeCompare(String(right.id ?? right.type));
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
function normalizeTaskPlanProposal(input, policyInput) {
|
|
517
|
+
const policy = normalizePlanningPolicy(policyInput);
|
|
518
|
+
const record = readRecord(input);
|
|
519
|
+
const tasks = (Array.isArray(record.tasks) ? record.tasks : []).map((entry, index) => normalizePlannedTaskNode(entry, index)).filter((entry) => Boolean(entry));
|
|
520
|
+
const estimate = synthesizePlanEstimate(tasks);
|
|
521
|
+
const planningDepth = Math.max(0, Math.floor(finiteNumber(record.planningDepth) ?? 0));
|
|
522
|
+
return {
|
|
523
|
+
schemaVersion: 1,
|
|
524
|
+
planId: readPlanString(record.planId) || stablePlanId(record),
|
|
525
|
+
sourceTaskId: readPlanString(record.sourceTaskId) || null,
|
|
526
|
+
parentTaskId: readPlanString(record.parentTaskId) || null,
|
|
527
|
+
planningDepth,
|
|
528
|
+
tasks,
|
|
529
|
+
totalEstimatedCreditsP50: Math.max(0, Math.ceil(finiteNumber(record.totalEstimatedCreditsP50) ?? estimate.totalEstimatedCreditsP50)),
|
|
530
|
+
totalEstimatedCreditsP90: Math.max(0, Math.ceil(finiteNumber(record.totalEstimatedCreditsP90) ?? estimate.totalEstimatedCreditsP90)),
|
|
531
|
+
createdAt: readPlanString(record.createdAt) || null,
|
|
532
|
+
metadata: readRecord(record.metadata)
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function validateTaskPlanProposal(input, policyInput) {
|
|
536
|
+
const policy = normalizePlanningPolicy(policyInput);
|
|
537
|
+
const reasons = [];
|
|
538
|
+
if (input.planningDepth > policy.maxPlanningDepth) {
|
|
539
|
+
reasons.push("planning_depth_exceeded");
|
|
540
|
+
}
|
|
541
|
+
if (input.tasks.length > policy.maxDownstreamTasks) {
|
|
542
|
+
reasons.push("fanout_limit_exceeded");
|
|
543
|
+
}
|
|
544
|
+
const rejected = input.tasks.map((node) => {
|
|
545
|
+
const nodeReasons = [];
|
|
546
|
+
if (!node.type) nodeReasons.push("missing_type");
|
|
547
|
+
if (Math.max(0, Math.floor(finiteNumber(node.expectedFanout) ?? 0)) > policy.maxDownstreamTasks) {
|
|
548
|
+
nodeReasons.push("node_fanout_limit_exceeded");
|
|
549
|
+
}
|
|
550
|
+
return nodeReasons.length > 0 ? { node, reasons: nodeReasons } : null;
|
|
551
|
+
}).filter((entry) => Boolean(entry));
|
|
552
|
+
return {
|
|
553
|
+
ok: reasons.length === 0 && rejected.length === 0,
|
|
554
|
+
reasons,
|
|
555
|
+
rejected
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function progressivelyAdmitPlanProposal(input) {
|
|
559
|
+
const policy = normalizePlanningPolicy(input.policy);
|
|
560
|
+
const proposal = normalizeTaskPlanProposal(input.proposal, policy);
|
|
561
|
+
const validation = validateTaskPlanProposal(proposal, policy);
|
|
562
|
+
const reasons = [...validation.reasons];
|
|
563
|
+
const admitted = [];
|
|
564
|
+
const deferred = [];
|
|
565
|
+
const rejected = [...validation.rejected];
|
|
566
|
+
if (!validation.ok) {
|
|
567
|
+
return {
|
|
568
|
+
proposal,
|
|
569
|
+
admitted,
|
|
570
|
+
deferred: proposal.tasks.filter((node) => !rejected.some((entry) => entry.node.id === node.id)),
|
|
571
|
+
rejected,
|
|
572
|
+
totalEstimatedCreditsP50: proposal.totalEstimatedCreditsP50,
|
|
573
|
+
totalEstimatedCreditsP90: proposal.totalEstimatedCreditsP90,
|
|
574
|
+
admittedCreditsP90: 0,
|
|
575
|
+
reasons
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
let availableCredits = Math.max(0, Math.floor(nonNegativeNumber(input.availableCredits, Number.POSITIVE_INFINITY)));
|
|
579
|
+
let remainingQueuedCredits = Math.max(0, Math.floor(nonNegativeNumber(input.remainingQueuedCredits, Number.POSITIVE_INFINITY)));
|
|
580
|
+
let remainingQueuedSlots = Math.max(0, Math.floor(nonNegativeNumber(input.remainingQueuedSlots, policy.maxAdmittedPlanTasksPerCycle)));
|
|
581
|
+
let admittedCreditsP90 = 0;
|
|
582
|
+
for (const task of rankPlannedTaskNodes(proposal.tasks)) {
|
|
583
|
+
if (admitted.length >= policy.maxAdmittedPlanTasksPerCycle || remainingQueuedSlots <= 0) {
|
|
584
|
+
deferred.push(task);
|
|
585
|
+
reasons.push("plan_cycle_limit_reached");
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
const p50 = Math.max(1, Math.ceil(finiteNumber(task.estimatedCreditsP50) ?? 1));
|
|
589
|
+
const p90 = Math.max(p50, Math.ceil(finiteNumber(task.estimatedCreditsP90) ?? p50));
|
|
590
|
+
if (p90 > availableCredits || p90 > remainingQueuedCredits) {
|
|
591
|
+
deferred.push(task);
|
|
592
|
+
reasons.push("insufficient_plan_budget");
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
admitted.push(task);
|
|
596
|
+
admittedCreditsP90 += p90;
|
|
597
|
+
availableCredits -= p90;
|
|
598
|
+
remainingQueuedCredits -= p90;
|
|
599
|
+
remainingQueuedSlots -= 1;
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
proposal,
|
|
603
|
+
admitted,
|
|
604
|
+
deferred,
|
|
605
|
+
rejected,
|
|
606
|
+
totalEstimatedCreditsP50: proposal.totalEstimatedCreditsP50,
|
|
607
|
+
totalEstimatedCreditsP90: proposal.totalEstimatedCreditsP90,
|
|
608
|
+
admittedCreditsP90,
|
|
609
|
+
reasons: [...new Set(reasons)]
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function computeWorkdayBudgetEnvelope(input) {
|
|
613
|
+
const dailyCreditBudget = Math.max(0, Math.floor(nonNegativeNumber(input.dailyCreditBudget, 0)));
|
|
614
|
+
const usedCredits = Math.max(0, Math.ceil(nonNegativeNumber(input.usedCredits, 0)));
|
|
615
|
+
const queuedCredits = Math.max(0, Math.ceil(nonNegativeNumber(input.queuedCredits, 0)));
|
|
616
|
+
const reservePercent = Math.min(100, Math.max(0, nonNegativeNumber(input.reserveBufferPercent, DEFAULT_TASK_ADMISSION_POLICY.reserveBufferPercent)));
|
|
617
|
+
const reserveBufferCredits = Math.ceil(dailyCreditBudget * reservePercent / 100);
|
|
618
|
+
const recoveryBudgetCredits = Math.ceil(nonNegativeNumber(input.recoveryBudgetCredits, DEFAULT_TASK_ADMISSION_POLICY.recoveryBudgetCredits));
|
|
619
|
+
const remainingCredits = Math.max(0, dailyCreditBudget - usedCredits - queuedCredits);
|
|
620
|
+
const activelyAllocatableCredits = Math.max(0, dailyCreditBudget - usedCredits - queuedCredits - reserveBufferCredits - recoveryBudgetCredits);
|
|
621
|
+
return {
|
|
622
|
+
dailyCreditBudget,
|
|
623
|
+
usedCredits,
|
|
624
|
+
queuedCredits,
|
|
625
|
+
reserveBufferCredits,
|
|
626
|
+
recoveryBudgetCredits,
|
|
627
|
+
activelyAllocatableCredits,
|
|
628
|
+
remainingCredits
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function mutationRequiresRepositoryClaim(scope) {
|
|
632
|
+
return scope === "repository_write" || scope === "production";
|
|
633
|
+
}
|
|
22
634
|
function reservationDebit(reservation) {
|
|
23
635
|
if (reservation.state === "released" || reservation.state === "expired" || reservation.state === "cancelled") {
|
|
24
636
|
return 0;
|
|
@@ -98,22 +710,125 @@ function lanePolicyReasons(lane, input) {
|
|
|
98
710
|
return reasons;
|
|
99
711
|
}
|
|
100
712
|
function reserveCreditsForEstimate(input) {
|
|
101
|
-
const
|
|
102
|
-
const
|
|
713
|
+
const executionProfile = input.executionProfile ? normalizeExecutionProfile(input.executionProfile) : input.executionProfileId ? normalizeExecutionProfile(input.executionProfileId) : null;
|
|
714
|
+
const selectedProfile = input.profile ?? selectTaskEstimateProfile({
|
|
715
|
+
profiles: input.profiles,
|
|
716
|
+
taskSignature: input.taskSignature ?? input.taskKind,
|
|
717
|
+
executionProfile,
|
|
718
|
+
executionProfileId: input.executionProfileId
|
|
719
|
+
});
|
|
720
|
+
const profileP50 = finiteNumber(selectedProfile?.creditsP50);
|
|
721
|
+
const profileP90 = finiteNumber(selectedProfile?.creditsP90);
|
|
722
|
+
const costMultiplier = positiveNumber(input.costMultiplier ?? executionProfile?.costMultiplier ?? 1, 1);
|
|
103
723
|
const p50 = Math.max(1, Math.ceil(
|
|
104
724
|
finiteNumber(input.estimatedCreditsP50) ?? profileP50 ?? finiteNumber(input.defaultCredits) ?? 1
|
|
105
725
|
));
|
|
106
726
|
const p90 = Math.max(p50, Math.ceil(
|
|
107
727
|
finiteNumber(input.estimatedCreditsP90) ?? profileP90 ?? p50 * 2
|
|
108
728
|
));
|
|
109
|
-
const
|
|
110
|
-
const
|
|
729
|
+
const profileConfidence = selectedProfile ? estimateConfidenceFromProfile(selectedProfile) : null;
|
|
730
|
+
const confidence = input.confidence ?? profileConfidence ?? "medium";
|
|
731
|
+
const baseReserved = confidence === "high" ? Math.max(p50, Math.ceil((p50 + p90) * 0.75)) : p90;
|
|
732
|
+
const reserved = Math.max(1, Math.ceil(baseReserved * costMultiplier));
|
|
111
733
|
return {
|
|
112
734
|
taskSignature: input.taskSignature ?? input.taskKind ?? "unknown",
|
|
113
735
|
confidence,
|
|
114
736
|
estimatedCreditsP50: p50,
|
|
115
737
|
estimatedCreditsP90: p90,
|
|
116
|
-
reservedCredits: reserved
|
|
738
|
+
reservedCredits: reserved,
|
|
739
|
+
baseReservedCredits: baseReserved,
|
|
740
|
+
executionProfileId: executionProfile?.id ?? input.executionProfileId ?? selectedProfile?.executionProfileId ?? null,
|
|
741
|
+
costMultiplier
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function estimateForClassification(input) {
|
|
745
|
+
const classification = input.classification ?? null;
|
|
746
|
+
return reserveCreditsForEstimate({
|
|
747
|
+
...input,
|
|
748
|
+
taskSignature: input.taskSignature ?? classification?.taskSignature,
|
|
749
|
+
confidence: input.confidence ?? classification?.confidence
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
function decideTaskAdmission(input) {
|
|
753
|
+
const policy = normalizeTaskAdmissionPolicy(input.policy);
|
|
754
|
+
const budget = computeWorkdayBudgetEnvelope({
|
|
755
|
+
...input.budget,
|
|
756
|
+
reserveBufferPercent: policy.reserveBufferPercent,
|
|
757
|
+
recoveryBudgetCredits: policy.recoveryBudgetCredits
|
|
758
|
+
});
|
|
759
|
+
const reasons = [];
|
|
760
|
+
const reservedCredits = Math.max(1, Math.ceil(input.estimate.reservedCredits));
|
|
761
|
+
const fanout = Math.max(0, Math.floor(input.classification.expectedFanout ?? 0));
|
|
762
|
+
const highRisk = input.classification.risk === "high" || input.classification.mutationScope === "production";
|
|
763
|
+
const requiresPlanning = input.classification.requiresPlanning || fanout > policy.maxDownstreamTasks || reservedCredits >= policy.planningThresholdCredits && (input.classification.confidence === "low" || fanout > 1 || highRisk);
|
|
764
|
+
const requiresApproval = input.classification.requiresApproval || highRisk || reservedCredits >= policy.approvalThresholdCredits;
|
|
765
|
+
let outcome = "admitted";
|
|
766
|
+
if (!input.classification.taskSignature || input.classification.taskSignature === "unknown") {
|
|
767
|
+
outcome = "rejected";
|
|
768
|
+
reasons.push("unknown_task_signature");
|
|
769
|
+
}
|
|
770
|
+
if (fanout > policy.maxDownstreamTasks) {
|
|
771
|
+
reasons.push("fanout_limit_exceeded");
|
|
772
|
+
}
|
|
773
|
+
if (requiresPlanning) {
|
|
774
|
+
outcome = "planning_required";
|
|
775
|
+
reasons.push("planning_required");
|
|
776
|
+
}
|
|
777
|
+
if (requiresApproval) {
|
|
778
|
+
outcome = "approval_required";
|
|
779
|
+
reasons.push("approval_required");
|
|
780
|
+
}
|
|
781
|
+
if (outcome === "admitted" && reservedCredits > budget.activelyAllocatableCredits) {
|
|
782
|
+
outcome = "budget_blocked";
|
|
783
|
+
reasons.push("insufficient_allocatable_budget");
|
|
784
|
+
}
|
|
785
|
+
return {
|
|
786
|
+
outcome,
|
|
787
|
+
taskSignature: input.classification.taskSignature,
|
|
788
|
+
estimatedCreditsP50: input.estimate.estimatedCreditsP50,
|
|
789
|
+
estimatedCreditsP90: input.estimate.estimatedCreditsP90,
|
|
790
|
+
reservedCredits,
|
|
791
|
+
baseReservedCredits: input.estimate.baseReservedCredits,
|
|
792
|
+
executionProfileId: input.estimate.executionProfileId ?? null,
|
|
793
|
+
costMultiplier: input.estimate.costMultiplier ?? null,
|
|
794
|
+
reasons: [...new Set(reasons)],
|
|
795
|
+
requiresApproval,
|
|
796
|
+
requiresPlanning,
|
|
797
|
+
budget,
|
|
798
|
+
policySnapshot: policy,
|
|
799
|
+
metadata: {
|
|
800
|
+
...input.metadata ?? {},
|
|
801
|
+
source: input.source ?? null,
|
|
802
|
+
classification: input.classification
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
function shouldInterruptForCapacity(input) {
|
|
807
|
+
const reservedCredits = nonNegativeNumber(input.reservedCredits, 0);
|
|
808
|
+
const consumedCredits = nonNegativeNumber(input.consumedCredits, 0);
|
|
809
|
+
const threshold = Math.min(100, Math.max(1, nonNegativeNumber(input.reservationUsedPercentThreshold, 80)));
|
|
810
|
+
const usedPercent = reservedCredits > 0 ? consumedCredits / reservedCredits * 100 : 0;
|
|
811
|
+
const estimatedRemainingP50 = nonNegativeNumber(input.estimatedRemainingCreditsP50, 0);
|
|
812
|
+
const estimatedRemainingP90 = nonNegativeNumber(input.estimatedRemainingCreditsP90, estimatedRemainingP50);
|
|
813
|
+
const recoveryBudgetRemaining = input.recoveryBudgetRemainingCredits === void 0 || input.recoveryBudgetRemainingCredits === null ? null : nonNegativeNumber(input.recoveryBudgetRemainingCredits, 0);
|
|
814
|
+
const recoveryMinimum = nonNegativeNumber(input.recoveryBudgetMinimumCredits, 3);
|
|
815
|
+
const reasons = [];
|
|
816
|
+
if (input.providerAvailable === false) {
|
|
817
|
+
reasons.push("provider_unavailable");
|
|
818
|
+
}
|
|
819
|
+
if (reservedCredits > 0 && usedPercent >= threshold && estimatedRemainingP50 > Math.max(0, reservedCredits - consumedCredits)) {
|
|
820
|
+
reasons.push("reservation_exhaustion_risk");
|
|
821
|
+
}
|
|
822
|
+
if (recoveryBudgetRemaining !== null && recoveryBudgetRemaining < recoveryMinimum) {
|
|
823
|
+
reasons.push("recovery_budget_low");
|
|
824
|
+
}
|
|
825
|
+
return {
|
|
826
|
+
interrupt: reasons.length > 0,
|
|
827
|
+
reasons,
|
|
828
|
+
usedPercent,
|
|
829
|
+
estimatedRemainingP50,
|
|
830
|
+
estimatedRemainingP90,
|
|
831
|
+
remainingReservationCredits: Math.max(0, reservedCredits - consumedCredits)
|
|
117
832
|
};
|
|
118
833
|
}
|
|
119
834
|
function summarizeCapacityPlan(plan) {
|
|
@@ -290,10 +1005,304 @@ function settleCapacityActuals(input) {
|
|
|
290
1005
|
overrunCredits
|
|
291
1006
|
};
|
|
292
1007
|
}
|
|
1008
|
+
function distinctProfiles(profiles) {
|
|
1009
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1010
|
+
return profiles.filter((profile) => {
|
|
1011
|
+
if (seen.has(profile.id)) return false;
|
|
1012
|
+
seen.add(profile.id);
|
|
1013
|
+
return true;
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
function executionProfilesForRoute(input) {
|
|
1017
|
+
const explicitProfiles = Array.isArray(input.executionProfiles) && input.executionProfiles.length > 0 ? input.executionProfiles : null;
|
|
1018
|
+
const preferred = stringArray(input.preferredExecutionProfiles);
|
|
1019
|
+
const disallowed = new Set(stringArray(input.disallowedExecutionProfiles));
|
|
1020
|
+
const rawProfiles = explicitProfiles ?? (preferred.length > 0 ? preferred : input.executionProfile ? [input.executionProfile] : input.estimate.executionProfileId ? [input.estimate.executionProfileId] : [DEFAULT_EXECUTION_PROFILE_ID]);
|
|
1021
|
+
const profiles = distinctProfiles(rawProfiles.map((profile) => normalizeExecutionProfile(profile))).filter((profile) => !disallowed.has(profile.id));
|
|
1022
|
+
return profiles.length > 0 ? profiles : [normalizeExecutionProfile(DEFAULT_EXECUTION_PROFILE_ID)];
|
|
1023
|
+
}
|
|
1024
|
+
function estimateForRouteProfile(input, profile) {
|
|
1025
|
+
const selectedProfile = selectTaskEstimateProfile({
|
|
1026
|
+
profiles: input.estimateProfiles ?? input.plan.estimateProfiles ?? null,
|
|
1027
|
+
taskSignature: input.estimate.taskSignature,
|
|
1028
|
+
executionProfile: profile,
|
|
1029
|
+
executionProfileId: profile.id
|
|
1030
|
+
});
|
|
1031
|
+
return reserveCreditsForEstimate({
|
|
1032
|
+
taskSignature: input.estimate.taskSignature,
|
|
1033
|
+
taskKind: input.taskKind ?? input.estimate.taskSignature,
|
|
1034
|
+
confidence: input.estimate.confidence ?? input.classification?.confidence ?? "medium",
|
|
1035
|
+
estimatedCreditsP50: selectedProfile ? void 0 : input.estimate.estimatedCreditsP50,
|
|
1036
|
+
estimatedCreditsP90: selectedProfile ? void 0 : input.estimate.estimatedCreditsP90,
|
|
1037
|
+
defaultCredits: input.estimate.estimatedCreditsP50,
|
|
1038
|
+
profiles: input.estimateProfiles ?? input.plan.estimateProfiles ?? null,
|
|
1039
|
+
profile: selectedProfile,
|
|
1040
|
+
executionProfile: profile,
|
|
1041
|
+
executionProfileId: profile.id
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
function routeMinimumQuality(input) {
|
|
1045
|
+
const explicit = finiteOrParsedNumber(input.minimumQualityWeight);
|
|
1046
|
+
if (explicit !== null) return Math.max(0, explicit);
|
|
1047
|
+
if (input.production || input.classification?.mutationScope === "production") return 1.5;
|
|
1048
|
+
if (input.classification?.risk === "high" || input.classification?.requiresApproval) return 1.25;
|
|
1049
|
+
if (input.classification?.confidence === "low") return 1.1;
|
|
1050
|
+
return 0;
|
|
1051
|
+
}
|
|
1052
|
+
function routeRequiredContext(input) {
|
|
1053
|
+
return Math.max(0, finiteOrParsedNumber(input.requiredContextTokens) ?? 0);
|
|
1054
|
+
}
|
|
1055
|
+
function reservationMetadata(reservation) {
|
|
1056
|
+
return readRecord(reservation.metadata);
|
|
1057
|
+
}
|
|
1058
|
+
function attentionValueFromMetadata(metadata, ...keys) {
|
|
1059
|
+
const estimate = readRecord(metadata.attentionEstimate);
|
|
1060
|
+
for (const key of keys) {
|
|
1061
|
+
const value = finiteOrParsedNumber(metadata[key]) ?? finiteOrParsedNumber(estimate[key]);
|
|
1062
|
+
if (value !== null) return value;
|
|
1063
|
+
}
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
function activeLaneReservations(plan, provider, lane) {
|
|
1067
|
+
return plan.activeReservations.filter(
|
|
1068
|
+
(reservation) => reservation.capacityProviderId === provider.id && (!lane || reservation.laneId === lane.id) && (reservation.state === "reserved" || reservation.state === "consuming")
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
function attentionLimitNumber(lane, provider, key) {
|
|
1072
|
+
return finiteOrParsedNumber(lane.hardLimits?.[key]) ?? finiteOrParsedNumber(lane.routingPolicy?.[key]) ?? finiteOrParsedNumber(lane.metadata?.[key]) ?? finiteOrParsedNumber(readRecord(lane.metadata?.pressure)[key]) ?? finiteOrParsedNumber(provider.capacityModel?.[key]) ?? finiteOrParsedNumber(provider.metadata?.[key]) ?? finiteOrParsedNumber(readRecord(provider.metadata?.pressure)[key]);
|
|
1073
|
+
}
|
|
1074
|
+
function deriveAttentionWeight(classification, profile, contextTokens) {
|
|
1075
|
+
let weight = 1;
|
|
1076
|
+
const mutationScope = classification?.mutationScope ?? "repository_read";
|
|
1077
|
+
if (mutationScope === "repository_read") weight = 1.5;
|
|
1078
|
+
if (mutationScope === "repository_write") weight = 3;
|
|
1079
|
+
if (mutationScope === "production") weight = 6;
|
|
1080
|
+
if (classification?.risk === "medium") weight += 1;
|
|
1081
|
+
if (classification?.risk === "high") weight += 3;
|
|
1082
|
+
if (classification?.confidence === "low") weight += 1;
|
|
1083
|
+
weight += Math.max(0, classification?.expectedFanout ?? 0) * 0.5;
|
|
1084
|
+
if (profile.concurrencyClass === "human_attention" || profile.quotaBehavior === "attention_bound") weight += 5;
|
|
1085
|
+
if (profile.concurrencyClass === "exclusive_project") weight += 3;
|
|
1086
|
+
if (contextTokens >= 1e5) weight += 4;
|
|
1087
|
+
else if (contextTokens >= 32e3) weight += 2;
|
|
1088
|
+
return Math.max(0, weight);
|
|
1089
|
+
}
|
|
1090
|
+
function estimateAttentionForTask(input) {
|
|
1091
|
+
const profile = normalizeExecutionProfile(input.executionProfile);
|
|
1092
|
+
const policy = normalizeAttentionPolicy(input.attentionPolicy);
|
|
1093
|
+
const requiredContextTokens = Math.max(0, finiteOrParsedNumber(input.requiredContextTokens) ?? 0);
|
|
1094
|
+
const estimatedContextTokens = Math.max(requiredContextTokens, finiteOrParsedNumber(input.estimatedContextTokens) ?? requiredContextTokens);
|
|
1095
|
+
const baseAttention = finiteOrParsedNumber(input.attentionWeight) ?? deriveAttentionWeight(input.classification, profile, estimatedContextTokens);
|
|
1096
|
+
const coordinationWeight = finiteOrParsedNumber(input.coordinationWeight) ?? Math.max(0, input.classification?.expectedFanout ?? 0) * policy.coordinationOverheadFactor;
|
|
1097
|
+
return {
|
|
1098
|
+
attentionWeight: Math.max(0, Math.ceil(baseAttention * 100) / 100),
|
|
1099
|
+
coordinationWeight: Math.max(0, Math.ceil(coordinationWeight * 100) / 100),
|
|
1100
|
+
totalAttentionWeight: Math.max(0, Math.ceil((baseAttention + coordinationWeight) * 100) / 100),
|
|
1101
|
+
estimatedContextTokens,
|
|
1102
|
+
requiredContextTokens,
|
|
1103
|
+
source: input.source ?? "capacity_attention_estimator",
|
|
1104
|
+
metadata: input.metadata ?? {}
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
function attentionEstimateForRoute(input, profile) {
|
|
1108
|
+
if (input.attentionEstimate) return input.attentionEstimate;
|
|
1109
|
+
return estimateAttentionForTask({
|
|
1110
|
+
classification: input.classification,
|
|
1111
|
+
executionProfile: profile,
|
|
1112
|
+
attentionPolicy: input.attentionPolicy,
|
|
1113
|
+
attentionWeight: input.attentionWeight,
|
|
1114
|
+
coordinationWeight: input.coordinationWeight,
|
|
1115
|
+
estimatedContextTokens: input.estimatedContextTokens,
|
|
1116
|
+
requiredContextTokens: input.requiredContextTokens,
|
|
1117
|
+
source: input.source ?? "capacity_router"
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
function readPressureNumber(provider, lane, key) {
|
|
1121
|
+
const laneValue = finiteOrParsedNumber(lane.metadata?.[key]);
|
|
1122
|
+
if (laneValue !== null) return laneValue;
|
|
1123
|
+
const providerValue = finiteOrParsedNumber(provider.metadata?.[key]);
|
|
1124
|
+
if (providerValue !== null) return providerValue;
|
|
1125
|
+
const lanePressure = readRecord(lane.metadata?.pressure);
|
|
1126
|
+
const providerPressure = readRecord(provider.metadata?.pressure);
|
|
1127
|
+
return finiteOrParsedNumber(lanePressure[key]) ?? finiteOrParsedNumber(providerPressure[key]);
|
|
1128
|
+
}
|
|
1129
|
+
function readPressureBoolean(provider, lane, key) {
|
|
1130
|
+
const values = [
|
|
1131
|
+
lane.metadata?.[key],
|
|
1132
|
+
provider.metadata?.[key],
|
|
1133
|
+
readRecord(lane.metadata?.pressure)[key],
|
|
1134
|
+
readRecord(provider.metadata?.pressure)[key]
|
|
1135
|
+
];
|
|
1136
|
+
return values.some((value) => value === true || value === "true");
|
|
1137
|
+
}
|
|
1138
|
+
function hardLimitNumber(lane, provider, ...keys) {
|
|
1139
|
+
for (const key of keys) {
|
|
1140
|
+
const value = finiteOrParsedNumber(lane.hardLimits?.[key]) ?? finiteOrParsedNumber(lane.routingPolicy?.[key]) ?? finiteOrParsedNumber(provider.capacityModel?.[key]) ?? finiteOrParsedNumber(provider.metadata?.[key]);
|
|
1141
|
+
if (value !== null && value >= 0) return value;
|
|
1142
|
+
}
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
function capacityRoutePressure(plan, provider, lane) {
|
|
1146
|
+
const reservations = activeLaneReservations(plan, provider, lane);
|
|
1147
|
+
const activeReservations = reservations.length;
|
|
1148
|
+
const maxActiveReservations = hardLimitNumber(
|
|
1149
|
+
lane,
|
|
1150
|
+
provider,
|
|
1151
|
+
"maxActiveReservations",
|
|
1152
|
+
"maxConcurrentTasks",
|
|
1153
|
+
"maxConcurrentWorkers"
|
|
1154
|
+
) ?? (provider.maxConcurrentWorkers > 0 ? provider.maxConcurrentWorkers : null);
|
|
1155
|
+
const congestionRatio = maxActiveReservations && maxActiveReservations > 0 ? activeReservations / maxActiveReservations : 0;
|
|
1156
|
+
const activeAttentionLoad = reservations.reduce((total, reservation) => {
|
|
1157
|
+
const metadata = reservationMetadata(reservation);
|
|
1158
|
+
return total + Math.max(0, attentionValueFromMetadata(metadata, "totalAttentionWeight", "attentionWeight") ?? 0);
|
|
1159
|
+
}, 0);
|
|
1160
|
+
const maxAttentionLoad = attentionLimitNumber(lane, provider, "maxAttentionLoad");
|
|
1161
|
+
const activeContextTokens = reservations.reduce((total, reservation) => {
|
|
1162
|
+
const metadata = reservationMetadata(reservation);
|
|
1163
|
+
return total + Math.max(0, attentionValueFromMetadata(metadata, "estimatedContextTokens", "contextTokens", "requiredContextTokens") ?? 0);
|
|
1164
|
+
}, 0);
|
|
1165
|
+
const maxContextTokens = attentionLimitNumber(lane, provider, "maxContextTokens");
|
|
1166
|
+
return {
|
|
1167
|
+
activeReservations,
|
|
1168
|
+
maxActiveReservations,
|
|
1169
|
+
congestionRatio,
|
|
1170
|
+
quotaRemainingPercent: readPressureNumber(provider, lane, "quotaRemainingPercent"),
|
|
1171
|
+
sessionRemainingMinutes: readPressureNumber(provider, lane, "sessionRemainingMinutes"),
|
|
1172
|
+
subscriptionSaturationPercent: readPressureNumber(provider, lane, "subscriptionSaturationPercent"),
|
|
1173
|
+
providerUnavailable: readPressureBoolean(provider, lane, "providerUnavailable"),
|
|
1174
|
+
activeAttentionLoad,
|
|
1175
|
+
maxAttentionLoad,
|
|
1176
|
+
attentionSaturationPercent: maxAttentionLoad && maxAttentionLoad > 0 ? activeAttentionLoad / maxAttentionLoad * 100 : null,
|
|
1177
|
+
activeContextTokens,
|
|
1178
|
+
maxContextTokens,
|
|
1179
|
+
contextSaturationPercent: maxContextTokens && maxContextTokens > 0 ? activeContextTokens / maxContextTokens * 100 : null
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
function latencyPenalty(profile) {
|
|
1183
|
+
if (profile.latencyClass === "high") return 12;
|
|
1184
|
+
if (profile.latencyClass === "medium") return 5;
|
|
1185
|
+
return 0;
|
|
1186
|
+
}
|
|
1187
|
+
function quotaPressurePenalty(pressure) {
|
|
1188
|
+
let penalty = 0;
|
|
1189
|
+
if (pressure.quotaRemainingPercent !== null) {
|
|
1190
|
+
penalty += Math.max(0, 100 - pressure.quotaRemainingPercent) * 0.25;
|
|
1191
|
+
}
|
|
1192
|
+
if (pressure.subscriptionSaturationPercent !== null) {
|
|
1193
|
+
penalty += Math.max(0, pressure.subscriptionSaturationPercent) * 0.35;
|
|
1194
|
+
}
|
|
1195
|
+
if (pressure.sessionRemainingMinutes !== null && pressure.sessionRemainingMinutes < 20) {
|
|
1196
|
+
penalty += (20 - pressure.sessionRemainingMinutes) * 1.5;
|
|
1197
|
+
}
|
|
1198
|
+
return penalty;
|
|
1199
|
+
}
|
|
1200
|
+
function attentionPressurePenalty(pressure, estimate, policy) {
|
|
1201
|
+
if (!pressure.maxAttentionLoad || pressure.maxAttentionLoad <= 0) return 0;
|
|
1202
|
+
const allocatableLoad = Math.max(0, pressure.maxAttentionLoad * (1 - policy.reserveAttentionPercent / 100));
|
|
1203
|
+
const projected = pressure.activeAttentionLoad + estimate.totalAttentionWeight;
|
|
1204
|
+
const saturation = allocatableLoad > 0 ? projected / allocatableLoad : 1;
|
|
1205
|
+
return Math.max(0, saturation) * 35;
|
|
1206
|
+
}
|
|
1207
|
+
function contextPressurePenalty(pressure, estimate, policy) {
|
|
1208
|
+
const maxContextTokens = pressure.maxContextTokens ?? policy.maxContextTokens;
|
|
1209
|
+
if (!maxContextTokens || maxContextTokens <= 0) return 0;
|
|
1210
|
+
const projected = pressure.activeContextTokens + estimate.estimatedContextTokens;
|
|
1211
|
+
const saturationPercent = projected / maxContextTokens * 100;
|
|
1212
|
+
return Math.max(0, saturationPercent - 50) * 0.6;
|
|
1213
|
+
}
|
|
1214
|
+
function laneSupportsExecutionProfile(lane, profile) {
|
|
1215
|
+
const allowedProfiles = stringArray(lane.routingPolicy?.executionProfiles);
|
|
1216
|
+
if (allowedProfiles.length > 0 && !allowedProfiles.includes(profile.id)) return false;
|
|
1217
|
+
const allowedModelClasses = stringArray(lane.routingPolicy?.modelClasses);
|
|
1218
|
+
if (allowedModelClasses.length > 0 && profile.modelClass && !allowedModelClasses.includes(profile.modelClass)) return false;
|
|
1219
|
+
return true;
|
|
1220
|
+
}
|
|
1221
|
+
function routeTrustScore(provider, lane, profile) {
|
|
1222
|
+
const laneMetadata = readRecord(lane.metadata);
|
|
1223
|
+
const providerMetadata = readRecord(provider.metadata);
|
|
1224
|
+
const profileMetadata = readRecord(profile.metadata);
|
|
1225
|
+
const trust = finiteOrParsedNumber(laneMetadata.trustScore) ?? finiteOrParsedNumber(providerMetadata.trustScore) ?? finiteOrParsedNumber(profileMetadata.trustScore) ?? 1;
|
|
1226
|
+
const availability = finiteOrParsedNumber(laneMetadata.availabilityScore) ?? finiteOrParsedNumber(providerMetadata.availabilityScore) ?? 1;
|
|
1227
|
+
return Math.max(0, Math.min(1, trust)) * Math.max(0, Math.min(1, availability));
|
|
1228
|
+
}
|
|
1229
|
+
function routeSuccessProbability(input) {
|
|
1230
|
+
const laneMetadata = readRecord(input.lane.metadata);
|
|
1231
|
+
const providerMetadata = readRecord(input.provider.metadata);
|
|
1232
|
+
const profileMetadata = readRecord(input.profile.metadata);
|
|
1233
|
+
const success = finiteOrParsedNumber(input.explicit) ?? finiteOrParsedNumber(laneMetadata.successProbability) ?? finiteOrParsedNumber(providerMetadata.successProbability) ?? finiteOrParsedNumber(profileMetadata.successProbability) ?? input.utilityEstimate.successProbability ?? 1;
|
|
1234
|
+
return Math.max(0, Math.min(1, success));
|
|
1235
|
+
}
|
|
1236
|
+
function routePriceMultiplier(provider, lane, profile) {
|
|
1237
|
+
const laneMetadata = readRecord(lane.metadata);
|
|
1238
|
+
const providerMetadata = readRecord(provider.metadata);
|
|
1239
|
+
const profileMetadata = readRecord(profile.metadata);
|
|
1240
|
+
return Math.max(0.1, finiteOrParsedNumber(laneMetadata.priceMultiplier) ?? finiteOrParsedNumber(providerMetadata.priceMultiplier) ?? finiteOrParsedNumber(profileMetadata.priceMultiplier) ?? 1);
|
|
1241
|
+
}
|
|
1242
|
+
function routeScore(input) {
|
|
1243
|
+
const reasons = [...input.baseScore.reasons];
|
|
1244
|
+
const qualityFit = input.minimumQualityWeight > 0 ? input.profile.qualityWeight / input.minimumQualityWeight : input.profile.qualityWeight;
|
|
1245
|
+
const qualityBonus = Math.min(50, Math.max(0, input.profile.qualityWeight * 20));
|
|
1246
|
+
const costPenalty = Math.max(0, input.estimate.reservedCredits);
|
|
1247
|
+
const latency = latencyPenalty(input.profile);
|
|
1248
|
+
const congestion = input.pressure.congestionRatio * 45;
|
|
1249
|
+
const quota = quotaPressurePenalty(input.pressure);
|
|
1250
|
+
const attention = attentionPressurePenalty(input.pressure, input.attentionEstimate, input.attentionPolicy);
|
|
1251
|
+
const context = contextPressurePenalty(input.pressure, input.attentionEstimate, input.attentionPolicy);
|
|
1252
|
+
const priceMultiplier = routePriceMultiplier(input.provider, input.lane, input.profile);
|
|
1253
|
+
const utilityBonus = input.utilityEstimate.utilityScore > 0 ? Math.min(100, input.utilityEstimate.utilityPerCredit * 18 * Math.max(0.1, input.trustScore) * Math.max(0.1, input.successProbability)) : 0;
|
|
1254
|
+
const cooperativeBonus = input.cooperativeRouting ? input.trustScore * 20 + input.successProbability * 15 : 0;
|
|
1255
|
+
const predictedReserveImpact = input.reservePrediction?.reserveCredits ?? 0;
|
|
1256
|
+
const laneModelFit = input.profile.modelClass && input.lane.modelClass === input.profile.modelClass ? 15 : 0;
|
|
1257
|
+
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);
|
|
1259
|
+
if (laneModelFit > 0) reasons.push("execution_profile_model_class_match");
|
|
1260
|
+
if (congestion > 0) reasons.push("lane_congestion_pressure");
|
|
1261
|
+
if (quota > 0) reasons.push("quota_pressure");
|
|
1262
|
+
if (attention > 0) reasons.push("attention_pressure");
|
|
1263
|
+
if (context > 0) reasons.push("context_pressure");
|
|
1264
|
+
if (utilityBonus > 0) reasons.push("utility_scored");
|
|
1265
|
+
if (cooperativeBonus > 0) reasons.push("cooperative_route_scored");
|
|
1266
|
+
if (predictedReserveImpact > 0) reasons.push("predictive_reserve_applied");
|
|
1267
|
+
return {
|
|
1268
|
+
...input.baseScore,
|
|
1269
|
+
score,
|
|
1270
|
+
qualityFit,
|
|
1271
|
+
latencyPenalty: latency,
|
|
1272
|
+
quotaPressure: quota,
|
|
1273
|
+
congestionPenalty: congestion,
|
|
1274
|
+
attentionPenalty: attention,
|
|
1275
|
+
contextPenalty: context,
|
|
1276
|
+
utilityScore: input.utilityEstimate.utilityScore,
|
|
1277
|
+
utilityPerCredit: input.utilityEstimate.utilityPerCredit,
|
|
1278
|
+
predictedReserveImpact,
|
|
1279
|
+
trustScore: input.trustScore,
|
|
1280
|
+
successProbability: input.successProbability,
|
|
1281
|
+
costPenalty,
|
|
1282
|
+
executionProfileId: input.profile.id,
|
|
1283
|
+
reservedCredits: input.estimate.reservedCredits,
|
|
1284
|
+
attentionEstimate: input.attentionEstimate,
|
|
1285
|
+
reasons: [...new Set(reasons)]
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
function routeCandidateKey(candidate) {
|
|
1289
|
+
return `${candidate.providerId}:${candidate.laneId}:${candidate.grantId}:${candidate.executionProfileId ?? DEFAULT_EXECUTION_PROFILE_ID}`;
|
|
1290
|
+
}
|
|
293
1291
|
function routeAndReserveCapacity(input) {
|
|
294
1292
|
const providers = input.plan.providers.filter((provider2) => providerIsEligible(provider2, input));
|
|
295
1293
|
const grants = input.plan.grants.filter((grant2) => grantIsEligible(grant2, input));
|
|
296
1294
|
const candidates = [];
|
|
1295
|
+
const executionProfiles = executionProfilesForRoute(input);
|
|
1296
|
+
const minimumQualityWeight = routeMinimumQuality(input);
|
|
1297
|
+
const requiredContextTokens = routeRequiredContext(input);
|
|
1298
|
+
const attentionPolicy = normalizeAttentionPolicy(input.attentionPolicy);
|
|
1299
|
+
const utilityPolicy = normalizeUtilityPolicy(input.utilityPolicy);
|
|
1300
|
+
const predictiveReservePolicy = normalizePredictiveReservePolicy(input.predictiveReservePolicy);
|
|
1301
|
+
const hybridExecutionPlan = normalizeHybridExecutionPlan(input.hybridExecutionPlan ?? readRecord(input.metadata).hybridExecutionPlan);
|
|
1302
|
+
const preferredProfiles = new Set(stringArray(input.preferredExecutionProfiles));
|
|
1303
|
+
const disallowedProfiles = new Set(stringArray(input.disallowedExecutionProfiles));
|
|
1304
|
+
const cooperativeRouting = input.cooperativeRouting === true || readRecord(input.metadata).cooperativeRouting === true;
|
|
1305
|
+
const trustRequirement = finiteOrParsedNumber(input.trustRequirement);
|
|
297
1306
|
for (const grant2 of grants) {
|
|
298
1307
|
const provider2 = providers.find((candidate) => candidate.id === grant2.capacityProviderId);
|
|
299
1308
|
if (!provider2) continue;
|
|
@@ -301,31 +1310,135 @@ function routeAndReserveCapacity(input) {
|
|
|
301
1310
|
(lane2) => lane2.capacityProviderId === provider2.id && (!grant2.laneId || grant2.laneId === lane2.id)
|
|
302
1311
|
);
|
|
303
1312
|
for (const lane2 of lanes) {
|
|
304
|
-
const reasons = lanePolicyReasons(lane2, input);
|
|
305
1313
|
const remainingCredits = grantRemainingCredits(input.plan, grant2);
|
|
306
|
-
|
|
307
|
-
|
|
1314
|
+
const pressure = capacityRoutePressure(input.plan, provider2, lane2);
|
|
1315
|
+
for (const profile of executionProfiles) {
|
|
1316
|
+
const estimate = estimateForRouteProfile(input, profile);
|
|
1317
|
+
const attentionEstimate = attentionEstimateForRoute(input, profile);
|
|
1318
|
+
const utilityEstimate = input.utilityEstimate ?? estimateUtilityForTask({
|
|
1319
|
+
classification: input.classification,
|
|
1320
|
+
executionProfile: profile,
|
|
1321
|
+
estimate,
|
|
1322
|
+
utilityPolicy,
|
|
1323
|
+
utilityValue: input.utilityValue,
|
|
1324
|
+
maintenanceValue: input.maintenanceValue,
|
|
1325
|
+
priority: finiteOrParsedNumber(readRecord(input.metadata).priority),
|
|
1326
|
+
deadlineAt: input.deadlineAt ?? (typeof readRecord(input.metadata).deadlineAt === "string" ? readRecord(input.metadata).deadlineAt : null),
|
|
1327
|
+
successProbability: input.successProbability,
|
|
1328
|
+
metadata: input.metadata,
|
|
1329
|
+
source: input.source ?? "capacity_router"
|
|
1330
|
+
});
|
|
1331
|
+
const reservePrediction = predictReserveForCapacityPlan({
|
|
1332
|
+
plan: input.plan,
|
|
1333
|
+
policy: predictiveReservePolicy,
|
|
1334
|
+
remainingCredits,
|
|
1335
|
+
metadata: input.metadata
|
|
1336
|
+
});
|
|
1337
|
+
const trustScore = routeTrustScore(provider2, lane2, profile);
|
|
1338
|
+
const successProbability = routeSuccessProbability({
|
|
1339
|
+
provider: provider2,
|
|
1340
|
+
lane: lane2,
|
|
1341
|
+
profile,
|
|
1342
|
+
explicit: input.successProbability,
|
|
1343
|
+
utilityEstimate
|
|
1344
|
+
});
|
|
1345
|
+
const estimateInput = { ...input, estimate };
|
|
1346
|
+
const reasons = lanePolicyReasons(lane2, estimateInput);
|
|
1347
|
+
let spilloverReason = null;
|
|
1348
|
+
if (trustRequirement !== null && trustScore < trustRequirement) reasons.push("trust_below_requirement");
|
|
1349
|
+
if (utilityPolicy.minimumUtilityScore !== null && utilityEstimate.utilityScore < utilityPolicy.minimumUtilityScore) reasons.push("utility_below_minimum");
|
|
1350
|
+
if (utilityPolicy.minimumUtilityPerCredit !== null && utilityEstimate.utilityPerCredit < utilityPolicy.minimumUtilityPerCredit) reasons.push("utility_per_credit_below_minimum");
|
|
1351
|
+
if (predictiveReservePolicy.enabled && reservePrediction.reserveCredits > 0 && reservePrediction.activelyAllocatableCredits < estimate.reservedCredits && utilityEstimate.utilityScore < 50) {
|
|
1352
|
+
reasons.push("predictive_reserve_blocked");
|
|
1353
|
+
}
|
|
1354
|
+
const routeMaxAttentionLoad = attentionPolicy.maxAttentionLoad ?? pressure.maxAttentionLoad;
|
|
1355
|
+
if (routeMaxAttentionLoad !== null && routeMaxAttentionLoad > 0) {
|
|
1356
|
+
const allocatableAttention = Math.max(0, routeMaxAttentionLoad * (1 - attentionPolicy.reserveAttentionPercent / 100));
|
|
1357
|
+
const projectedAttention = pressure.activeAttentionLoad + attentionEstimate.totalAttentionWeight;
|
|
1358
|
+
if (projectedAttention > allocatableAttention) reasons.push("attention_load_exceeded");
|
|
1359
|
+
const availableAttention = Math.max(0, allocatableAttention - pressure.activeAttentionLoad);
|
|
1360
|
+
const minimumAttentionAvailable = finiteOrParsedNumber(input.minimumAttentionAvailable);
|
|
1361
|
+
if (minimumAttentionAvailable !== null && availableAttention < minimumAttentionAvailable) reasons.push("minimum_attention_unavailable");
|
|
1362
|
+
}
|
|
1363
|
+
const routeMaxContextTokens = attentionPolicy.maxContextTokens ?? pressure.maxContextTokens;
|
|
1364
|
+
if (routeMaxContextTokens !== null && routeMaxContextTokens > 0) {
|
|
1365
|
+
const projectedContextTokens = pressure.activeContextTokens + attentionEstimate.estimatedContextTokens;
|
|
1366
|
+
const projectedSaturation = projectedContextTokens / routeMaxContextTokens * 100;
|
|
1367
|
+
if (projectedContextTokens > routeMaxContextTokens || projectedSaturation > attentionPolicy.maxContextSaturationPercent) {
|
|
1368
|
+
reasons.push("context_saturation_exceeded");
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
if (disallowedProfiles.has(profile.id)) reasons.push("execution_profile_disallowed");
|
|
1372
|
+
if (preferredProfiles.size > 0 && !preferredProfiles.has(profile.id)) reasons.push("execution_profile_not_preferred");
|
|
1373
|
+
if (!laneSupportsExecutionProfile(lane2, profile)) reasons.push("execution_profile_not_supported");
|
|
1374
|
+
if (minimumQualityWeight > 0 && profile.qualityWeight < minimumQualityWeight) reasons.push("quality_below_minimum");
|
|
1375
|
+
if (requiredContextTokens > 0 && profile.contextWindowTokens !== null && profile.contextWindowTokens !== void 0 && profile.contextWindowTokens < requiredContextTokens) {
|
|
1376
|
+
reasons.push("context_window_too_small");
|
|
1377
|
+
}
|
|
1378
|
+
if (pressure.providerUnavailable) reasons.push("provider_unavailable");
|
|
1379
|
+
if (pressure.maxActiveReservations !== null && pressure.congestionRatio >= 1) reasons.push("lane_congested");
|
|
1380
|
+
if (pressure.quotaRemainingPercent !== null && pressure.quotaRemainingPercent <= 0) reasons.push("quota_exhausted");
|
|
1381
|
+
if (pressure.sessionRemainingMinutes !== null && pressure.sessionRemainingMinutes <= 0) reasons.push("session_exhausted");
|
|
1382
|
+
if (remainingCredits !== null && remainingCredits < estimate.reservedCredits) {
|
|
1383
|
+
if (grant2.overflowPolicy === "approval_required") {
|
|
1384
|
+
reasons.push("approval_required");
|
|
1385
|
+
} else if (grant2.overflowPolicy === "fallback_lane") {
|
|
1386
|
+
spilloverReason = "fallback_lane";
|
|
1387
|
+
reasons.push("fallback_lane_exhausted");
|
|
1388
|
+
} else if (grant2.overflowPolicy === "deny" || grant2.overflowPolicy === "hard_grant") {
|
|
1389
|
+
reasons.push("insufficient_budget");
|
|
1390
|
+
} else {
|
|
1391
|
+
reasons.push("soft_budget_pressure");
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
const baseScore = scoreCapacityLane({
|
|
1395
|
+
lane: lane2,
|
|
1396
|
+
grant: grant2,
|
|
1397
|
+
remainingCredits,
|
|
1398
|
+
taskKind: input.taskKind ?? estimate.taskSignature,
|
|
1399
|
+
requiredCapabilities: input.requiredCapabilities,
|
|
1400
|
+
modelClass: input.modelClass ?? profile.modelClass ?? null
|
|
1401
|
+
});
|
|
1402
|
+
const score = routeScore({
|
|
1403
|
+
provider: provider2,
|
|
1404
|
+
lane: lane2,
|
|
1405
|
+
grant: grant2,
|
|
1406
|
+
estimate,
|
|
1407
|
+
profile,
|
|
1408
|
+
remainingCredits,
|
|
1409
|
+
pressure,
|
|
1410
|
+
minimumQualityWeight,
|
|
1411
|
+
attentionEstimate,
|
|
1412
|
+
attentionPolicy,
|
|
1413
|
+
utilityEstimate,
|
|
1414
|
+
reservePrediction,
|
|
1415
|
+
trustScore,
|
|
1416
|
+
successProbability,
|
|
1417
|
+
cooperativeRouting,
|
|
1418
|
+
baseScore
|
|
1419
|
+
});
|
|
1420
|
+
candidates.push({
|
|
1421
|
+
providerId: provider2.id,
|
|
1422
|
+
laneId: lane2.id,
|
|
1423
|
+
grantId: grant2.id,
|
|
1424
|
+
executionProfileId: profile.id,
|
|
1425
|
+
remainingCredits,
|
|
1426
|
+
score,
|
|
1427
|
+
eligible: reasons.filter(
|
|
1428
|
+
(reason) => reason !== "soft_budget_pressure" && reason !== "execution_profile_not_preferred"
|
|
1429
|
+
).length === 0,
|
|
1430
|
+
reasons: [.../* @__PURE__ */ new Set([...reasons, ...score.reasons])],
|
|
1431
|
+
estimate,
|
|
1432
|
+
pressure,
|
|
1433
|
+
qualityFit: score.qualityFit,
|
|
1434
|
+
attentionEstimate,
|
|
1435
|
+
utilityEstimate,
|
|
1436
|
+
reservePrediction,
|
|
1437
|
+
trustScore,
|
|
1438
|
+
successProbability,
|
|
1439
|
+
spilloverReason
|
|
1440
|
+
});
|
|
308
1441
|
}
|
|
309
|
-
if (remainingCredits !== null && remainingCredits < input.estimate.reservedCredits && grant2.overflowPolicy === "approval_required") {
|
|
310
|
-
reasons.push("approval_required");
|
|
311
|
-
}
|
|
312
|
-
const score = scoreCapacityLane({
|
|
313
|
-
lane: lane2,
|
|
314
|
-
grant: grant2,
|
|
315
|
-
remainingCredits,
|
|
316
|
-
taskKind: input.taskKind ?? input.estimate.taskSignature,
|
|
317
|
-
requiredCapabilities: input.requiredCapabilities,
|
|
318
|
-
modelClass: input.modelClass ?? null
|
|
319
|
-
});
|
|
320
|
-
candidates.push({
|
|
321
|
-
providerId: provider2.id,
|
|
322
|
-
laneId: lane2.id,
|
|
323
|
-
grantId: grant2.id,
|
|
324
|
-
remainingCredits,
|
|
325
|
-
score,
|
|
326
|
-
eligible: reasons.length === 0,
|
|
327
|
-
reasons
|
|
328
|
-
});
|
|
329
1442
|
}
|
|
330
1443
|
}
|
|
331
1444
|
if (input.plan.providers.length === 0 || providers.length === 0) {
|
|
@@ -346,7 +1459,9 @@ function routeAndReserveCapacity(input) {
|
|
|
346
1459
|
candidates
|
|
347
1460
|
};
|
|
348
1461
|
}
|
|
349
|
-
const eligible = candidates.filter((candidate) => candidate.eligible).sort(
|
|
1462
|
+
const eligible = candidates.filter((candidate) => candidate.eligible).sort(
|
|
1463
|
+
(left, right) => right.score.score - left.score.score || (left.estimate?.reservedCredits ?? Number.MAX_SAFE_INTEGER) - (right.estimate?.reservedCredits ?? Number.MAX_SAFE_INTEGER) || routeCandidateKey(left).localeCompare(routeCandidateKey(right))
|
|
1464
|
+
);
|
|
350
1465
|
const selected = eligible[0] ?? null;
|
|
351
1466
|
if (!selected) {
|
|
352
1467
|
const hasApprovalBlock = candidates.some((candidate) => candidate.reasons.includes("approval_required"));
|
|
@@ -362,6 +1477,7 @@ function routeAndReserveCapacity(input) {
|
|
|
362
1477
|
const provider = providers.find((candidate) => candidate.id === selected.providerId);
|
|
363
1478
|
const lane = input.plan.lanes.find((candidate) => candidate.id === selected.laneId);
|
|
364
1479
|
const grant = grants.find((candidate) => candidate.id === selected.grantId);
|
|
1480
|
+
const selectedEstimate = selected.estimate ?? input.estimate;
|
|
365
1481
|
if (!provider || !lane || !grant) {
|
|
366
1482
|
return {
|
|
367
1483
|
ok: false,
|
|
@@ -375,12 +1491,22 @@ function routeAndReserveCapacity(input) {
|
|
|
375
1491
|
providerId: candidate.providerId,
|
|
376
1492
|
laneId: candidate.laneId,
|
|
377
1493
|
grantId: candidate.grantId,
|
|
1494
|
+
executionProfileId: candidate.executionProfileId ?? null,
|
|
378
1495
|
remainingCredits: candidate.remainingCredits,
|
|
379
1496
|
eligible: candidate.eligible,
|
|
380
1497
|
reasons: candidate.reasons,
|
|
381
|
-
score: candidate.score.score
|
|
1498
|
+
score: candidate.score.score,
|
|
1499
|
+
reservedCredits: candidate.estimate?.reservedCredits ?? null,
|
|
1500
|
+
qualityFit: candidate.qualityFit ?? null,
|
|
1501
|
+
attentionEstimate: candidate.attentionEstimate ?? null,
|
|
1502
|
+
utilityEstimate: candidate.utilityEstimate ?? null,
|
|
1503
|
+
reservePrediction: candidate.reservePrediction ?? null,
|
|
1504
|
+
trustScore: candidate.trustScore ?? null,
|
|
1505
|
+
successProbability: candidate.successProbability ?? null,
|
|
1506
|
+
pressure: candidate.pressure ?? null,
|
|
1507
|
+
spilloverReason: candidate.spilloverReason ?? null
|
|
382
1508
|
}));
|
|
383
|
-
const scorePayload = Object.fromEntries(candidates.map((candidate) => [candidate
|
|
1509
|
+
const scorePayload = Object.fromEntries(candidates.map((candidate) => [routeCandidateKey(candidate), candidate.score]));
|
|
384
1510
|
const reservation = {
|
|
385
1511
|
capacityProviderId: provider.id,
|
|
386
1512
|
laneId: lane.id,
|
|
@@ -389,13 +1515,20 @@ function routeAndReserveCapacity(input) {
|
|
|
389
1515
|
workDayId: input.workDayId ?? null,
|
|
390
1516
|
taskId: input.taskId ?? null,
|
|
391
1517
|
state: "reserved",
|
|
392
|
-
reservedCredits:
|
|
1518
|
+
reservedCredits: selectedEstimate.reservedCredits,
|
|
393
1519
|
metadata: {
|
|
394
1520
|
...input.metadata ?? {},
|
|
395
1521
|
grantId: grant.id,
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
1522
|
+
executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
|
|
1523
|
+
taskSignature: selectedEstimate.taskSignature,
|
|
1524
|
+
estimatedCreditsP50: selectedEstimate.estimatedCreditsP50,
|
|
1525
|
+
estimatedCreditsP90: selectedEstimate.estimatedCreditsP90,
|
|
1526
|
+
routingScore: selected.score.score,
|
|
1527
|
+
attentionEstimate: selected.attentionEstimate ?? null,
|
|
1528
|
+
utilityEstimate: selected.utilityEstimate ?? null,
|
|
1529
|
+
reservePrediction: selected.reservePrediction ?? null,
|
|
1530
|
+
hybridExecutionPlan,
|
|
1531
|
+
routingCandidates: candidatePayload
|
|
399
1532
|
}
|
|
400
1533
|
};
|
|
401
1534
|
const routingDecision = {
|
|
@@ -404,7 +1537,7 @@ function routeAndReserveCapacity(input) {
|
|
|
404
1537
|
projectId: input.plan.projectId,
|
|
405
1538
|
selectedProviderId: provider.id,
|
|
406
1539
|
selectedLaneId: lane.id,
|
|
407
|
-
selectedModel: input.selectedModel ?? null,
|
|
1540
|
+
selectedModel: input.selectedModel ?? selected.executionProfileId ?? null,
|
|
408
1541
|
decision: "selected",
|
|
409
1542
|
reason: selected.score.reasons.length > 0 ? selected.score.reasons.join(",") : "best_eligible_lane",
|
|
410
1543
|
candidates: candidatePayload,
|
|
@@ -412,8 +1545,14 @@ function routeAndReserveCapacity(input) {
|
|
|
412
1545
|
metadata: {
|
|
413
1546
|
...input.metadata ?? {},
|
|
414
1547
|
grantId: grant.id,
|
|
1548
|
+
executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
|
|
415
1549
|
remainingCreditsBefore: selected.remainingCredits,
|
|
416
|
-
reservedCredits:
|
|
1550
|
+
reservedCredits: selectedEstimate.reservedCredits,
|
|
1551
|
+
attentionEstimate: selected.attentionEstimate ?? null,
|
|
1552
|
+
utilityEstimate: selected.utilityEstimate ?? null,
|
|
1553
|
+
reservePrediction: selected.reservePrediction ?? null,
|
|
1554
|
+
hybridExecutionPlan,
|
|
1555
|
+
escalationPath: candidatePayload.filter((candidate) => candidate.eligible).sort((left, right) => Number(left.reservedCredits ?? 0) - Number(right.reservedCredits ?? 0)).map((candidate) => candidate.executionProfileId).filter((value, index, array) => typeof value === "string" && value && array.indexOf(value) === index)
|
|
417
1556
|
}
|
|
418
1557
|
};
|
|
419
1558
|
const ledgerEntry = {
|
|
@@ -424,12 +1563,17 @@ function routeAndReserveCapacity(input) {
|
|
|
424
1563
|
workDayId: input.workDayId ?? null,
|
|
425
1564
|
taskId: input.taskId ?? null,
|
|
426
1565
|
phase: "reservation_created",
|
|
427
|
-
credits:
|
|
1566
|
+
credits: selectedEstimate.reservedCredits,
|
|
428
1567
|
source: input.source ?? "capacity_coordinator",
|
|
429
1568
|
metadata: {
|
|
430
1569
|
...input.metadata ?? {},
|
|
431
1570
|
grantId: grant.id,
|
|
432
|
-
|
|
1571
|
+
executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
|
|
1572
|
+
taskSignature: selectedEstimate.taskSignature,
|
|
1573
|
+
attentionEstimate: selected.attentionEstimate ?? null,
|
|
1574
|
+
utilityEstimate: selected.utilityEstimate ?? null,
|
|
1575
|
+
reservePrediction: selected.reservePrediction ?? null,
|
|
1576
|
+
hybridExecutionPlan
|
|
433
1577
|
}
|
|
434
1578
|
};
|
|
435
1579
|
return {
|
|
@@ -437,7 +1581,7 @@ function routeAndReserveCapacity(input) {
|
|
|
437
1581
|
provider,
|
|
438
1582
|
lane,
|
|
439
1583
|
grant,
|
|
440
|
-
estimate:
|
|
1584
|
+
estimate: selectedEstimate,
|
|
441
1585
|
remainingCreditsBefore: selected.remainingCredits,
|
|
442
1586
|
reservation,
|
|
443
1587
|
routingDecision,
|
|
@@ -448,22 +1592,61 @@ function routeAndReserveCapacity(input) {
|
|
|
448
1592
|
grantId: grant.id,
|
|
449
1593
|
reservationId: reservation.id ?? null,
|
|
450
1594
|
routingDecisionId: routingDecision.id ?? null,
|
|
451
|
-
estimatedCreditsP50:
|
|
452
|
-
estimatedCreditsP90:
|
|
453
|
-
reservedCredits:
|
|
1595
|
+
estimatedCreditsP50: selectedEstimate.estimatedCreditsP50,
|
|
1596
|
+
estimatedCreditsP90: selectedEstimate.estimatedCreditsP90,
|
|
1597
|
+
reservedCredits: selectedEstimate.reservedCredits,
|
|
1598
|
+
executionProfileId: selected.executionProfileId ?? selectedEstimate.executionProfileId ?? null,
|
|
1599
|
+
costMultiplier: selectedEstimate.costMultiplier ?? null,
|
|
1600
|
+
score: selected.score.score,
|
|
1601
|
+
attentionEstimate: selected.attentionEstimate ?? null,
|
|
1602
|
+
utilityEstimate: selected.utilityEstimate ?? null,
|
|
1603
|
+
reservePrediction: selected.reservePrediction ?? null,
|
|
1604
|
+
hybridExecutionPlan,
|
|
1605
|
+
candidates: candidatePayload
|
|
454
1606
|
},
|
|
455
1607
|
candidates
|
|
456
1608
|
};
|
|
457
1609
|
}
|
|
458
1610
|
export {
|
|
1611
|
+
DEFAULT_EXECUTION_PROFILES,
|
|
1612
|
+
DEFAULT_EXECUTION_PROFILE_ID,
|
|
1613
|
+
DEFAULT_TASK_ADMISSION_POLICY,
|
|
1614
|
+
buildTaskEstimateProfileFromActuals,
|
|
1615
|
+
capacityRoutePressure,
|
|
1616
|
+
computeWorkdayBudgetEnvelope,
|
|
459
1617
|
createReservationReleaseEntry,
|
|
1618
|
+
decideTaskAdmission,
|
|
1619
|
+
estimateAttentionForTask,
|
|
1620
|
+
estimateConfidenceFromProfile,
|
|
1621
|
+
estimateForClassification,
|
|
1622
|
+
estimateLearningPercentile,
|
|
1623
|
+
estimateLearningVariance,
|
|
1624
|
+
estimateProfileConfidenceScore,
|
|
1625
|
+
estimateUtilityForTask,
|
|
1626
|
+
isInterruptedUsageActual,
|
|
1627
|
+
mutationRequiresRepositoryClaim,
|
|
1628
|
+
normalizeAttentionPolicy,
|
|
1629
|
+
normalizeExecutionProfile,
|
|
1630
|
+
normalizeHybridExecutionPlan,
|
|
1631
|
+
normalizePlanningPolicy,
|
|
1632
|
+
normalizePredictiveReservePolicy,
|
|
1633
|
+
normalizeTaskAdmissionPolicy,
|
|
1634
|
+
normalizeTaskPlanProposal,
|
|
1635
|
+
normalizeUtilityPolicy,
|
|
1636
|
+
predictReserveForCapacityPlan,
|
|
1637
|
+
progressivelyAdmitPlanProposal,
|
|
1638
|
+
rankPlannedTaskNodes,
|
|
460
1639
|
reservationHasCapacity,
|
|
461
1640
|
reserveCreditsForEstimate,
|
|
462
1641
|
routeAndReserveCapacity,
|
|
463
1642
|
scoreCapacityLane,
|
|
464
1643
|
selectBestCapacityLane,
|
|
1644
|
+
selectTaskEstimateProfile,
|
|
465
1645
|
settleCapacityActuals,
|
|
1646
|
+
shouldInterruptForCapacity,
|
|
466
1647
|
summarizeCapacityPlan,
|
|
467
1648
|
summarizeProjectCapacityPlan,
|
|
468
|
-
summarizeTeamCapacityPlan
|
|
1649
|
+
summarizeTeamCapacityPlan,
|
|
1650
|
+
synthesizePlanEstimate,
|
|
1651
|
+
validateTaskPlanProposal
|
|
469
1652
|
};
|