@treeseed/sdk 0.8.7 → 0.8.9
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/capacity.d.ts +120 -1
- package/dist/capacity.js +371 -2
- package/dist/control-plane-client.d.ts +1 -1
- package/dist/d1-store.d.ts +5 -5
- package/dist/d1-store.js +24 -24
- package/dist/db/d1.d.ts +102 -0
- package/dist/db/node-sqlite.d.ts +102 -0
- package/dist/db/schema.d.ts +204 -0
- package/dist/db/schema.js +9 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +33 -23
- package/dist/market-client.d.ts +32 -0
- package/dist/market-client.js +48 -0
- package/dist/operations/services/config-runtime.js +57 -20
- package/dist/operations/services/d1-migration.js +10 -2
- package/dist/operations/services/github-automation.js +1 -1
- package/dist/operations/services/hosting-audit.d.ts +2 -1
- package/dist/operations/services/hosting-audit.js +18 -9
- package/dist/operations/services/hub-provider-launch.d.ts +3 -3
- package/dist/operations/services/hub-provider-launch.js +16 -16
- package/dist/operations/services/{knowledge-coop-packaging.d.ts → market-packaging.d.ts} +13 -13
- package/dist/operations/services/{knowledge-coop-packaging.js → market-packaging.js} +10 -10
- package/dist/operations/services/project-platform.d.ts +32 -0
- package/dist/operations/services/project-platform.js +91 -1
- package/dist/platform/contracts.d.ts +53 -31
- package/dist/platform/utils/site-config-schema.js +120 -52
- package/dist/{knowledge-coop.d.ts → project-workflow.d.ts} +15 -15
- package/dist/{knowledge-coop.js → project-workflow.js} +15 -15
- package/dist/reconcile/builtin-adapters.js +95 -11
- package/dist/scripts/tenant-d1-migrate-local.js +1 -0
- package/dist/sdk-types.d.ts +5 -4
- package/dist/sdk.d.ts +1 -1
- package/dist/stores/{knowledge-coop-store.d.ts → project-workflow-store.d.ts} +3 -3
- package/dist/stores/{knowledge-coop-store.js → project-workflow-store.js} +4 -4
- package/dist/workflow/operations.js +6 -6
- package/dist/workflow-state.js +2 -2
- package/package.json +1 -1
package/dist/capacity.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CapacityEstimateConfidence, CapacityGrant, CapacityPlan, CapacityProviderLane, CapacityReservation, TaskEstimateProfile } from './sdk-types.ts';
|
|
1
|
+
import type { CapacityEstimateConfidence, CapacityGrant, CapacityPlan, CapacityProvider, CapacityProviderLane, CapacityReservation, CreateCapacityReservationRequest, CreateCapacityRoutingDecisionRequest, RecordCapacityUsageRequest, TaskEstimateProfile } from './sdk-types.ts';
|
|
2
2
|
import type { AgentProviderProfile } from './types/agents.ts';
|
|
3
3
|
export type ProcessingEnvironment = 'local' | 'staging' | 'prod';
|
|
4
4
|
export interface CapacityProviderRegistration {
|
|
@@ -62,6 +62,111 @@ export interface CapacityLaneScore {
|
|
|
62
62
|
costPenalty: number;
|
|
63
63
|
reasons: string[];
|
|
64
64
|
}
|
|
65
|
+
export interface CapacityTaskEstimate {
|
|
66
|
+
taskSignature: string;
|
|
67
|
+
confidence: CapacityEstimateConfidence;
|
|
68
|
+
estimatedCreditsP50: number;
|
|
69
|
+
estimatedCreditsP90: number;
|
|
70
|
+
reservedCredits: number;
|
|
71
|
+
}
|
|
72
|
+
export interface TeamCapacitySummary {
|
|
73
|
+
teamId: string;
|
|
74
|
+
monthlyCredits: number | null;
|
|
75
|
+
monthlyUsedCredits: number;
|
|
76
|
+
monthlyRemainingCredits: number | null;
|
|
77
|
+
dailyCredits: number | null;
|
|
78
|
+
dailyUsedCredits: number;
|
|
79
|
+
dailyReservedCredits: number;
|
|
80
|
+
dailyRemainingCredits: number | null;
|
|
81
|
+
providerCount: number;
|
|
82
|
+
activeProviderCount: number;
|
|
83
|
+
degradedProviderCount: number;
|
|
84
|
+
grantCount: number;
|
|
85
|
+
blockedTaskCount: number;
|
|
86
|
+
approvalRequiredCount: number;
|
|
87
|
+
}
|
|
88
|
+
export interface ProjectCapacitySummary extends TeamCapacitySummary {
|
|
89
|
+
projectId: string;
|
|
90
|
+
environment: ProcessingEnvironment;
|
|
91
|
+
readiness: 'ready' | 'waiting_for_budget' | 'waiting_for_provider' | 'paused_by_policy' | 'needs_approval';
|
|
92
|
+
reasons: string[];
|
|
93
|
+
}
|
|
94
|
+
export interface RouteAndReserveInput {
|
|
95
|
+
plan: CapacityPlan;
|
|
96
|
+
estimate: CapacityTaskEstimate;
|
|
97
|
+
taskId?: string | null;
|
|
98
|
+
workDayId?: string | null;
|
|
99
|
+
taskKind?: string | null;
|
|
100
|
+
requiredCapabilities?: string[];
|
|
101
|
+
modelClass?: string | null;
|
|
102
|
+
priorityClass?: string | null;
|
|
103
|
+
allowDegradedProviders?: boolean;
|
|
104
|
+
repositoryMutation?: boolean;
|
|
105
|
+
production?: boolean;
|
|
106
|
+
selectedModel?: string | null;
|
|
107
|
+
source?: string;
|
|
108
|
+
metadata?: Record<string, unknown>;
|
|
109
|
+
}
|
|
110
|
+
export type RouteAndReserveBlockCode = 'no_capacity_provider' | 'no_capacity_grant' | 'no_eligible_lane' | 'insufficient_budget' | 'approval_required';
|
|
111
|
+
export interface RouteAndReserveCandidate {
|
|
112
|
+
providerId: string;
|
|
113
|
+
laneId: string;
|
|
114
|
+
grantId: string;
|
|
115
|
+
remainingCredits: number | null;
|
|
116
|
+
score: CapacityLaneScore;
|
|
117
|
+
eligible: boolean;
|
|
118
|
+
reasons: string[];
|
|
119
|
+
}
|
|
120
|
+
export type RouteAndReserveResult = {
|
|
121
|
+
ok: true;
|
|
122
|
+
provider: CapacityProvider;
|
|
123
|
+
lane: CapacityProviderLane;
|
|
124
|
+
grant: CapacityGrant;
|
|
125
|
+
estimate: CapacityTaskEstimate;
|
|
126
|
+
remainingCreditsBefore: number | null;
|
|
127
|
+
reservation: CreateCapacityReservationRequest;
|
|
128
|
+
routingDecision: CreateCapacityRoutingDecisionRequest;
|
|
129
|
+
ledgerEntry: RecordCapacityUsageRequest;
|
|
130
|
+
capacityMetadata: {
|
|
131
|
+
providerId: string;
|
|
132
|
+
laneId: string;
|
|
133
|
+
grantId: string;
|
|
134
|
+
reservationId: string | null;
|
|
135
|
+
routingDecisionId: string | null;
|
|
136
|
+
estimatedCreditsP50: number;
|
|
137
|
+
estimatedCreditsP90: number;
|
|
138
|
+
reservedCredits: number;
|
|
139
|
+
};
|
|
140
|
+
candidates: RouteAndReserveCandidate[];
|
|
141
|
+
} | {
|
|
142
|
+
ok: false;
|
|
143
|
+
code: RouteAndReserveBlockCode;
|
|
144
|
+
reason: string;
|
|
145
|
+
estimate: CapacityTaskEstimate;
|
|
146
|
+
candidates: RouteAndReserveCandidate[];
|
|
147
|
+
};
|
|
148
|
+
export interface CapacitySettlementInput {
|
|
149
|
+
reservation: CapacityReservation;
|
|
150
|
+
actualCredits: number;
|
|
151
|
+
actualProviderUnits?: number | null;
|
|
152
|
+
actualUsd?: number | null;
|
|
153
|
+
teamId?: string | null;
|
|
154
|
+
projectId?: string | null;
|
|
155
|
+
workDayId?: string | null;
|
|
156
|
+
taskId?: string | null;
|
|
157
|
+
source?: string;
|
|
158
|
+
metadata?: Record<string, unknown>;
|
|
159
|
+
}
|
|
160
|
+
export interface CapacitySettlement {
|
|
161
|
+
reservationId: string;
|
|
162
|
+
state: 'consumed' | 'overran_pending_approval';
|
|
163
|
+
consumeEntry: RecordCapacityUsageRequest;
|
|
164
|
+
releaseEntry: RecordCapacityUsageRequest | null;
|
|
165
|
+
overrunEntry: RecordCapacityUsageRequest | null;
|
|
166
|
+
consumedCredits: number;
|
|
167
|
+
releasedCredits: number;
|
|
168
|
+
overrunCredits: number;
|
|
169
|
+
}
|
|
65
170
|
export declare function reserveCreditsForEstimate(input: CapacityEstimateInput): {
|
|
66
171
|
taskSignature: string;
|
|
67
172
|
confidence: CapacityEstimateConfidence;
|
|
@@ -78,9 +183,23 @@ export declare function summarizeCapacityPlan(plan: CapacityPlan): {
|
|
|
78
183
|
laneCount: number;
|
|
79
184
|
grantCount: number;
|
|
80
185
|
};
|
|
186
|
+
export declare function summarizeTeamCapacityPlan(plan: CapacityPlan): TeamCapacitySummary;
|
|
187
|
+
export declare function summarizeProjectCapacityPlan(plan: CapacityPlan, options?: {
|
|
188
|
+
workPolicyEnabled?: boolean | null;
|
|
189
|
+
approvalRequiredCount?: number;
|
|
190
|
+
blockedTaskCount?: number;
|
|
191
|
+
}): ProjectCapacitySummary;
|
|
81
192
|
export declare function scoreCapacityLane(input: CapacityLaneCandidate): CapacityLaneScore;
|
|
82
193
|
export declare function selectBestCapacityLane(candidates: CapacityLaneCandidate[]): {
|
|
83
194
|
selected: CapacityLaneScore;
|
|
84
195
|
scores: CapacityLaneScore[];
|
|
85
196
|
};
|
|
86
197
|
export declare function reservationHasCapacity(reservation: CapacityReservation): boolean;
|
|
198
|
+
export declare function createReservationReleaseEntry(input: {
|
|
199
|
+
reservation: CapacityReservation;
|
|
200
|
+
credits?: number | null;
|
|
201
|
+
source?: string;
|
|
202
|
+
metadata?: Record<string, unknown>;
|
|
203
|
+
}): RecordCapacityUsageRequest;
|
|
204
|
+
export declare function settleCapacityActuals(input: CapacitySettlementInput): CapacitySettlement;
|
|
205
|
+
export declare function routeAndReserveCapacity(input: RouteAndReserveInput): RouteAndReserveResult;
|
package/dist/capacity.js
CHANGED
|
@@ -6,6 +6,97 @@ function scarcityPenalty(level) {
|
|
|
6
6
|
if (level === "medium") return 15;
|
|
7
7
|
return 0;
|
|
8
8
|
}
|
|
9
|
+
function metadataStatus(value) {
|
|
10
|
+
const status = value?.status;
|
|
11
|
+
return typeof status === "string" ? status : null;
|
|
12
|
+
}
|
|
13
|
+
function stringArray(value) {
|
|
14
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
15
|
+
}
|
|
16
|
+
function booleanValue(value) {
|
|
17
|
+
return typeof value === "boolean" ? value : null;
|
|
18
|
+
}
|
|
19
|
+
function numberValue(value) {
|
|
20
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
21
|
+
}
|
|
22
|
+
function reservationDebit(reservation) {
|
|
23
|
+
if (reservation.state === "released" || reservation.state === "expired" || reservation.state === "cancelled") {
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
if (reservation.state === "consumed" || reservation.state === "failed") {
|
|
27
|
+
return Math.max(0, reservation.consumedCredits);
|
|
28
|
+
}
|
|
29
|
+
return Math.max(reservation.reservedCredits, reservation.consumedCredits, 0);
|
|
30
|
+
}
|
|
31
|
+
function activeReservationDebit(reservation) {
|
|
32
|
+
if (reservation.state === "reserved" || reservation.state === "consuming") {
|
|
33
|
+
return Math.max(reservation.reservedCredits, reservation.consumedCredits, 0);
|
|
34
|
+
}
|
|
35
|
+
if (reservation.state === "consumed" || reservation.state === "failed") {
|
|
36
|
+
return Math.max(reservation.consumedCredits, 0);
|
|
37
|
+
}
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
function grantMatchesReservation(grant, reservation) {
|
|
41
|
+
if (grant.teamId !== reservation.teamId) return false;
|
|
42
|
+
if (grant.capacityProviderId !== reservation.capacityProviderId) return false;
|
|
43
|
+
if (grant.laneId && grant.laneId !== reservation.laneId) return false;
|
|
44
|
+
if (grant.projectId && grant.projectId !== reservation.projectId) return false;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function grantRemainingCredits(plan, grant) {
|
|
48
|
+
const limit = grant.dailyCreditLimit ?? grant.monthlyCreditLimit;
|
|
49
|
+
if (limit === null || limit === void 0) return null;
|
|
50
|
+
const debits = plan.activeReservations.filter((reservation) => grantMatchesReservation(grant, reservation)).reduce((total, reservation) => total + reservationDebit(reservation), 0);
|
|
51
|
+
return Math.max(0, Number(limit) - debits);
|
|
52
|
+
}
|
|
53
|
+
function providerIsEligible(provider, input) {
|
|
54
|
+
if (provider.status === "active") return true;
|
|
55
|
+
if (provider.status === "degraded" && input.allowDegradedProviders) return true;
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
function grantIsEligible(grant, input) {
|
|
59
|
+
if (grant.state !== "active") return false;
|
|
60
|
+
if (grant.teamId !== input.plan.teamId) return false;
|
|
61
|
+
if (grant.environment && grant.environment !== input.plan.environment) return false;
|
|
62
|
+
if (grant.projectId && grant.projectId !== input.plan.projectId) return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function lanePolicyReasons(lane, input) {
|
|
66
|
+
const reasons = [];
|
|
67
|
+
const laneStatus = metadataStatus(lane.metadata);
|
|
68
|
+
if (laneStatus && laneStatus !== "active") reasons.push(`lane_status:${laneStatus}`);
|
|
69
|
+
const policy = lane.routingPolicy ?? {};
|
|
70
|
+
const taskKinds = stringArray(policy.taskKinds);
|
|
71
|
+
const taskKind = input.taskKind ?? input.estimate.taskSignature;
|
|
72
|
+
if (taskKinds.length > 0 && !taskKinds.includes(taskKind)) reasons.push("task_kind_mismatch");
|
|
73
|
+
const requiredCapabilities = stringArray(policy.requiredCapabilities);
|
|
74
|
+
const missingCapabilities = (input.requiredCapabilities ?? []).filter((capability) => !requiredCapabilities.includes(capability));
|
|
75
|
+
if (requiredCapabilities.length > 0 && missingCapabilities.length > 0) {
|
|
76
|
+
reasons.push("capability_mismatch");
|
|
77
|
+
}
|
|
78
|
+
const allowedEnvironments = stringArray(policy.allowedEnvironments);
|
|
79
|
+
if (allowedEnvironments.length > 0 && !allowedEnvironments.includes(input.plan.environment)) {
|
|
80
|
+
reasons.push("environment_mismatch");
|
|
81
|
+
}
|
|
82
|
+
const maxCreditsPerTask = numberValue(policy.maxCreditsPerTask);
|
|
83
|
+
if (maxCreditsPerTask !== null && input.estimate.reservedCredits > maxCreditsPerTask) {
|
|
84
|
+
reasons.push("task_credit_limit_exceeded");
|
|
85
|
+
}
|
|
86
|
+
const approvalThreshold = numberValue(policy.requiresApprovalAboveCredits);
|
|
87
|
+
if (approvalThreshold !== null && input.estimate.reservedCredits > approvalThreshold) {
|
|
88
|
+
reasons.push("approval_required");
|
|
89
|
+
}
|
|
90
|
+
const repositoryMutationAllowed = booleanValue(policy.repositoryMutationAllowed);
|
|
91
|
+
if (input.repositoryMutation && repositoryMutationAllowed === false) {
|
|
92
|
+
reasons.push("repository_mutation_not_allowed");
|
|
93
|
+
}
|
|
94
|
+
const productionAllowed = booleanValue(policy.productionAllowed);
|
|
95
|
+
if (input.production && productionAllowed === false) {
|
|
96
|
+
reasons.push("production_not_allowed");
|
|
97
|
+
}
|
|
98
|
+
return reasons;
|
|
99
|
+
}
|
|
9
100
|
function reserveCreditsForEstimate(input) {
|
|
10
101
|
const profileP50 = finiteNumber(input.profile?.creditsP50);
|
|
11
102
|
const profileP90 = finiteNumber(input.profile?.creditsP90);
|
|
@@ -26,7 +117,7 @@ function reserveCreditsForEstimate(input) {
|
|
|
26
117
|
};
|
|
27
118
|
}
|
|
28
119
|
function summarizeCapacityPlan(plan) {
|
|
29
|
-
const reservedCredits = plan.activeReservations.filter((reservation) => reservation.state === "reserved").reduce((total, reservation) => total + reservation.reservedCredits, 0);
|
|
120
|
+
const reservedCredits = plan.activeReservations.filter((reservation) => reservation.state === "reserved" || reservation.state === "consuming").reduce((total, reservation) => total + reservation.reservedCredits, 0);
|
|
30
121
|
const consumedCredits = plan.activeReservations.reduce((total, reservation) => total + reservation.consumedCredits, 0);
|
|
31
122
|
const grantedDailyCredits = plan.grants.filter((grant) => grant.state === "active").reduce((total, grant) => total + (grant.dailyCreditLimit ?? 0), 0);
|
|
32
123
|
return {
|
|
@@ -39,6 +130,55 @@ function summarizeCapacityPlan(plan) {
|
|
|
39
130
|
grantCount: plan.grants.length
|
|
40
131
|
};
|
|
41
132
|
}
|
|
133
|
+
function summarizeTeamCapacityPlan(plan) {
|
|
134
|
+
const dailyCredits = plan.grants.filter((grant) => grant.state === "active").reduce((total, grant) => total + (grant.dailyCreditLimit ?? 0), 0);
|
|
135
|
+
const monthlyCredits = plan.grants.filter((grant) => grant.state === "active").reduce((total, grant) => total + (grant.monthlyCreditLimit ?? 0), 0);
|
|
136
|
+
const dailyReservedCredits = plan.activeReservations.reduce((total, reservation) => total + activeReservationDebit(reservation), 0);
|
|
137
|
+
const dailyUsedCredits = plan.activeReservations.reduce((total, reservation) => total + Math.max(0, reservation.consumedCredits), 0);
|
|
138
|
+
return {
|
|
139
|
+
teamId: plan.teamId,
|
|
140
|
+
monthlyCredits: monthlyCredits > 0 ? monthlyCredits : null,
|
|
141
|
+
monthlyUsedCredits: dailyUsedCredits,
|
|
142
|
+
monthlyRemainingCredits: monthlyCredits > 0 ? Math.max(0, monthlyCredits - dailyUsedCredits) : null,
|
|
143
|
+
dailyCredits: dailyCredits > 0 ? dailyCredits : null,
|
|
144
|
+
dailyUsedCredits,
|
|
145
|
+
dailyReservedCredits,
|
|
146
|
+
dailyRemainingCredits: dailyCredits > 0 ? Math.max(0, dailyCredits - dailyReservedCredits - dailyUsedCredits) : null,
|
|
147
|
+
providerCount: plan.providers.length,
|
|
148
|
+
activeProviderCount: plan.providers.filter((provider) => provider.status === "active").length,
|
|
149
|
+
degradedProviderCount: plan.providers.filter((provider) => provider.status === "degraded").length,
|
|
150
|
+
grantCount: plan.grants.length,
|
|
151
|
+
blockedTaskCount: 0,
|
|
152
|
+
approvalRequiredCount: 0
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function summarizeProjectCapacityPlan(plan, options = {}) {
|
|
156
|
+
const summary = summarizeTeamCapacityPlan(plan);
|
|
157
|
+
const reasons = [];
|
|
158
|
+
let readiness = "ready";
|
|
159
|
+
if (options.workPolicyEnabled === false) {
|
|
160
|
+
readiness = "paused_by_policy";
|
|
161
|
+
reasons.push("work_policy_disabled");
|
|
162
|
+
} else if (summary.activeProviderCount <= 0) {
|
|
163
|
+
readiness = "waiting_for_provider";
|
|
164
|
+
reasons.push("no_active_provider");
|
|
165
|
+
} else if (summary.dailyRemainingCredits !== null && summary.dailyRemainingCredits <= 0) {
|
|
166
|
+
readiness = "waiting_for_budget";
|
|
167
|
+
reasons.push("daily_budget_exhausted");
|
|
168
|
+
} else if ((options.approvalRequiredCount ?? 0) > 0) {
|
|
169
|
+
readiness = "needs_approval";
|
|
170
|
+
reasons.push("approval_required");
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
...summary,
|
|
174
|
+
projectId: plan.projectId,
|
|
175
|
+
environment: plan.environment,
|
|
176
|
+
readiness,
|
|
177
|
+
reasons,
|
|
178
|
+
blockedTaskCount: options.blockedTaskCount ?? summary.blockedTaskCount,
|
|
179
|
+
approvalRequiredCount: options.approvalRequiredCount ?? summary.approvalRequiredCount
|
|
180
|
+
};
|
|
181
|
+
}
|
|
42
182
|
function scoreCapacityLane(input) {
|
|
43
183
|
const reasons = [];
|
|
44
184
|
let agentFit = 0;
|
|
@@ -91,10 +231,239 @@ function selectBestCapacityLane(candidates) {
|
|
|
91
231
|
function reservationHasCapacity(reservation) {
|
|
92
232
|
return reservation.state === "reserved" && reservation.reservedCredits > reservation.consumedCredits;
|
|
93
233
|
}
|
|
234
|
+
function createReservationReleaseEntry(input) {
|
|
235
|
+
const credits = Math.max(0, Number(input.credits ?? input.reservation.reservedCredits - input.reservation.consumedCredits));
|
|
236
|
+
return {
|
|
237
|
+
capacityProviderId: input.reservation.capacityProviderId,
|
|
238
|
+
laneId: input.reservation.laneId,
|
|
239
|
+
reservationId: input.reservation.id,
|
|
240
|
+
teamId: input.reservation.teamId,
|
|
241
|
+
projectId: input.reservation.projectId,
|
|
242
|
+
workDayId: input.reservation.workDayId,
|
|
243
|
+
taskId: input.reservation.taskId,
|
|
244
|
+
phase: "reservation_released",
|
|
245
|
+
credits: -credits,
|
|
246
|
+
source: input.source ?? "capacity_coordinator",
|
|
247
|
+
metadata: input.metadata ?? {}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function settleCapacityActuals(input) {
|
|
251
|
+
const consumedCredits = Math.max(0, Number(input.actualCredits ?? 0));
|
|
252
|
+
const releasedCredits = Math.max(0, input.reservation.reservedCredits - consumedCredits);
|
|
253
|
+
const overrunCredits = Math.max(0, consumedCredits - input.reservation.reservedCredits);
|
|
254
|
+
const base = {
|
|
255
|
+
capacityProviderId: input.reservation.capacityProviderId,
|
|
256
|
+
laneId: input.reservation.laneId,
|
|
257
|
+
reservationId: input.reservation.id,
|
|
258
|
+
teamId: input.teamId ?? input.reservation.teamId,
|
|
259
|
+
projectId: input.projectId ?? input.reservation.projectId,
|
|
260
|
+
workDayId: input.workDayId ?? input.reservation.workDayId,
|
|
261
|
+
taskId: input.taskId ?? input.reservation.taskId,
|
|
262
|
+
source: input.source ?? "capacity_coordinator",
|
|
263
|
+
metadata: input.metadata ?? {}
|
|
264
|
+
};
|
|
265
|
+
const consumeEntry = {
|
|
266
|
+
...base,
|
|
267
|
+
phase: "task_completed_actual_settlement",
|
|
268
|
+
credits: consumedCredits,
|
|
269
|
+
providerUnits: input.actualProviderUnits ?? null,
|
|
270
|
+
usd: input.actualUsd ?? null
|
|
271
|
+
};
|
|
272
|
+
const releaseEntry = releasedCredits > 0 ? {
|
|
273
|
+
...base,
|
|
274
|
+
phase: "reservation_released",
|
|
275
|
+
credits: -releasedCredits
|
|
276
|
+
} : null;
|
|
277
|
+
const overrunEntry = overrunCredits > 0 ? {
|
|
278
|
+
...base,
|
|
279
|
+
phase: "overrun_hold",
|
|
280
|
+
credits: overrunCredits
|
|
281
|
+
} : null;
|
|
282
|
+
return {
|
|
283
|
+
reservationId: input.reservation.id,
|
|
284
|
+
state: overrunCredits > 0 ? "overran_pending_approval" : "consumed",
|
|
285
|
+
consumeEntry,
|
|
286
|
+
releaseEntry,
|
|
287
|
+
overrunEntry,
|
|
288
|
+
consumedCredits,
|
|
289
|
+
releasedCredits,
|
|
290
|
+
overrunCredits
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function routeAndReserveCapacity(input) {
|
|
294
|
+
const providers = input.plan.providers.filter((provider2) => providerIsEligible(provider2, input));
|
|
295
|
+
const grants = input.plan.grants.filter((grant2) => grantIsEligible(grant2, input));
|
|
296
|
+
const candidates = [];
|
|
297
|
+
for (const grant2 of grants) {
|
|
298
|
+
const provider2 = providers.find((candidate) => candidate.id === grant2.capacityProviderId);
|
|
299
|
+
if (!provider2) continue;
|
|
300
|
+
const lanes = input.plan.lanes.filter(
|
|
301
|
+
(lane2) => lane2.capacityProviderId === provider2.id && (!grant2.laneId || grant2.laneId === lane2.id)
|
|
302
|
+
);
|
|
303
|
+
for (const lane2 of lanes) {
|
|
304
|
+
const reasons = lanePolicyReasons(lane2, input);
|
|
305
|
+
const remainingCredits = grantRemainingCredits(input.plan, grant2);
|
|
306
|
+
if (remainingCredits !== null && remainingCredits < input.estimate.reservedCredits && (grant2.overflowPolicy === "deny" || grant2.overflowPolicy === "hard_grant")) {
|
|
307
|
+
reasons.push("insufficient_budget");
|
|
308
|
+
}
|
|
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
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (input.plan.providers.length === 0 || providers.length === 0) {
|
|
332
|
+
return {
|
|
333
|
+
ok: false,
|
|
334
|
+
code: "no_capacity_provider",
|
|
335
|
+
reason: "No active helper capacity provider is available.",
|
|
336
|
+
estimate: input.estimate,
|
|
337
|
+
candidates
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
if (grants.length === 0) {
|
|
341
|
+
return {
|
|
342
|
+
ok: false,
|
|
343
|
+
code: "no_capacity_grant",
|
|
344
|
+
reason: "No active capacity grant is available for this team, project, and environment.",
|
|
345
|
+
estimate: input.estimate,
|
|
346
|
+
candidates
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const eligible = candidates.filter((candidate) => candidate.eligible).sort((left, right) => right.score.score - left.score.score || left.laneId.localeCompare(right.laneId));
|
|
350
|
+
const selected = eligible[0] ?? null;
|
|
351
|
+
if (!selected) {
|
|
352
|
+
const hasApprovalBlock = candidates.some((candidate) => candidate.reasons.includes("approval_required"));
|
|
353
|
+
const hasBudgetBlock = candidates.some((candidate) => candidate.reasons.includes("insufficient_budget"));
|
|
354
|
+
return {
|
|
355
|
+
ok: false,
|
|
356
|
+
code: hasApprovalBlock ? "approval_required" : hasBudgetBlock ? "insufficient_budget" : "no_eligible_lane",
|
|
357
|
+
reason: hasApprovalBlock ? "The requested helper task needs approval before capacity can be reserved." : hasBudgetBlock ? "The requested helper task is above the remaining approved budget." : "No provider lane matches the task policy and capability requirements.",
|
|
358
|
+
estimate: input.estimate,
|
|
359
|
+
candidates
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const provider = providers.find((candidate) => candidate.id === selected.providerId);
|
|
363
|
+
const lane = input.plan.lanes.find((candidate) => candidate.id === selected.laneId);
|
|
364
|
+
const grant = grants.find((candidate) => candidate.id === selected.grantId);
|
|
365
|
+
if (!provider || !lane || !grant) {
|
|
366
|
+
return {
|
|
367
|
+
ok: false,
|
|
368
|
+
code: "no_eligible_lane",
|
|
369
|
+
reason: "The selected capacity lane could not be resolved.",
|
|
370
|
+
estimate: input.estimate,
|
|
371
|
+
candidates
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
const candidatePayload = candidates.map((candidate) => ({
|
|
375
|
+
providerId: candidate.providerId,
|
|
376
|
+
laneId: candidate.laneId,
|
|
377
|
+
grantId: candidate.grantId,
|
|
378
|
+
remainingCredits: candidate.remainingCredits,
|
|
379
|
+
eligible: candidate.eligible,
|
|
380
|
+
reasons: candidate.reasons,
|
|
381
|
+
score: candidate.score.score
|
|
382
|
+
}));
|
|
383
|
+
const scorePayload = Object.fromEntries(candidates.map((candidate) => [candidate.laneId, candidate.score]));
|
|
384
|
+
const reservation = {
|
|
385
|
+
capacityProviderId: provider.id,
|
|
386
|
+
laneId: lane.id,
|
|
387
|
+
teamId: input.plan.teamId,
|
|
388
|
+
projectId: input.plan.projectId,
|
|
389
|
+
workDayId: input.workDayId ?? null,
|
|
390
|
+
taskId: input.taskId ?? null,
|
|
391
|
+
state: "reserved",
|
|
392
|
+
reservedCredits: input.estimate.reservedCredits,
|
|
393
|
+
metadata: {
|
|
394
|
+
...input.metadata ?? {},
|
|
395
|
+
grantId: grant.id,
|
|
396
|
+
taskSignature: input.estimate.taskSignature,
|
|
397
|
+
estimatedCreditsP50: input.estimate.estimatedCreditsP50,
|
|
398
|
+
estimatedCreditsP90: input.estimate.estimatedCreditsP90
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
const routingDecision = {
|
|
402
|
+
taskId: input.taskId ?? null,
|
|
403
|
+
workDayId: input.workDayId ?? null,
|
|
404
|
+
projectId: input.plan.projectId,
|
|
405
|
+
selectedProviderId: provider.id,
|
|
406
|
+
selectedLaneId: lane.id,
|
|
407
|
+
selectedModel: input.selectedModel ?? null,
|
|
408
|
+
decision: "selected",
|
|
409
|
+
reason: selected.score.reasons.length > 0 ? selected.score.reasons.join(",") : "best_eligible_lane",
|
|
410
|
+
candidates: candidatePayload,
|
|
411
|
+
scores: scorePayload,
|
|
412
|
+
metadata: {
|
|
413
|
+
...input.metadata ?? {},
|
|
414
|
+
grantId: grant.id,
|
|
415
|
+
remainingCreditsBefore: selected.remainingCredits,
|
|
416
|
+
reservedCredits: input.estimate.reservedCredits
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
const ledgerEntry = {
|
|
420
|
+
capacityProviderId: provider.id,
|
|
421
|
+
laneId: lane.id,
|
|
422
|
+
teamId: input.plan.teamId,
|
|
423
|
+
projectId: input.plan.projectId,
|
|
424
|
+
workDayId: input.workDayId ?? null,
|
|
425
|
+
taskId: input.taskId ?? null,
|
|
426
|
+
phase: "reservation_created",
|
|
427
|
+
credits: input.estimate.reservedCredits,
|
|
428
|
+
source: input.source ?? "capacity_coordinator",
|
|
429
|
+
metadata: {
|
|
430
|
+
...input.metadata ?? {},
|
|
431
|
+
grantId: grant.id,
|
|
432
|
+
taskSignature: input.estimate.taskSignature
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
return {
|
|
436
|
+
ok: true,
|
|
437
|
+
provider,
|
|
438
|
+
lane,
|
|
439
|
+
grant,
|
|
440
|
+
estimate: input.estimate,
|
|
441
|
+
remainingCreditsBefore: selected.remainingCredits,
|
|
442
|
+
reservation,
|
|
443
|
+
routingDecision,
|
|
444
|
+
ledgerEntry,
|
|
445
|
+
capacityMetadata: {
|
|
446
|
+
providerId: provider.id,
|
|
447
|
+
laneId: lane.id,
|
|
448
|
+
grantId: grant.id,
|
|
449
|
+
reservationId: reservation.id ?? null,
|
|
450
|
+
routingDecisionId: routingDecision.id ?? null,
|
|
451
|
+
estimatedCreditsP50: input.estimate.estimatedCreditsP50,
|
|
452
|
+
estimatedCreditsP90: input.estimate.estimatedCreditsP90,
|
|
453
|
+
reservedCredits: input.estimate.reservedCredits
|
|
454
|
+
},
|
|
455
|
+
candidates
|
|
456
|
+
};
|
|
457
|
+
}
|
|
94
458
|
export {
|
|
459
|
+
createReservationReleaseEntry,
|
|
95
460
|
reservationHasCapacity,
|
|
96
461
|
reserveCreditsForEstimate,
|
|
462
|
+
routeAndReserveCapacity,
|
|
97
463
|
scoreCapacityLane,
|
|
98
464
|
selectBestCapacityLane,
|
|
99
|
-
|
|
465
|
+
settleCapacityActuals,
|
|
466
|
+
summarizeCapacityPlan,
|
|
467
|
+
summarizeProjectCapacityPlan,
|
|
468
|
+
summarizeTeamCapacityPlan
|
|
100
469
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentPool, AgentPoolRegistration, AgentPoolScaleDecision, ApprovalRequest, CapacityGrant, CapacityPlan, CapacityProvider, CapacityProviderLane, CapacityReservation, CapacityRoutingDecision, CatalogArtifactVersion, CatalogItem, CatalogItemFilters, CreateApprovalRequestRequest, CreateCapacityReservationRequest, CreateCapacityRoutingDecisionRequest, CreateProjectDeploymentRequest, CreateTaskEstimateRequest, CreateTaskUsageActualRequest, PriorityOverride, PrioritySnapshot, ProjectConnection, ProjectDeployment, ProjectEnvironment, ProjectEnvironmentName, ProjectHosting, ProjectInfrastructureResource, ProjectWorkdaySummary, RecordAgentPoolRegistrationRequest, RecordCapacityUsageRequest, RepositoryClaim, RunnerScaleDecision, ScaleDecision, SdkCreateWorkdayRequest, SdkRecordRepositoryClaimRequest, SdkRecordRunnerScaleDecisionRequest, SdkRecordWorkerRunnerRequest, TeamStorageLocator, TeamWebHost, TaskEstimate, UpsertAgentPoolRequest, UpsertCapacityGrantRequest, UpsertCapacityProviderLaneRequest, UpsertCapacityProviderRequest, UpsertCatalogArtifactVersionRequest, UpsertCatalogItemRequest, UpsertProjectEnvironmentRequest, UpsertProjectHostingRequest, UpsertProjectInfrastructureResourceRequest, UpsertTeamStorageLocatorRequest, UpsertTeamWebHostRequest, WorkdayPolicy, WorkdayRequest, WorkerRunner, SdkPriorityOverrideRequest, SdkUpsertWorkPolicyRequest } from './sdk-types.ts';
|
|
2
|
-
import type { AgentMessageRecord, AgentStatusRecord, DirectBoardItemSummary, InboxItem, LaunchProjectRequest, LaunchProjectResult, ProjectOverviewSummary, ReleaseDetail, ReleaseSummary, SharePackageStatus, TeamHomeSummary, TeamMemberSummary, WorkstreamDetail, WorkstreamSummary } from './
|
|
2
|
+
import type { AgentMessageRecord, AgentStatusRecord, DirectBoardItemSummary, InboxItem, LaunchProjectRequest, LaunchProjectResult, ProjectOverviewSummary, ReleaseDetail, ReleaseSummary, SharePackageStatus, TeamHomeSummary, TeamMemberSummary, WorkstreamDetail, WorkstreamSummary } from './project-workflow.ts';
|
|
3
3
|
export interface ControlPlaneClientOptions {
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
accessToken?: string | null;
|
package/dist/d1-store.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ContentLeaseRecord } from './types/agents.ts';
|
|
2
2
|
import type { D1DatabaseLike } from './types/cloudflare.ts';
|
|
3
|
-
import type { ReleaseDetail, ReleaseSummary, SharePackageStatus, WorkstreamDetail, WorkstreamEvent, WorkstreamSummary } from './
|
|
3
|
+
import type { ReleaseDetail, ReleaseSummary, SharePackageStatus, WorkstreamDetail, WorkstreamEvent, WorkstreamSummary } from './project-workflow.ts';
|
|
4
4
|
import type { SdkAppendTaskEventRequest, SdkAckMessageRequest, SdkClaimMessageRequest, SdkClaimTaskRequest, SdkCloseWorkDayRequest, SdkCompleteTaskRequest, SdkCreateReportRequest, SdkCreateMessageRequest, SdkCreatePrioritySnapshotRequest, SdkCreateTaskRequest, SdkCursorEntity, SdkCursorRequest, SdkFailTaskRequest, SdkFollowRequest, SdkGetRequest, SdkGetCursorRequest, SdkLeaseEntity, SdkLeaseReleaseRequest, SdkManagerContextPayload, SdkMessageEntity, SdkMutationRequest, SdkPickRequest, SdkPickResult, SdkPriorityOverrideRequest, SdkClaimWorkdayManagerLeaseRequest, SdkCreateWorkdayRequest, SdkRecordRepositoryClaimRequest, SdkRecordRunnerScaleDecisionRequest, SdkRecordWorkerRunnerRequest, SdkRecordRunRequest, SdkRecordScaleDecisionRequest, SdkRecordTaskCreditsRequest, SdkReleaseWorkdayManagerLeaseRequest, SdkReportEntity, SdkRunEntity, SdkSearchRequest, SdkStartWorkDayRequest, SdkSubscriptionEntity, SdkTaskEntity, SdkTaskSearchRequest, SdkUpsertWorkPolicyRequest, SdkTaskProgressRequest, SdkUpdateWorkDayGraphRequest, SdkUpdateRequest, SdkWorkDayEntity, RepositoryClaim, RunnerScaleDecision, ScaleDecision, TaskCreditLedgerEntry, WorkdayManagerLease, WorkdayPolicy, WorkdayRequest, WorkerRunner, PrioritySnapshot } from './sdk-types.ts';
|
|
5
5
|
import { type LeaseClaimInput } from './stores/lease-store.ts';
|
|
6
6
|
export interface TryClaimContentLeaseInput extends LeaseClaimInput {
|
|
@@ -91,7 +91,7 @@ export declare class MemoryAgentDatabase implements AgentDatabase {
|
|
|
91
91
|
private readonly prioritySnapshots;
|
|
92
92
|
private readonly taskCreditLedger;
|
|
93
93
|
private readonly scaleDecisions;
|
|
94
|
-
private readonly
|
|
94
|
+
private readonly projectWorkflow;
|
|
95
95
|
private messageId;
|
|
96
96
|
constructor(seed?: {
|
|
97
97
|
subscriptions?: SdkSubscriptionEntity[];
|
|
@@ -235,7 +235,7 @@ export declare class CloudflareD1AgentDatabase implements AgentDatabase {
|
|
|
235
235
|
private readonly cursors;
|
|
236
236
|
private readonly leases;
|
|
237
237
|
private readonly operational;
|
|
238
|
-
private readonly
|
|
238
|
+
private readonly projectWorkflow;
|
|
239
239
|
constructor(db: D1DatabaseLike);
|
|
240
240
|
get(request: SdkGetRequest): Promise<Record<string, unknown> | null>;
|
|
241
241
|
search(request: SdkSearchRequest): Promise<Record<string, unknown>[]>;
|
|
@@ -318,11 +318,11 @@ export declare class CloudflareD1AgentDatabase implements AgentDatabase {
|
|
|
318
318
|
projectId: string;
|
|
319
319
|
title: string;
|
|
320
320
|
summary: string | null;
|
|
321
|
-
state: import("./
|
|
321
|
+
state: import("./project-workflow.ts").WorkstreamState;
|
|
322
322
|
branchName: string | null;
|
|
323
323
|
branchRef: string | null;
|
|
324
324
|
owner: string | null;
|
|
325
|
-
linkedItems: import("./
|
|
325
|
+
linkedItems: import("./project-workflow.ts").LinkedProjectRecordRef[];
|
|
326
326
|
verificationStatus: "completed" | "failed" | "waiting" | null;
|
|
327
327
|
verificationSummary: string | null;
|
|
328
328
|
lastSaveAt: string | null;
|