@agent-finops/core 0.8.0 → 0.9.0
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 +5 -3
- package/dist/actionPlanner.d.ts +140 -0
- package/dist/actionPlanner.js +938 -0
- package/dist/actionVerification.d.ts +1240 -0
- package/dist/actionVerification.js +1028 -0
- package/dist/activitySnapshot.d.ts +101 -9
- package/dist/activitySnapshot.js +145 -6
- package/dist/activitySnapshotCache.d.ts +8 -1
- package/dist/activitySnapshotCache.js +103 -7
- package/dist/agentEconomicsReceipt.d.ts +58 -58
- package/dist/analyze.js +3 -1
- package/dist/cutList.js +1 -1
- package/dist/glance.d.ts +30 -2
- package/dist/glance.js +265 -84
- package/dist/index.d.ts +11 -2
- package/dist/index.js +10 -1
- package/dist/insights.js +3 -1
- package/dist/localAgentFormats/gemini.js +2 -2
- package/dist/localAgentFormats/registry.js +6 -2
- package/dist/localAgentFormats/runtimeRegistry.js +5 -2
- package/dist/localAgentFormats/types.d.ts +2 -1
- package/dist/localAgentLogs.d.ts +362 -3
- package/dist/localAgentLogs.js +1964 -165
- package/dist/modelPricing.d.ts +1 -1
- package/dist/modelPricing.js +4 -1
- package/dist/planMath.js +12 -7
- package/dist/projectEconomics.d.ts +617 -0
- package/dist/projectEconomics.js +620 -0
- package/dist/projectEconomicsBuilder.d.ts +89 -0
- package/dist/projectEconomicsBuilder.js +473 -0
- package/dist/projectIndexStore.d.ts +545 -0
- package/dist/projectIndexStore.js +606 -0
- package/dist/providerConnectors.d.ts +59 -1
- package/dist/providerConnectors.js +192 -12
- package/dist/qualitativeIndexCache.d.ts +494 -0
- package/dist/qualitativeIndexCache.js +930 -0
- package/dist/resultCard.d.ts +350 -0
- package/dist/resultCard.js +604 -0
- package/dist/runtimeCommands.d.ts +21 -0
- package/dist/runtimeCommands.js +27 -0
- package/dist/scanGuard.d.ts +3 -1
- package/dist/scanGuard.js +164 -4
- package/dist/schema.d.ts +31 -31
- package/dist/sessionVitals.d.ts +145 -0
- package/dist/sessionVitals.js +521 -0
- package/dist/sourceRegistry.js +90 -52
- package/dist/toolInvocations.d.ts +40 -1
- package/dist/toolInvocations.js +101 -20
- package/package.json +1 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type TokenReductionExperimentV0 } from "./actionVerification.js";
|
|
2
|
+
import { type AcceptedOutcomeV0, type ApprovalEventV0, type ConfirmedOwnershipV0, type ProjectEconomicsReceiptV0 } from "./projectEconomics.js";
|
|
3
|
+
import { type UsageRecord } from "./schema.js";
|
|
4
|
+
export declare const projectEconomicsProjectionMissingValues: readonly ["confirmed_ownership", "ownership_project_scope", "token_experiment", "experiment_project_scope", "experiment_work_unit_scope", "intervention_application", "approval_event", "approval_experiment_link", "accepted_outcome", "outcome_work_unit_scope", "financial_project_attribution", "financial_work_unit_attribution", "financial_cost_evidence", "financial_line_limit", "measured_token_result", "provider_bill_reconciliation"];
|
|
5
|
+
export type ProjectEconomicsProjectionMissingCodeV0 = typeof projectEconomicsProjectionMissingValues[number];
|
|
6
|
+
export type ProjectEconomicsProjectionMissingV0 = {
|
|
7
|
+
code: ProjectEconomicsProjectionMissingCodeV0;
|
|
8
|
+
evidence: "missing";
|
|
9
|
+
blocksReceipt: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ProjectEconomicsScopeV0 = {
|
|
12
|
+
/** Receipt/accountability reference for the exact project. */
|
|
13
|
+
projectRef: string;
|
|
14
|
+
/** Receipt/accountability reference for the exact accepted work unit. */
|
|
15
|
+
workUnitRef: string;
|
|
16
|
+
/** Action-verification reference for the same exact project. */
|
|
17
|
+
actionProjectRef: string;
|
|
18
|
+
/** Action-verification reference for the same exact accepted work unit. */
|
|
19
|
+
actionWorkUnitRef: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Explicit attribution bridge for one candidate financial record.
|
|
23
|
+
*
|
|
24
|
+
* The builder never hashes or guesses `UsageRecord.projectId` into receipt
|
|
25
|
+
* scope. The caller must supply the already-confirmed opaque references and
|
|
26
|
+
* their evidence labels; unknown attribution remains null + missing.
|
|
27
|
+
*/
|
|
28
|
+
export type ProjectEconomicsUsageBindingV0 = {
|
|
29
|
+
record: UsageRecord;
|
|
30
|
+
projectRef: string | null;
|
|
31
|
+
projectEvidence: "verified" | "observed" | "user_declared" | "missing";
|
|
32
|
+
workUnitRef: string | null;
|
|
33
|
+
workUnitEvidence: "verified" | "observed" | "user_declared" | "missing";
|
|
34
|
+
};
|
|
35
|
+
export type BuildProjectEconomicsProjectionV0Input = {
|
|
36
|
+
generatedAt: string;
|
|
37
|
+
scope: ProjectEconomicsScopeV0;
|
|
38
|
+
financialRecords: readonly ProjectEconomicsUsageBindingV0[];
|
|
39
|
+
ownership?: ConfirmedOwnershipV0;
|
|
40
|
+
approvalEvent?: ApprovalEventV0;
|
|
41
|
+
outcome?: AcceptedOutcomeV0;
|
|
42
|
+
tokenExperiment?: TokenReductionExperimentV0;
|
|
43
|
+
};
|
|
44
|
+
export type ProjectEconomicsProjectionV0 = {
|
|
45
|
+
schemaVersion: 0;
|
|
46
|
+
status: "receipt_ready" | "incomplete";
|
|
47
|
+
generatedAt: string;
|
|
48
|
+
scope: ProjectEconomicsScopeV0;
|
|
49
|
+
costs: ProjectEconomicsReceiptV0["costs"];
|
|
50
|
+
action: {
|
|
51
|
+
wasteFindingRef: string | null;
|
|
52
|
+
tokenExperimentRef: string | null;
|
|
53
|
+
tokenExperimentRevisionRef: string | null;
|
|
54
|
+
};
|
|
55
|
+
measuredTokenResult: ProjectEconomicsReceiptV0["measuredTokenResult"];
|
|
56
|
+
billReconciliation: ProjectEconomicsReceiptV0["billReconciliation"];
|
|
57
|
+
claims: ProjectEconomicsReceiptV0["claims"];
|
|
58
|
+
missing: ProjectEconomicsProjectionMissingV0[];
|
|
59
|
+
receipt: ProjectEconomicsReceiptV0 | null;
|
|
60
|
+
};
|
|
61
|
+
export type ProjectEconomicsPlannedActionRefsV0 = {
|
|
62
|
+
changeRef: string;
|
|
63
|
+
rollbackRef: string;
|
|
64
|
+
canaryRef: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Stable pre-change reference for one exact reversible plan. Approval binds
|
|
68
|
+
* the immutable experiment lineage plus the opaque change, rollback, and
|
|
69
|
+
* canary descriptions before the plan is handed to an agent. Timestamps and
|
|
70
|
+
* the eventual canary outcome are deliberately excluded: they are execution
|
|
71
|
+
* evidence, not part of what the human approved.
|
|
72
|
+
*/
|
|
73
|
+
export declare function createProjectEconomicsPlannedActionRefV0(tokenExperiment: TokenReductionExperimentV0, references: ProjectEconomicsPlannedActionRefsV0): string;
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the same pre-change action reference from its later applied form.
|
|
76
|
+
* Post-change evidence may advance the experiment revision without rewriting
|
|
77
|
+
* the human's earlier approval boundary.
|
|
78
|
+
*/
|
|
79
|
+
export declare function createProjectEconomicsApprovalActionRefV0(tokenExperiment: TokenReductionExperimentV0): string;
|
|
80
|
+
/**
|
|
81
|
+
* Build the smallest read-only project-economics view.
|
|
82
|
+
*
|
|
83
|
+
* Invalid or forged supplied contracts throw. Absent evidence and valid but
|
|
84
|
+
* unlinked evidence return an incomplete projection with stable missing codes.
|
|
85
|
+
* No state is written, no native identity is returned, and no ROI, complete
|
|
86
|
+
* invoice reconciliation, or RBAC claim is created.
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildProjectEconomicsProjectionV0(input: BuildProjectEconomicsProjectionV0Input): ProjectEconomicsProjectionV0;
|
|
89
|
+
//# sourceMappingURL=projectEconomicsBuilder.d.ts.map
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createActionVerificationReference, parseTokenReductionExperimentV0 } from "./actionVerification.js";
|
|
4
|
+
import { MAX_PROJECT_COST_LINES_V0, PROJECT_ECONOMICS_RECEIPT_V0_KIND, PROJECT_ECONOMICS_V0_VERSION, createProjectEconomicsReceiptV0, createProjectEconomicsReference, parseAcceptedOutcomeV0, parseApprovalEventV0, parseConfirmedOwnershipV0, projectEconomicsReferenceSchema } from "./projectEconomics.js";
|
|
5
|
+
import { usageRecordSchema } from "./schema.js";
|
|
6
|
+
const utcTimestampSchema = z.string()
|
|
7
|
+
.datetime({ offset: true })
|
|
8
|
+
.transform((value) => new Date(value).toISOString());
|
|
9
|
+
const actionVerificationReferenceSchema = z.string()
|
|
10
|
+
.regex(/^avref_[a-f0-9]{64}$/);
|
|
11
|
+
const attributionEvidenceSchema = z.enum([
|
|
12
|
+
"verified",
|
|
13
|
+
"observed",
|
|
14
|
+
"user_declared",
|
|
15
|
+
"missing"
|
|
16
|
+
]);
|
|
17
|
+
const API_EQUIVALENT_COST_TYPES = new Set([
|
|
18
|
+
"local_agent_logs",
|
|
19
|
+
"anthropic_claude_code_usage",
|
|
20
|
+
"anthropic_claude_code_api_equivalent"
|
|
21
|
+
]);
|
|
22
|
+
export const projectEconomicsProjectionMissingValues = [
|
|
23
|
+
"confirmed_ownership",
|
|
24
|
+
"ownership_project_scope",
|
|
25
|
+
"token_experiment",
|
|
26
|
+
"experiment_project_scope",
|
|
27
|
+
"experiment_work_unit_scope",
|
|
28
|
+
"intervention_application",
|
|
29
|
+
"approval_event",
|
|
30
|
+
"approval_experiment_link",
|
|
31
|
+
"accepted_outcome",
|
|
32
|
+
"outcome_work_unit_scope",
|
|
33
|
+
"financial_project_attribution",
|
|
34
|
+
"financial_work_unit_attribution",
|
|
35
|
+
"financial_cost_evidence",
|
|
36
|
+
"financial_line_limit",
|
|
37
|
+
"measured_token_result",
|
|
38
|
+
"provider_bill_reconciliation"
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Stable pre-change reference for one exact reversible plan. Approval binds
|
|
42
|
+
* the immutable experiment lineage plus the opaque change, rollback, and
|
|
43
|
+
* canary descriptions before the plan is handed to an agent. Timestamps and
|
|
44
|
+
* the eventual canary outcome are deliberately excluded: they are execution
|
|
45
|
+
* evidence, not part of what the human approved.
|
|
46
|
+
*/
|
|
47
|
+
export function createProjectEconomicsPlannedActionRefV0(tokenExperiment, references) {
|
|
48
|
+
// This constructor binds only canonical lineage IDs and opaque plan refs.
|
|
49
|
+
// The receipt builder still parses the complete experiment before making
|
|
50
|
+
// any linkage claim; requiring unrelated future experiment fields here
|
|
51
|
+
// would make a pre-change reference depend on post-change evidence.
|
|
52
|
+
const experiment = tokenExperiment;
|
|
53
|
+
if (!/^tre_v0_[a-f0-9]{64}$/.test(experiment.id) ||
|
|
54
|
+
!/^wf_v0_[a-f0-9]{64}$/.test(experiment.finding?.id ?? "")) {
|
|
55
|
+
throw new TypeError("A planned-action reference requires canonical experiment and finding lineage.");
|
|
56
|
+
}
|
|
57
|
+
if (experiment.lifecycle !== "baseline_ready" && !hasAppliedIntervention(experiment)) {
|
|
58
|
+
throw new TypeError("A planned-action reference requires a complete baseline or its applied continuation.");
|
|
59
|
+
}
|
|
60
|
+
for (const reference of [
|
|
61
|
+
references.changeRef,
|
|
62
|
+
references.rollbackRef,
|
|
63
|
+
references.canaryRef
|
|
64
|
+
]) {
|
|
65
|
+
if (!/^avref_[a-f0-9]{64}$/.test(reference)) {
|
|
66
|
+
throw new TypeError("A planned-action reference requires opaque change, rollback, and canary references.");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return createActionVerificationReference("project-economics-approved-action", JSON.stringify({
|
|
70
|
+
experimentId: experiment.id,
|
|
71
|
+
findingId: experiment.finding.id,
|
|
72
|
+
changeRef: references.changeRef,
|
|
73
|
+
rollbackRef: references.rollbackRef,
|
|
74
|
+
canaryRef: references.canaryRef
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the same pre-change action reference from its later applied form.
|
|
79
|
+
* Post-change evidence may advance the experiment revision without rewriting
|
|
80
|
+
* the human's earlier approval boundary.
|
|
81
|
+
*/
|
|
82
|
+
export function createProjectEconomicsApprovalActionRefV0(tokenExperiment) {
|
|
83
|
+
const experiment = parseTokenReductionExperimentV0(tokenExperiment);
|
|
84
|
+
if (!hasAppliedIntervention(experiment)) {
|
|
85
|
+
throw new TypeError("An approval action reference requires one explicitly approved, applied intervention.");
|
|
86
|
+
}
|
|
87
|
+
return createProjectEconomicsPlannedActionRefV0(experiment, {
|
|
88
|
+
changeRef: experiment.intervention.changeRef,
|
|
89
|
+
rollbackRef: experiment.intervention.rollbackRef,
|
|
90
|
+
canaryRef: experiment.intervention.canary.evidenceRef
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build the smallest read-only project-economics view.
|
|
95
|
+
*
|
|
96
|
+
* Invalid or forged supplied contracts throw. Absent evidence and valid but
|
|
97
|
+
* unlinked evidence return an incomplete projection with stable missing codes.
|
|
98
|
+
* No state is written, no native identity is returned, and no ROI, complete
|
|
99
|
+
* invoice reconciliation, or RBAC claim is created.
|
|
100
|
+
*/
|
|
101
|
+
export function buildProjectEconomicsProjectionV0(input) {
|
|
102
|
+
const generatedAt = utcTimestampSchema.parse(input.generatedAt);
|
|
103
|
+
const scope = parseScope(input.scope);
|
|
104
|
+
const bindings = parseUsageBindings(input.financialRecords);
|
|
105
|
+
const missing = new Set();
|
|
106
|
+
const ownership = input.ownership === undefined
|
|
107
|
+
? undefined
|
|
108
|
+
: parseConfirmedOwnershipV0(input.ownership);
|
|
109
|
+
if (!ownership)
|
|
110
|
+
missing.add("confirmed_ownership");
|
|
111
|
+
else if (ownership.projectRef !== scope.projectRef) {
|
|
112
|
+
missing.add("ownership_project_scope");
|
|
113
|
+
}
|
|
114
|
+
const experiment = input.tokenExperiment === undefined
|
|
115
|
+
? undefined
|
|
116
|
+
: parseTokenReductionExperimentV0(input.tokenExperiment);
|
|
117
|
+
if (!experiment)
|
|
118
|
+
missing.add("token_experiment");
|
|
119
|
+
const experimentProjectExact = experiment?.cohort.projectRef === scope.actionProjectRef;
|
|
120
|
+
if (experiment && !experimentProjectExact) {
|
|
121
|
+
missing.add("experiment_project_scope");
|
|
122
|
+
}
|
|
123
|
+
const experimentWorkUnitExact = experiment
|
|
124
|
+
? hasExactExperimentWorkUnit(experiment, scope.actionWorkUnitRef)
|
|
125
|
+
: false;
|
|
126
|
+
if (experiment && !experimentWorkUnitExact) {
|
|
127
|
+
missing.add("experiment_work_unit_scope");
|
|
128
|
+
}
|
|
129
|
+
if (experiment && !hasAppliedIntervention(experiment)) {
|
|
130
|
+
missing.add("intervention_application");
|
|
131
|
+
}
|
|
132
|
+
const approvalEvent = input.approvalEvent === undefined
|
|
133
|
+
? undefined
|
|
134
|
+
: parseApprovalEventV0(input.approvalEvent);
|
|
135
|
+
if (!approvalEvent)
|
|
136
|
+
missing.add("approval_event");
|
|
137
|
+
else if (!experiment || !approvalLinksExactRevision(approvalEvent, experiment)) {
|
|
138
|
+
missing.add("approval_experiment_link");
|
|
139
|
+
}
|
|
140
|
+
const outcome = input.outcome === undefined
|
|
141
|
+
? undefined
|
|
142
|
+
: parseAcceptedOutcomeV0(input.outcome);
|
|
143
|
+
if (!outcome)
|
|
144
|
+
missing.add("accepted_outcome");
|
|
145
|
+
else if (outcome.workUnitRef !== scope.workUnitRef) {
|
|
146
|
+
missing.add("outcome_work_unit_scope");
|
|
147
|
+
}
|
|
148
|
+
const costProjection = buildCostProjection(bindings, scope);
|
|
149
|
+
for (const code of costProjection.missing)
|
|
150
|
+
missing.add(code);
|
|
151
|
+
if (costProjection.costs.lines.length > MAX_PROJECT_COST_LINES_V0) {
|
|
152
|
+
missing.add("financial_line_limit");
|
|
153
|
+
}
|
|
154
|
+
const exactExperimentScope = Boolean(experimentProjectExact && experimentWorkUnitExact);
|
|
155
|
+
const measuredTokenResult = experiment
|
|
156
|
+
? projectMeasuredTokenResult(experiment, exactExperimentScope)
|
|
157
|
+
: inconclusiveTokenResult(0, 0);
|
|
158
|
+
if (measuredTokenResult.status === "inconclusive") {
|
|
159
|
+
missing.add("measured_token_result");
|
|
160
|
+
}
|
|
161
|
+
missing.add("provider_bill_reconciliation");
|
|
162
|
+
const billReconciliation = {
|
|
163
|
+
status: "not_attempted",
|
|
164
|
+
evidence: "missing"
|
|
165
|
+
};
|
|
166
|
+
const claims = {
|
|
167
|
+
roi: "not_claimed",
|
|
168
|
+
invoiceReconciled: false,
|
|
169
|
+
rbacVerified: false
|
|
170
|
+
};
|
|
171
|
+
const blocksReceipt = [...missing].some((code) => missingBlocksReceipt(code));
|
|
172
|
+
const canCreateReceipt = !blocksReceipt && ownership !== undefined &&
|
|
173
|
+
experiment !== undefined && approvalEvent !== undefined && outcome !== undefined;
|
|
174
|
+
const receipt = canCreateReceipt
|
|
175
|
+
? createProjectEconomicsReceiptV0({
|
|
176
|
+
kind: PROJECT_ECONOMICS_RECEIPT_V0_KIND,
|
|
177
|
+
schemaVersion: PROJECT_ECONOMICS_V0_VERSION,
|
|
178
|
+
generatedAt,
|
|
179
|
+
scope: {
|
|
180
|
+
projectRef: scope.projectRef,
|
|
181
|
+
workUnitRef: scope.workUnitRef
|
|
182
|
+
},
|
|
183
|
+
ownership,
|
|
184
|
+
costs: costProjection.costs,
|
|
185
|
+
action: {
|
|
186
|
+
wasteFindingRef: experiment.finding.id,
|
|
187
|
+
tokenExperimentRef: experiment.id,
|
|
188
|
+
tokenExperimentRevisionRef: experiment.revisionId,
|
|
189
|
+
approvalEvent
|
|
190
|
+
},
|
|
191
|
+
outcome,
|
|
192
|
+
measuredTokenResult,
|
|
193
|
+
billReconciliation,
|
|
194
|
+
claims
|
|
195
|
+
})
|
|
196
|
+
: null;
|
|
197
|
+
return {
|
|
198
|
+
schemaVersion: 0,
|
|
199
|
+
status: receipt ? "receipt_ready" : "incomplete",
|
|
200
|
+
generatedAt,
|
|
201
|
+
scope,
|
|
202
|
+
costs: costProjection.costs,
|
|
203
|
+
action: {
|
|
204
|
+
wasteFindingRef: experiment?.finding.id ?? null,
|
|
205
|
+
tokenExperimentRef: experiment?.id ?? null,
|
|
206
|
+
tokenExperimentRevisionRef: experiment?.revisionId ?? null
|
|
207
|
+
},
|
|
208
|
+
measuredTokenResult,
|
|
209
|
+
billReconciliation,
|
|
210
|
+
claims,
|
|
211
|
+
missing: projectEconomicsProjectionMissingValues
|
|
212
|
+
.filter((code) => missing.has(code))
|
|
213
|
+
.map((code) => ({
|
|
214
|
+
code,
|
|
215
|
+
evidence: "missing",
|
|
216
|
+
blocksReceipt: missingBlocksReceipt(code)
|
|
217
|
+
})),
|
|
218
|
+
receipt
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function parseScope(scope) {
|
|
222
|
+
return {
|
|
223
|
+
projectRef: projectEconomicsReferenceSchema.parse(scope.projectRef),
|
|
224
|
+
workUnitRef: projectEconomicsReferenceSchema.parse(scope.workUnitRef),
|
|
225
|
+
actionProjectRef: actionVerificationReferenceSchema.parse(scope.actionProjectRef),
|
|
226
|
+
actionWorkUnitRef: actionVerificationReferenceSchema.parse(scope.actionWorkUnitRef)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function parseUsageBindings(bindings) {
|
|
230
|
+
const parsed = bindings.map((binding) => {
|
|
231
|
+
const record = usageRecordSchema.parse(binding.record);
|
|
232
|
+
const projectRef = binding.projectRef === null
|
|
233
|
+
? null
|
|
234
|
+
: projectEconomicsReferenceSchema.parse(binding.projectRef);
|
|
235
|
+
const workUnitRef = binding.workUnitRef === null
|
|
236
|
+
? null
|
|
237
|
+
: projectEconomicsReferenceSchema.parse(binding.workUnitRef);
|
|
238
|
+
const projectEvidence = attributionEvidenceSchema.parse(binding.projectEvidence);
|
|
239
|
+
const workUnitEvidence = attributionEvidenceSchema.parse(binding.workUnitEvidence);
|
|
240
|
+
requireReferenceEvidencePair(projectRef, projectEvidence, "project");
|
|
241
|
+
requireReferenceEvidencePair(workUnitRef, workUnitEvidence, "work unit");
|
|
242
|
+
return { record, projectRef, projectEvidence, workUnitRef, workUnitEvidence };
|
|
243
|
+
});
|
|
244
|
+
const ids = new Set();
|
|
245
|
+
for (const binding of parsed) {
|
|
246
|
+
if (ids.has(binding.record.id)) {
|
|
247
|
+
throw new TypeError("Duplicate financial usage record IDs are not allowed.");
|
|
248
|
+
}
|
|
249
|
+
ids.add(binding.record.id);
|
|
250
|
+
}
|
|
251
|
+
return parsed.sort((left, right) => left.record.id.localeCompare(right.record.id));
|
|
252
|
+
}
|
|
253
|
+
function requireReferenceEvidencePair(reference, evidence, label) {
|
|
254
|
+
if ((reference === null) !== (evidence === "missing")) {
|
|
255
|
+
throw new TypeError(`A ${label} reference and non-missing attribution evidence must be present together.`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function hasExactExperimentWorkUnit(experiment, actionWorkUnitRef) {
|
|
259
|
+
if (experiment.matchingPolicy.basis !== "accepted_work_unit")
|
|
260
|
+
return false;
|
|
261
|
+
const excludedPostSessions = new Set(experiment.evaluation.exclusions
|
|
262
|
+
.filter((entry) => entry.phase === "post_change")
|
|
263
|
+
.map((entry) => entry.sessionRef));
|
|
264
|
+
// Accepted-work-unit cohorts compare successful tasks, not one PR against
|
|
265
|
+
// itself. Link the receipt's exact accepted outcome to one included post
|
|
266
|
+
// session while allowing the other matched baseline/post outcomes to retain
|
|
267
|
+
// their own opaque work-unit references.
|
|
268
|
+
return experiment.postSessions.some((session) => session.workUnitRef === actionWorkUnitRef &&
|
|
269
|
+
!excludedPostSessions.has(session.sessionRef));
|
|
270
|
+
}
|
|
271
|
+
function hasAppliedIntervention(experiment) {
|
|
272
|
+
return experiment.intervention.approval.status === "explicit" &&
|
|
273
|
+
experiment.intervention.approval.evidence !== "missing" &&
|
|
274
|
+
experiment.intervention.approval.approvedAt !== undefined &&
|
|
275
|
+
experiment.intervention.appliedAt !== undefined &&
|
|
276
|
+
experiment.intervention.changeRef !== undefined &&
|
|
277
|
+
experiment.intervention.rollbackRef !== undefined &&
|
|
278
|
+
experiment.intervention.canary?.status !== "missing" &&
|
|
279
|
+
experiment.intervention.canary?.evidenceRef !== undefined;
|
|
280
|
+
}
|
|
281
|
+
function approvalLinksExactRevision(approval, experiment) {
|
|
282
|
+
const intervention = experiment.intervention;
|
|
283
|
+
return hasAppliedIntervention(experiment) &&
|
|
284
|
+
approval.references.actionRef === createProjectEconomicsApprovalActionRefV0(experiment) &&
|
|
285
|
+
approval.references.changeRef === intervention.changeRef &&
|
|
286
|
+
approval.references.rollbackRef === intervention.rollbackRef &&
|
|
287
|
+
approval.references.canaryRef === intervention.canary?.evidenceRef &&
|
|
288
|
+
approval.approvedAt === intervention.approval.approvedAt;
|
|
289
|
+
}
|
|
290
|
+
function buildCostProjection(bindings, scope) {
|
|
291
|
+
const missing = new Set();
|
|
292
|
+
const groups = new Map();
|
|
293
|
+
let coveredRecords = 0;
|
|
294
|
+
for (const binding of bindings) {
|
|
295
|
+
const projectExact = binding.projectRef === scope.projectRef;
|
|
296
|
+
const workUnitExact = binding.workUnitRef === scope.workUnitRef;
|
|
297
|
+
if (!projectExact)
|
|
298
|
+
missing.add("financial_project_attribution");
|
|
299
|
+
if (!workUnitExact)
|
|
300
|
+
missing.add("financial_work_unit_attribution");
|
|
301
|
+
const classification = projectExact && workUnitExact
|
|
302
|
+
? classifyFinancialRecord(binding.record)
|
|
303
|
+
: missingCostLine(binding.record);
|
|
304
|
+
if (classification.basis === "missing") {
|
|
305
|
+
missing.add("financial_cost_evidence");
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
coveredRecords += 1;
|
|
309
|
+
}
|
|
310
|
+
const key = `${classification.sourceRef}\u0000${classification.basis}\u0000${classification.evidence}`;
|
|
311
|
+
const current = groups.get(key);
|
|
312
|
+
if (!current) {
|
|
313
|
+
groups.set(key, { ...classification, records: 1 });
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (classification.amountUsd === null || current.amountUsd === null) {
|
|
317
|
+
if (classification.amountUsd !== current.amountUsd) {
|
|
318
|
+
throw new TypeError("A financial cost group cannot mix missing and present amounts.");
|
|
319
|
+
}
|
|
320
|
+
current.records += 1;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const amountUsd = current.amountUsd + classification.amountUsd;
|
|
324
|
+
if (!Number.isFinite(amountUsd) || amountUsd < 0) {
|
|
325
|
+
throw new TypeError("A financial cost line total must remain finite and non-negative.");
|
|
326
|
+
}
|
|
327
|
+
current.amountUsd = Object.is(amountUsd, -0) ? 0 : amountUsd;
|
|
328
|
+
current.records += 1;
|
|
329
|
+
}
|
|
330
|
+
if (bindings.length === 0 || coveredRecords === 0) {
|
|
331
|
+
missing.add("financial_cost_evidence");
|
|
332
|
+
}
|
|
333
|
+
const status = bindings.length === 0 || coveredRecords === 0
|
|
334
|
+
? "missing"
|
|
335
|
+
: coveredRecords === bindings.length ? "complete" : "partial";
|
|
336
|
+
return {
|
|
337
|
+
costs: {
|
|
338
|
+
lines: [...groups.values()]
|
|
339
|
+
.map(({ records: _records, ...line }) => line)
|
|
340
|
+
.sort((left, right) => costLineKey(left).localeCompare(costLineKey(right))),
|
|
341
|
+
coverage: {
|
|
342
|
+
status,
|
|
343
|
+
coveredRecords,
|
|
344
|
+
eligibleRecords: bindings.length,
|
|
345
|
+
evidence: status === "missing" ? "missing" : "calculated"
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
missing
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function classifyFinancialRecord(record) {
|
|
352
|
+
const sourceRef = costSourceRef(record);
|
|
353
|
+
if (isBundledSampleRecord(record) || record.amountUsd === null ||
|
|
354
|
+
record.costConfidence === "missing" ||
|
|
355
|
+
record.costConfidence === "detected_unverified" ||
|
|
356
|
+
record.source.confidence === "missing" ||
|
|
357
|
+
record.source.confidence === "detected_unverified") {
|
|
358
|
+
return { sourceRef, basis: "missing", amountUsd: null, evidence: "missing" };
|
|
359
|
+
}
|
|
360
|
+
if (record.costConfidence === "verified" &&
|
|
361
|
+
record.source.confidence === "verified" &&
|
|
362
|
+
!API_EQUIVALENT_COST_TYPES.has(record.providerCostType ?? "") &&
|
|
363
|
+
record.usageGranularity === "billing_bucket") {
|
|
364
|
+
return {
|
|
365
|
+
sourceRef,
|
|
366
|
+
basis: "provider_billed",
|
|
367
|
+
amountUsd: record.amountUsd,
|
|
368
|
+
evidence: "verified"
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
if (record.costConfidence === "estimated" &&
|
|
372
|
+
API_EQUIVALENT_COST_TYPES.has(record.providerCostType ?? "")) {
|
|
373
|
+
return {
|
|
374
|
+
sourceRef,
|
|
375
|
+
basis: "api_equivalent_estimate",
|
|
376
|
+
amountUsd: record.amountUsd,
|
|
377
|
+
evidence: "calculated"
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
return { sourceRef, basis: "missing", amountUsd: null, evidence: "missing" };
|
|
381
|
+
}
|
|
382
|
+
function missingCostLine(record) {
|
|
383
|
+
return {
|
|
384
|
+
sourceRef: costSourceRef(record),
|
|
385
|
+
basis: "missing",
|
|
386
|
+
amountUsd: null,
|
|
387
|
+
evidence: "missing"
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function isBundledSampleRecord(record) {
|
|
391
|
+
return record.source.observedFrom === "sample_csv" &&
|
|
392
|
+
/(?:^|-)sample$/i.test(record.source.id);
|
|
393
|
+
}
|
|
394
|
+
function costSourceRef(record) {
|
|
395
|
+
const sourceIdentity = JSON.stringify([
|
|
396
|
+
record.source.id,
|
|
397
|
+
record.source.name,
|
|
398
|
+
record.source.provider,
|
|
399
|
+
record.source.observedFrom,
|
|
400
|
+
record.providerCostType ?? null,
|
|
401
|
+
record.usageGranularity ?? null
|
|
402
|
+
]);
|
|
403
|
+
const digest = createHash("sha256").update(sourceIdentity).digest("hex");
|
|
404
|
+
return createProjectEconomicsReference("cost-source", digest);
|
|
405
|
+
}
|
|
406
|
+
function costLineKey(line) {
|
|
407
|
+
return `${line.sourceRef}\u0000${line.basis}\u0000${line.evidence}\u0000${line.amountUsd ?? ""}`;
|
|
408
|
+
}
|
|
409
|
+
function projectMeasuredTokenResult(experiment, exactScope) {
|
|
410
|
+
const evaluation = experiment.evaluation;
|
|
411
|
+
if (!exactScope || !isConclusiveReceiptStatus(evaluation.status) ||
|
|
412
|
+
evaluation.metricEvidence !== "calculated" ||
|
|
413
|
+
evaluation.baseline.medianTotalTokens === null ||
|
|
414
|
+
evaluation.postChange.medianTotalTokens === null ||
|
|
415
|
+
evaluation.reductionPercent === null ||
|
|
416
|
+
evaluation.matchingEvidence === "missing" ||
|
|
417
|
+
evaluation.qualityEvidence === "missing") {
|
|
418
|
+
return inconclusiveTokenResult(evaluation.baseline.includedSessions, evaluation.postChange.includedSessions);
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
status: evaluation.status,
|
|
422
|
+
baselineMedianTokens: evaluation.baseline.medianTotalTokens,
|
|
423
|
+
postChangeMedianTokens: evaluation.postChange.medianTotalTokens,
|
|
424
|
+
baselineSessions: evaluation.baseline.includedSessions,
|
|
425
|
+
postChangeSessions: evaluation.postChange.includedSessions,
|
|
426
|
+
reductionPercent: evaluation.reductionPercent,
|
|
427
|
+
metricEvidence: "calculated",
|
|
428
|
+
matchingEvidence: evaluation.matchingEvidence,
|
|
429
|
+
qualityStatus: evaluation.qualityStatus,
|
|
430
|
+
qualityEvidence: evaluation.qualityEvidence
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function isConclusiveReceiptStatus(status) {
|
|
434
|
+
return status === "measured_token_reduction" ||
|
|
435
|
+
status === "no_measured_change" || status === "regressed";
|
|
436
|
+
}
|
|
437
|
+
function inconclusiveTokenResult(baselineSessions, postChangeSessions) {
|
|
438
|
+
return {
|
|
439
|
+
status: "inconclusive",
|
|
440
|
+
baselineMedianTokens: null,
|
|
441
|
+
postChangeMedianTokens: null,
|
|
442
|
+
baselineSessions,
|
|
443
|
+
postChangeSessions,
|
|
444
|
+
reductionPercent: null,
|
|
445
|
+
metricEvidence: "missing",
|
|
446
|
+
matchingEvidence: "missing",
|
|
447
|
+
qualityStatus: "insufficient",
|
|
448
|
+
qualityEvidence: "missing"
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function missingBlocksReceipt(code) {
|
|
452
|
+
switch (code) {
|
|
453
|
+
case "confirmed_ownership":
|
|
454
|
+
case "ownership_project_scope":
|
|
455
|
+
case "token_experiment":
|
|
456
|
+
case "experiment_project_scope":
|
|
457
|
+
case "experiment_work_unit_scope":
|
|
458
|
+
case "intervention_application":
|
|
459
|
+
case "approval_event":
|
|
460
|
+
case "approval_experiment_link":
|
|
461
|
+
case "accepted_outcome":
|
|
462
|
+
case "outcome_work_unit_scope":
|
|
463
|
+
case "financial_line_limit":
|
|
464
|
+
return true;
|
|
465
|
+
case "financial_project_attribution":
|
|
466
|
+
case "financial_work_unit_attribution":
|
|
467
|
+
case "financial_cost_evidence":
|
|
468
|
+
case "measured_token_result":
|
|
469
|
+
case "provider_bill_reconciliation":
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
//# sourceMappingURL=projectEconomicsBuilder.js.map
|