@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.
Files changed (53) hide show
  1. package/dist/.treeseed-build-complete.json +1 -1
  2. package/dist/agent-capacity/allocation.d.ts +0 -3
  3. package/dist/agent-capacity/allocation.js +0 -221
  4. package/dist/agent-capacity/contracts/capacity/assignments/agent-execution.d.ts +280 -84
  5. package/dist/agent-capacity/contracts/capacity/assignments/agent-execution.js +22 -1
  6. package/dist/agent-capacity/contracts/capacity/assignments/assignment-records.d.ts +5 -0
  7. package/dist/agent-capacity/contracts/capacity/workdays/assignment-allocation.d.ts +82 -0
  8. package/dist/agent-capacity/contracts/capacity/workdays/assignment-allocation.js +115 -0
  9. package/dist/agent-capacity/contracts/capacity/workdays/capability-accounting.d.ts +142 -0
  10. package/dist/agent-capacity/contracts/capacity/workdays/capability-accounting.js +51 -0
  11. package/dist/agent-capacity/contracts/capacity/workdays/workday-allocation.d.ts +167 -35
  12. package/dist/agent-capacity/contracts/capacity/workdays/workday-allocation.js +59 -20
  13. package/dist/agent-capacity/contracts/capacity/workdays/workday-records.d.ts +2 -21
  14. package/dist/agent-capacity/contracts/support/execution-graph.d.ts +18 -18
  15. package/dist/agent-capacity/contracts/support/time-capacity.d.ts +0 -14
  16. package/dist/agent-capacity/contracts/support/time-capacity.js +1 -8
  17. package/dist/agent-capacity/validation/agent-definition-schema.d.ts +338 -338
  18. package/dist/capacity/agents/agent-capacity.d.ts +2 -0
  19. package/dist/capacity/agents/agent-capacity.js +2 -0
  20. package/dist/capacity-provider/client.d.ts +5 -3
  21. package/dist/capacity-provider/contracts/capacity-facts.d.ts +14 -0
  22. package/dist/capacity-provider/contracts/governance.d.ts +6 -0
  23. package/dist/capacity-provider/sandbox-contracts.d.ts +55 -1
  24. package/dist/capacity-provider/sandbox-contracts.js +4 -0
  25. package/dist/capacity-provider/source-workspace.d.ts +69 -37
  26. package/dist/capacity-provider/source-workspace.js +36 -5
  27. package/dist/content/validation/auxiliary-content-schemas.d.ts +10 -10
  28. package/dist/content/validation/content-model-schemas.d.ts +613 -613
  29. package/dist/content/validation/content-model-schemas.js +14 -2
  30. package/dist/content/validation/portable-content-data.d.ts +22 -22
  31. package/dist/deployment/postgres/requirements.d.ts +3 -2
  32. package/dist/deployment/postgres/requirements.js +3 -1
  33. package/dist/operator-contracts/canonical-command-tree.js +20 -6
  34. package/dist/operator-contracts/catalog/communication-operations.d.ts +2 -0
  35. package/dist/operator-contracts/catalog/workdays/profile-commands.js +4 -4
  36. package/dist/operator-contracts/catalog/workdays/profile-operations.d.ts +28 -3
  37. package/dist/operator-contracts/catalog/workdays/profile-operations.js +10 -8
  38. package/dist/operator-contracts/catalog/workdays/selection-commands.js +13 -0
  39. package/dist/operator-contracts/command-tree.d.ts +1 -1
  40. package/dist/operator-contracts/communication/contracts.d.ts +6 -0
  41. package/dist/operator-contracts/communication/contracts.js +2 -0
  42. package/dist/operator-contracts/control-plane-operations.d.ts +34 -3
  43. package/dist/operator-contracts/control-plane-operations.js +1 -0
  44. package/dist/operator-contracts/index.d.ts +0 -1
  45. package/dist/operator-contracts/index.js +0 -1
  46. package/dist/operator-contracts/workday-lifecycle.d.ts +8 -20
  47. package/dist/operator-contracts/workday-lifecycle.js +15 -5
  48. package/dist/secrets-capability/shared-resources.d.ts +6 -6
  49. package/package.json +1 -1
  50. package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.d.ts +0 -29
  51. package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.js +0 -110
  52. package/dist/operator-contracts/workday-profile.d.ts +0 -72
  53. 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({ providerId: identifier, offerId: identifier, offerRevision: z.number().int().positive(), runtimeBuild: digest }).strict();
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
+ };