@treeseed/sdk 0.13.0-rc.119 → 0.13.0-rc.120
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/dist/.treeseed-build-complete.json +1 -1
- package/dist/agent-capacity/allocation.d.ts +0 -3
- package/dist/agent-capacity/allocation.js +0 -221
- package/dist/agent-capacity/contracts/capacity/assignments/agent-execution.d.ts +280 -84
- package/dist/agent-capacity/contracts/capacity/assignments/agent-execution.js +22 -1
- package/dist/agent-capacity/contracts/capacity/assignments/assignment-records.d.ts +5 -0
- package/dist/agent-capacity/contracts/capacity/workdays/assignment-allocation.d.ts +82 -0
- package/dist/agent-capacity/contracts/capacity/workdays/assignment-allocation.js +115 -0
- package/dist/agent-capacity/contracts/capacity/workdays/capability-accounting.d.ts +142 -0
- package/dist/agent-capacity/contracts/capacity/workdays/capability-accounting.js +51 -0
- package/dist/agent-capacity/contracts/capacity/workdays/workday-allocation.d.ts +167 -35
- package/dist/agent-capacity/contracts/capacity/workdays/workday-allocation.js +59 -20
- package/dist/agent-capacity/contracts/capacity/workdays/workday-records.d.ts +2 -21
- package/dist/agent-capacity/contracts/support/execution-graph.d.ts +18 -18
- package/dist/agent-capacity/contracts/support/time-capacity.d.ts +0 -14
- package/dist/agent-capacity/contracts/support/time-capacity.js +1 -8
- package/dist/agent-capacity/validation/agent-definition-schema.d.ts +338 -338
- package/dist/capacity/agents/agent-capacity.d.ts +2 -0
- package/dist/capacity/agents/agent-capacity.js +2 -0
- package/dist/capacity-provider/client.d.ts +5 -3
- package/dist/capacity-provider/contracts/capacity-facts.d.ts +14 -0
- package/dist/capacity-provider/contracts/governance.d.ts +6 -0
- package/dist/capacity-provider/sandbox-contracts.d.ts +55 -1
- package/dist/capacity-provider/sandbox-contracts.js +4 -0
- package/dist/capacity-provider/source-workspace.d.ts +69 -37
- package/dist/capacity-provider/source-workspace.js +36 -5
- package/dist/content/validation/auxiliary-content-schemas.d.ts +10 -10
- package/dist/content/validation/content-model-schemas.d.ts +613 -613
- package/dist/content/validation/content-model-schemas.js +14 -2
- package/dist/content/validation/portable-content-data.d.ts +22 -22
- package/dist/deployment/postgres/requirements.d.ts +3 -2
- package/dist/deployment/postgres/requirements.js +3 -1
- package/dist/operator-contracts/canonical-command-tree.js +20 -6
- package/dist/operator-contracts/catalog/communication-operations.d.ts +2 -0
- package/dist/operator-contracts/catalog/workdays/profile-commands.js +4 -4
- package/dist/operator-contracts/catalog/workdays/profile-operations.d.ts +28 -3
- package/dist/operator-contracts/catalog/workdays/profile-operations.js +10 -8
- package/dist/operator-contracts/catalog/workdays/selection-commands.js +13 -0
- package/dist/operator-contracts/command-tree.d.ts +1 -1
- package/dist/operator-contracts/communication/contracts.d.ts +6 -0
- package/dist/operator-contracts/communication/contracts.js +2 -0
- package/dist/operator-contracts/control-plane-operations.d.ts +34 -3
- package/dist/operator-contracts/control-plane-operations.js +1 -0
- package/dist/operator-contracts/index.d.ts +0 -1
- package/dist/operator-contracts/index.js +0 -1
- package/dist/operator-contracts/workday-lifecycle.d.ts +8 -20
- package/dist/operator-contracts/workday-lifecycle.js +15 -5
- package/dist/secrets-capability/shared-resources.d.ts +6 -6
- package/package.json +1 -1
- package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.d.ts +0 -29
- package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.js +0 -110
- package/dist/operator-contracts/workday-profile.d.ts +0 -72
- package/dist/operator-contracts/workday-profile.js +0 -106
|
@@ -57,7 +57,15 @@ const effectiveActivityProfileSchema = activityProfileSchema.omit({ dependsOn: t
|
|
|
57
57
|
handlerOrigin: z.enum(["agent-package", "project-runtime"]),
|
|
58
58
|
permissionCeiling: activityProfileSchema.shape.permissions
|
|
59
59
|
}).omit({ permissions: true }).strict();
|
|
60
|
-
const providerSelectionSchema = z.object({
|
|
60
|
+
const providerSelectionSchema = z.object({
|
|
61
|
+
providerId: identifier,
|
|
62
|
+
offerId: identifier,
|
|
63
|
+
executionProviderId: identifier,
|
|
64
|
+
modelConfigurationId: identifier,
|
|
65
|
+
executionCapabilityId: identifier,
|
|
66
|
+
offerRevision: z.number().int().positive(),
|
|
67
|
+
runtimeBuild: digest
|
|
68
|
+
}).strict();
|
|
61
69
|
const limitsSchema = z.object({
|
|
62
70
|
maximumSeconds: z.number().int().positive(),
|
|
63
71
|
maximumContextBytes: z.number().int().positive(),
|
|
@@ -115,6 +123,17 @@ const diagnosticSchema = z.object({
|
|
|
115
123
|
message: z.string().min(1),
|
|
116
124
|
ref: exactEntityReferenceSchema.optional()
|
|
117
125
|
}).strict();
|
|
126
|
+
const assignmentTimingAwarenessReceiptSchema = z.object({
|
|
127
|
+
schemaVersion: z.literal("treeseed.assignment-timing-awareness/v1"),
|
|
128
|
+
requiredChecks: z.literal(2),
|
|
129
|
+
completedChecks: z.number().int().min(2),
|
|
130
|
+
firstTool: z.literal("treedx:treeseed_time_status"),
|
|
131
|
+
firstToolSucceeded: z.literal(true),
|
|
132
|
+
lastTool: z.literal("treedx:treeseed_time_status"),
|
|
133
|
+
lastToolSucceeded: z.literal(true),
|
|
134
|
+
firstToolCompliant: z.literal(true),
|
|
135
|
+
finalToolCompliant: z.literal(true)
|
|
136
|
+
}).strict();
|
|
118
137
|
const assignmentResultSchema = z.object({
|
|
119
138
|
schemaVersion: z.literal("treeseed.assignment-result/v1"),
|
|
120
139
|
id: identifier,
|
|
@@ -125,6 +144,7 @@ const assignmentResultSchema = z.object({
|
|
|
125
144
|
verification: z.array(verificationRecordSchema),
|
|
126
145
|
usage: usageSchema,
|
|
127
146
|
diagnostics: z.array(diagnosticSchema),
|
|
147
|
+
timingAwareness: assignmentTimingAwarenessReceiptSchema.optional(),
|
|
128
148
|
completedAt: timestamp
|
|
129
149
|
}).strict();
|
|
130
150
|
const authorizedContextItemSchema = z.object({
|
|
@@ -143,6 +163,7 @@ export {
|
|
|
143
163
|
assignmentContextSchema,
|
|
144
164
|
assignmentReferenceSchema,
|
|
145
165
|
assignmentResultSchema,
|
|
166
|
+
assignmentTimingAwarenessReceiptSchema,
|
|
146
167
|
assignmentWorkspaceSchema,
|
|
147
168
|
authorizedContextItemSchema,
|
|
148
169
|
diagnosticSchema,
|
|
@@ -212,6 +212,11 @@ export interface ProviderAssignment {
|
|
|
212
212
|
updatedAt: string;
|
|
213
213
|
}
|
|
214
214
|
export interface ProviderAssignmentLifecycleRequest {
|
|
215
|
+
activeSeconds?: number | null;
|
|
216
|
+
elapsedSeconds?: number | null;
|
|
217
|
+
actualUsd?: number | null;
|
|
218
|
+
providerUnits?: number | null;
|
|
219
|
+
usage?: Record<string, unknown> | null;
|
|
215
220
|
runnerId?: string | null;
|
|
216
221
|
leaseToken?: string | null;
|
|
217
222
|
leaseSeconds?: number | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Pure allocation arithmetic. Admission must reserve the returned amount atomically. */
|
|
2
|
+
import { type AppliedWorkday } from './workday-allocation.js';
|
|
3
|
+
export interface AllocationShare {
|
|
4
|
+
id: string;
|
|
5
|
+
weight: number;
|
|
6
|
+
committedSeconds: number;
|
|
7
|
+
maximumAdditionalSeconds: number;
|
|
8
|
+
}
|
|
9
|
+
/** Redistributable shares; existing reservations/consumption retain their entitlement. */
|
|
10
|
+
export declare function distributeAllocationSeconds(remainingSeconds: number, shares: AllocationShare[]): Record<string, number>;
|
|
11
|
+
/** Resolve each live workday's phase opportunity from the same shared hard supply.
|
|
12
|
+
* maximumAdditionalSeconds is graph-ready demand, bounded by supply windows/concurrency.
|
|
13
|
+
* Existing commitments are never resized; execution mode does not alter entitlement.
|
|
14
|
+
*/
|
|
15
|
+
export declare function allocateWorkdayCapacity(input: {
|
|
16
|
+
remainingSeconds: number;
|
|
17
|
+
now: string;
|
|
18
|
+
workdays: Array<{
|
|
19
|
+
plan: AppliedWorkday;
|
|
20
|
+
committedSeconds: number;
|
|
21
|
+
planningCommittedSeconds: number;
|
|
22
|
+
maximumAdditionalSeconds: number;
|
|
23
|
+
}>;
|
|
24
|
+
}): {
|
|
25
|
+
[k: string]: {
|
|
26
|
+
shareSeconds: number;
|
|
27
|
+
phase: "planning" | "ended" | "acting";
|
|
28
|
+
phaseRemainingSeconds: number;
|
|
29
|
+
weight: number;
|
|
30
|
+
committedSeconds: number;
|
|
31
|
+
planningCommittedSeconds: number;
|
|
32
|
+
remainingSupplySeconds: number;
|
|
33
|
+
totalEligibleWeight: number;
|
|
34
|
+
availableSeconds: number;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export interface AllocationMeasurement {
|
|
38
|
+
id: string;
|
|
39
|
+
completedAt: string;
|
|
40
|
+
expectedSeconds: number;
|
|
41
|
+
allocatedSeconds: number;
|
|
42
|
+
activeSeconds: number;
|
|
43
|
+
outcome: 'completed' | 'expired' | 'cancelled' | 'credential-failure' | 'infrastructure-failure' | 'invalid-result';
|
|
44
|
+
}
|
|
45
|
+
/** Caller scopes history to exact provider/model, capability, class, and activity. */
|
|
46
|
+
export declare function calibrateAssignmentSeconds(estimate: {
|
|
47
|
+
expectedSeconds: number;
|
|
48
|
+
maximumSeconds: number;
|
|
49
|
+
}, measurements: AllocationMeasurement[]): {
|
|
50
|
+
seconds: number;
|
|
51
|
+
multiplier: number;
|
|
52
|
+
measurementIds: string[];
|
|
53
|
+
};
|
|
54
|
+
export interface AssignmentAllocationConstraint {
|
|
55
|
+
id: string;
|
|
56
|
+
remainingSeconds: number;
|
|
57
|
+
}
|
|
58
|
+
export declare function calculateAssignmentAllocation(input: {
|
|
59
|
+
estimate: {
|
|
60
|
+
minimumSeconds: number;
|
|
61
|
+
expectedSeconds: number;
|
|
62
|
+
maximumSeconds: number;
|
|
63
|
+
};
|
|
64
|
+
measurements: AllocationMeasurement[];
|
|
65
|
+
constraints: AssignmentAllocationConstraint[];
|
|
66
|
+
providerMinimumSeconds?: number;
|
|
67
|
+
providerMaximumSeconds?: number;
|
|
68
|
+
profileMaximumSeconds?: number;
|
|
69
|
+
planningTurnMaximumSeconds?: number;
|
|
70
|
+
}): {
|
|
71
|
+
admitted: boolean;
|
|
72
|
+
allocatedSeconds: number;
|
|
73
|
+
minimumSeconds: number;
|
|
74
|
+
desiredSeconds: number;
|
|
75
|
+
calibration: {
|
|
76
|
+
seconds: number;
|
|
77
|
+
multiplier: number;
|
|
78
|
+
measurementIds: string[];
|
|
79
|
+
};
|
|
80
|
+
limitingConstraint: string;
|
|
81
|
+
constraints: AssignmentAllocationConstraint[];
|
|
82
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { workdayPhase } from "./workday-allocation.js";
|
|
2
|
+
function nonnegative(value) {
|
|
3
|
+
if (!Number.isFinite(value) || value < 0) throw new Error("allocation_amount_invalid");
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
function distributeAllocationSeconds(remainingSeconds, shares) {
|
|
7
|
+
nonnegative(remainingSeconds);
|
|
8
|
+
if (new Set(shares.map((share) => share.id)).size !== shares.length) throw new Error("allocation_share_duplicate");
|
|
9
|
+
const ordered = [...shares].sort((a, b) => a.id.localeCompare(b.id));
|
|
10
|
+
for (const share of ordered) {
|
|
11
|
+
if (!Number.isFinite(share.weight) || share.weight <= 0) throw new Error("allocation_weight_invalid");
|
|
12
|
+
nonnegative(share.committedSeconds);
|
|
13
|
+
nonnegative(share.maximumAdditionalSeconds);
|
|
14
|
+
}
|
|
15
|
+
const result = Object.fromEntries(ordered.map((share) => [share.id, 0]));
|
|
16
|
+
const capacity = Math.min(remainingSeconds, ordered.reduce((sum, share) => sum + share.maximumAdditionalSeconds, 0));
|
|
17
|
+
if (!capacity) return result;
|
|
18
|
+
let low = 0;
|
|
19
|
+
let high = Math.max(...ordered.map((share) => (share.committedSeconds + capacity) / share.weight));
|
|
20
|
+
for (let iteration = 0; iteration < 80; iteration += 1) {
|
|
21
|
+
const level = (low + high) / 2;
|
|
22
|
+
const amount = ordered.reduce((sum, share) => sum + Math.min(
|
|
23
|
+
share.maximumAdditionalSeconds,
|
|
24
|
+
Math.max(0, level * share.weight - share.committedSeconds)
|
|
25
|
+
), 0);
|
|
26
|
+
if (amount > capacity) high = level;
|
|
27
|
+
else low = level;
|
|
28
|
+
}
|
|
29
|
+
for (const share of ordered) result[share.id] = Math.floor(Math.min(
|
|
30
|
+
share.maximumAdditionalSeconds,
|
|
31
|
+
Math.max(0, low * share.weight - share.committedSeconds)
|
|
32
|
+
) + 1e-9);
|
|
33
|
+
let residual = Math.floor(capacity) - Object.values(result).reduce((sum, amount) => sum + amount, 0);
|
|
34
|
+
for (const share of [...ordered].sort((a, b) => (a.committedSeconds + result[a.id]) / a.weight - (b.committedSeconds + result[b.id]) / b.weight || a.id.localeCompare(b.id))) {
|
|
35
|
+
if (residual > 0 && result[share.id] + 1 <= share.maximumAdditionalSeconds) {
|
|
36
|
+
result[share.id] += 1;
|
|
37
|
+
residual -= 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
function allocateWorkdayCapacity(input) {
|
|
43
|
+
if (!Number.isFinite(Date.parse(input.now))) throw new Error("allocation_time_invalid");
|
|
44
|
+
const shares = input.workdays.map(({ plan, committedSeconds, planningCommittedSeconds, maximumAdditionalSeconds }) => {
|
|
45
|
+
nonnegative(planningCommittedSeconds);
|
|
46
|
+
if (planningCommittedSeconds > committedSeconds) throw new Error("allocation_planning_commitment_invalid");
|
|
47
|
+
const eligible = Date.parse(input.now) >= Date.parse(plan.startsAt) && (plan.state === "closing" || plan.state === "active" && workdayPhase(plan, input.now) !== "ended");
|
|
48
|
+
return {
|
|
49
|
+
id: plan.id,
|
|
50
|
+
weight: plan.policySnapshot.allocationWeight,
|
|
51
|
+
committedSeconds,
|
|
52
|
+
maximumAdditionalSeconds: eligible ? maximumAdditionalSeconds : 0
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
const opportunities = distributeAllocationSeconds(input.remainingSeconds, shares);
|
|
56
|
+
return Object.fromEntries(input.workdays.map(({ plan, committedSeconds, planningCommittedSeconds }) => {
|
|
57
|
+
const shareSeconds = opportunities[plan.id];
|
|
58
|
+
const phase = plan.state === "closing" ? "acting" : workdayPhase(plan, input.now);
|
|
59
|
+
const phaseRemainingSeconds = phase === "planning" ? Math.max(0, Math.floor((committedSeconds + shareSeconds) * plan.policySnapshot.planningPercent / 100) - planningCommittedSeconds) : shareSeconds;
|
|
60
|
+
return [plan.id, {
|
|
61
|
+
shareSeconds,
|
|
62
|
+
phase,
|
|
63
|
+
phaseRemainingSeconds,
|
|
64
|
+
weight: plan.policySnapshot.allocationWeight,
|
|
65
|
+
committedSeconds,
|
|
66
|
+
planningCommittedSeconds,
|
|
67
|
+
remainingSupplySeconds: input.remainingSeconds,
|
|
68
|
+
totalEligibleWeight: shares.filter((share) => share.maximumAdditionalSeconds > 0).reduce((sum, share) => sum + share.weight, 0),
|
|
69
|
+
availableSeconds: Math.min(shareSeconds, phaseRemainingSeconds)
|
|
70
|
+
}];
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
function calibrateAssignmentSeconds(estimate, measurements) {
|
|
74
|
+
if (!Number.isFinite(estimate.expectedSeconds) || estimate.expectedSeconds <= 0 || !Number.isFinite(estimate.maximumSeconds) || estimate.maximumSeconds < estimate.expectedSeconds) throw new Error("allocation_estimate_invalid");
|
|
75
|
+
const eligible = measurements.filter((entry) => entry.outcome === "completed" || entry.outcome === "expired").sort((a, b) => Date.parse(a.completedAt) - Date.parse(b.completedAt) || a.id.localeCompare(b.id)).slice(-20);
|
|
76
|
+
let multiplier = estimate.maximumSeconds / estimate.expectedSeconds;
|
|
77
|
+
for (const entry of eligible) {
|
|
78
|
+
if (!Number.isFinite(Date.parse(entry.completedAt)) || !Number.isFinite(entry.expectedSeconds) || entry.expectedSeconds <= 0 || !Number.isFinite(entry.allocatedSeconds) || entry.allocatedSeconds <= 0) throw new Error("allocation_measurement_invalid");
|
|
79
|
+
nonnegative(entry.activeSeconds);
|
|
80
|
+
const ratio = entry.activeSeconds / entry.expectedSeconds;
|
|
81
|
+
multiplier = entry.outcome === "expired" ? Math.max(multiplier * 1.25, entry.allocatedSeconds / entry.expectedSeconds * 1.25, ratio * 1.25) : Math.max(multiplier * 0.9, ratio * 1.25);
|
|
82
|
+
}
|
|
83
|
+
return { seconds: Math.ceil(estimate.expectedSeconds * multiplier), multiplier, measurementIds: eligible.map((entry) => entry.id) };
|
|
84
|
+
}
|
|
85
|
+
function calculateAssignmentAllocation(input) {
|
|
86
|
+
const calibration = calibrateAssignmentSeconds(input.estimate, input.measurements);
|
|
87
|
+
const minimumSeconds = Math.max(1, nonnegative(input.estimate.minimumSeconds), nonnegative(input.providerMinimumSeconds ?? 1));
|
|
88
|
+
const constraints = [...input.constraints];
|
|
89
|
+
for (const [id, value] of [
|
|
90
|
+
["provider-maximum", input.providerMaximumSeconds],
|
|
91
|
+
["profile-maximum", input.profileMaximumSeconds],
|
|
92
|
+
["planning-turn", input.planningTurnMaximumSeconds]
|
|
93
|
+
]) if (value !== void 0) constraints.push({ id, remainingSeconds: nonnegative(value) });
|
|
94
|
+
if (!constraints.length) throw new Error("allocation_constraints_required");
|
|
95
|
+
for (const entry of constraints) nonnegative(entry.remainingSeconds);
|
|
96
|
+
constraints.sort((a, b) => a.remainingSeconds - b.remainingSeconds || a.id.localeCompare(b.id));
|
|
97
|
+
const availableSeconds = Math.floor(constraints[0].remainingSeconds);
|
|
98
|
+
const desiredSeconds = input.planningTurnMaximumSeconds ?? calibration.seconds;
|
|
99
|
+
const allocatedSeconds = Math.min(Math.max(minimumSeconds, desiredSeconds), availableSeconds);
|
|
100
|
+
return {
|
|
101
|
+
admitted: availableSeconds >= minimumSeconds,
|
|
102
|
+
allocatedSeconds: availableSeconds >= minimumSeconds ? allocatedSeconds : 0,
|
|
103
|
+
minimumSeconds,
|
|
104
|
+
desiredSeconds,
|
|
105
|
+
calibration,
|
|
106
|
+
limitingConstraint: availableSeconds < Math.max(minimumSeconds, desiredSeconds) ? constraints[0].id : "task-duration",
|
|
107
|
+
constraints
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
allocateWorkdayCapacity,
|
|
112
|
+
calculateAssignmentAllocation,
|
|
113
|
+
calibrateAssignmentSeconds,
|
|
114
|
+
distributeAllocationSeconds
|
|
115
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** Provider-owned configuration, carried in the existing adapter nativeLimits. */
|
|
3
|
+
export declare const capabilityAccountingLimitsSchema: z.ZodObject<{
|
|
4
|
+
modelConfigurationId: z.ZodString;
|
|
5
|
+
dailyActiveSecondsLimit: z.ZodNumber;
|
|
6
|
+
capabilityLimits: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
7
|
+
dailyActiveSecondsLimit: z.ZodNumber;
|
|
8
|
+
minimumAssignmentSeconds: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
maximumAssignmentSeconds: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
}, "strict", z.ZodTypeAny, {
|
|
11
|
+
dailyActiveSecondsLimit: number;
|
|
12
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
13
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
dailyActiveSecondsLimit: number;
|
|
16
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
17
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
18
|
+
}>, {
|
|
19
|
+
dailyActiveSecondsLimit: number;
|
|
20
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
21
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
dailyActiveSecondsLimit: number;
|
|
24
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
25
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
26
|
+
}>>, Record<string, {
|
|
27
|
+
dailyActiveSecondsLimit: number;
|
|
28
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
29
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
30
|
+
}>, Record<string, {
|
|
31
|
+
dailyActiveSecondsLimit: number;
|
|
32
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
33
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
34
|
+
}>>;
|
|
35
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
36
|
+
modelConfigurationId: z.ZodString;
|
|
37
|
+
dailyActiveSecondsLimit: z.ZodNumber;
|
|
38
|
+
capabilityLimits: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
39
|
+
dailyActiveSecondsLimit: z.ZodNumber;
|
|
40
|
+
minimumAssignmentSeconds: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
maximumAssignmentSeconds: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
}, "strict", z.ZodTypeAny, {
|
|
43
|
+
dailyActiveSecondsLimit: number;
|
|
44
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
45
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
dailyActiveSecondsLimit: number;
|
|
48
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
49
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
50
|
+
}>, {
|
|
51
|
+
dailyActiveSecondsLimit: number;
|
|
52
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
53
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
54
|
+
}, {
|
|
55
|
+
dailyActiveSecondsLimit: number;
|
|
56
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
57
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
58
|
+
}>>, Record<string, {
|
|
59
|
+
dailyActiveSecondsLimit: number;
|
|
60
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
61
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
62
|
+
}>, Record<string, {
|
|
63
|
+
dailyActiveSecondsLimit: number;
|
|
64
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
65
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
66
|
+
}>>;
|
|
67
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
68
|
+
modelConfigurationId: z.ZodString;
|
|
69
|
+
dailyActiveSecondsLimit: z.ZodNumber;
|
|
70
|
+
capabilityLimits: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
71
|
+
dailyActiveSecondsLimit: z.ZodNumber;
|
|
72
|
+
minimumAssignmentSeconds: z.ZodOptional<z.ZodNumber>;
|
|
73
|
+
maximumAssignmentSeconds: z.ZodOptional<z.ZodNumber>;
|
|
74
|
+
}, "strict", z.ZodTypeAny, {
|
|
75
|
+
dailyActiveSecondsLimit: number;
|
|
76
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
77
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
78
|
+
}, {
|
|
79
|
+
dailyActiveSecondsLimit: number;
|
|
80
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
81
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
82
|
+
}>, {
|
|
83
|
+
dailyActiveSecondsLimit: number;
|
|
84
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
85
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
86
|
+
}, {
|
|
87
|
+
dailyActiveSecondsLimit: number;
|
|
88
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
89
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
90
|
+
}>>, Record<string, {
|
|
91
|
+
dailyActiveSecondsLimit: number;
|
|
92
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
93
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
94
|
+
}>, Record<string, {
|
|
95
|
+
dailyActiveSecondsLimit: number;
|
|
96
|
+
minimumAssignmentSeconds?: number | undefined;
|
|
97
|
+
maximumAssignmentSeconds?: number | undefined;
|
|
98
|
+
}>>;
|
|
99
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
100
|
+
export type CapabilityAccountingLimits = z.infer<typeof capabilityAccountingLimitsSchema>;
|
|
101
|
+
export interface CapabilityAccountingObservation {
|
|
102
|
+
day: string;
|
|
103
|
+
observedAt: string;
|
|
104
|
+
healthy: boolean;
|
|
105
|
+
activeSeconds: number;
|
|
106
|
+
reservedSeconds: number;
|
|
107
|
+
}
|
|
108
|
+
/** Totals must cover the same accounting scope: provider/model or capability. */
|
|
109
|
+
export declare function remainingCapabilitySeconds(input: {
|
|
110
|
+
now: string;
|
|
111
|
+
maximumObservationAgeSeconds: number;
|
|
112
|
+
dailyLimitSeconds: number;
|
|
113
|
+
observation: CapabilityAccountingObservation;
|
|
114
|
+
previousObservation?: CapabilityAccountingObservation;
|
|
115
|
+
ledgerActiveSeconds: number;
|
|
116
|
+
/** Unconsumed portions of outstanding reservations; active portions are already usage. */
|
|
117
|
+
ledgerReservedSeconds: number;
|
|
118
|
+
}): {
|
|
119
|
+
availableSeconds: number;
|
|
120
|
+
reason: "non-monotonic";
|
|
121
|
+
activeSeconds?: undefined;
|
|
122
|
+
reservedSeconds?: undefined;
|
|
123
|
+
day?: undefined;
|
|
124
|
+
} | {
|
|
125
|
+
availableSeconds: number;
|
|
126
|
+
reason: "unhealthy";
|
|
127
|
+
activeSeconds?: undefined;
|
|
128
|
+
reservedSeconds?: undefined;
|
|
129
|
+
day?: undefined;
|
|
130
|
+
} | {
|
|
131
|
+
availableSeconds: number;
|
|
132
|
+
reason: "stale";
|
|
133
|
+
activeSeconds?: undefined;
|
|
134
|
+
reservedSeconds?: undefined;
|
|
135
|
+
day?: undefined;
|
|
136
|
+
} | {
|
|
137
|
+
availableSeconds: number;
|
|
138
|
+
reason: "accounted";
|
|
139
|
+
activeSeconds: number;
|
|
140
|
+
reservedSeconds: number;
|
|
141
|
+
day: string;
|
|
142
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const capabilityAccountingLimitsSchema = z.object({
|
|
3
|
+
modelConfigurationId: z.string().trim().min(1),
|
|
4
|
+
dailyActiveSecondsLimit: z.number().finite().nonnegative(),
|
|
5
|
+
capabilityLimits: z.record(z.object({
|
|
6
|
+
dailyActiveSecondsLimit: z.number().finite().nonnegative(),
|
|
7
|
+
minimumAssignmentSeconds: z.number().int().positive().optional(),
|
|
8
|
+
maximumAssignmentSeconds: z.number().int().positive().optional()
|
|
9
|
+
}).strict().superRefine((value, context) => {
|
|
10
|
+
if (value.minimumAssignmentSeconds !== void 0 && value.maximumAssignmentSeconds !== void 0 && value.minimumAssignmentSeconds > value.maximumAssignmentSeconds) context.addIssue({
|
|
11
|
+
code: z.ZodIssueCode.custom,
|
|
12
|
+
path: ["maximumAssignmentSeconds"],
|
|
13
|
+
message: "Maximum must not be below minimum."
|
|
14
|
+
});
|
|
15
|
+
})).refine((value) => Object.keys(value).length > 0, "At least one execution capability limit is required.")
|
|
16
|
+
}).passthrough();
|
|
17
|
+
function remainingCapabilitySeconds(input) {
|
|
18
|
+
const now = Date.parse(input.now), observed = Date.parse(input.observation.observedAt);
|
|
19
|
+
const numbers = [
|
|
20
|
+
input.maximumObservationAgeSeconds,
|
|
21
|
+
input.dailyLimitSeconds,
|
|
22
|
+
input.observation.activeSeconds,
|
|
23
|
+
input.observation.reservedSeconds,
|
|
24
|
+
input.ledgerActiveSeconds,
|
|
25
|
+
input.ledgerReservedSeconds
|
|
26
|
+
];
|
|
27
|
+
if (!numbers.every((value) => Number.isFinite(value) && value >= 0) || !Number.isFinite(now) || !Number.isFinite(observed)) throw new Error("capability_accounting_invalid");
|
|
28
|
+
const day = new Date(now).toISOString().slice(0, 10);
|
|
29
|
+
const previous = input.previousObservation;
|
|
30
|
+
if (previous && (!Number.isFinite(Date.parse(previous.observedAt)) || !Number.isFinite(previous.activeSeconds) || previous.activeSeconds < 0)) throw new Error("capability_accounting_invalid");
|
|
31
|
+
if (previous && (observed < Date.parse(previous.observedAt) || previous.day === day && input.observation.activeSeconds < previous.activeSeconds)) {
|
|
32
|
+
return { availableSeconds: 0, reason: "non-monotonic" };
|
|
33
|
+
}
|
|
34
|
+
if (!input.observation.healthy) return { availableSeconds: 0, reason: "unhealthy" };
|
|
35
|
+
if (input.observation.day !== day || new Date(observed).toISOString().slice(0, 10) !== day || observed > now || now - observed > input.maximumObservationAgeSeconds * 1e3) {
|
|
36
|
+
return { availableSeconds: 0, reason: "stale" };
|
|
37
|
+
}
|
|
38
|
+
const activeSeconds = Math.max(input.observation.activeSeconds, input.ledgerActiveSeconds);
|
|
39
|
+
const reservedSeconds = Math.max(input.observation.reservedSeconds, input.ledgerReservedSeconds);
|
|
40
|
+
return {
|
|
41
|
+
availableSeconds: Math.max(0, Math.floor(input.dailyLimitSeconds - activeSeconds - reservedSeconds)),
|
|
42
|
+
reason: "accounted",
|
|
43
|
+
activeSeconds,
|
|
44
|
+
reservedSeconds,
|
|
45
|
+
day
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
capabilityAccountingLimitsSchema,
|
|
50
|
+
remainingCapabilitySeconds
|
|
51
|
+
};
|