@agent-finops/core 0.8.1 → 0.9.1
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 +142 -50
- package/dist/activitySnapshot.js +145 -6
- package/dist/activitySnapshotCache.d.ts +8 -1
- package/dist/activitySnapshotCache.js +103 -7
- package/dist/agentDraftToken.d.ts +80 -0
- package/dist/agentDraftToken.js +188 -0
- package/dist/agentEconomicsReceipt.d.ts +74 -74
- package/dist/agentLoopContract.d.ts +27 -0
- package/dist/agentLoopContract.js +36 -0
- package/dist/glance.d.ts +27 -1
- package/dist/glance.js +151 -12
- package/dist/guidedAnswer.d.ts +51 -0
- package/dist/guidedAnswer.js +352 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.js +13 -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 +1 -1
- 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 +161 -1
- package/dist/providerConnectors.js +406 -11
- 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 +36 -0
- package/dist/runtimeCommands.js +50 -0
- package/dist/scanGuard.d.ts +3 -1
- package/dist/scanGuard.js +164 -4
- package/dist/schema.d.ts +33 -31
- package/dist/schema.js +9 -1
- package/dist/sessionVitals.d.ts +145 -0
- package/dist/sessionVitals.js +521 -0
- package/dist/toolInvocations.d.ts +40 -1
- package/dist/toolInvocations.js +101 -20
- package/package.json +1 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Distribution truth for the guided action loop.
|
|
3
|
+
*
|
|
4
|
+
* Release gate: change this single value to `published` only in the exact,
|
|
5
|
+
* coordinated new-version release candidate. Its packed-install gate must
|
|
6
|
+
* prove every generated handoff uses the new package command before publish;
|
|
7
|
+
* the clean public-registry smoke then verifies that same exact commit. Until
|
|
8
|
+
* that release candidate exists, handoffs execute the already-built checkout
|
|
9
|
+
* and must not let `npx` download the older public package.
|
|
10
|
+
*/
|
|
11
|
+
export const AIBILL_IMPROVE_DELIVERY_V0 = "published";
|
|
12
|
+
/**
|
|
13
|
+
* Build a command for capabilities that exist only in the current source
|
|
14
|
+
* preview. Keep every generated handoff on the checkout until the coordinated
|
|
15
|
+
* npm release containing those capabilities has passed its registry smoke.
|
|
16
|
+
*/
|
|
17
|
+
export function aibillCommandV0(args, delivery = AIBILL_IMPROVE_DELIVERY_V0) {
|
|
18
|
+
const commandArgs = args.trim();
|
|
19
|
+
return delivery === "source_preview"
|
|
20
|
+
? `node packages/cli/dist/index.js${commandArgs.length > 0 ? ` ${commandArgs}` : ""}`
|
|
21
|
+
: `npx aibill${commandArgs.length > 0 ? ` ${commandArgs}` : ""}`;
|
|
22
|
+
}
|
|
23
|
+
/** One privacy-safe command shared by terminal, MCP, and Glance. */
|
|
24
|
+
export function aibillImproveCommandV0(delivery = AIBILL_IMPROVE_DELIVERY_V0) {
|
|
25
|
+
return aibillCommandV0(delivery === "source_preview" ? "improve --path ." : "improve", delivery);
|
|
26
|
+
}
|
|
27
|
+
/** Published semver shape a composed pin must have (charset-safe by regex). */
|
|
28
|
+
const pinnableVersionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
29
|
+
/**
|
|
30
|
+
* Version-pinned command for machine-composed lines (M4c): a command an AI
|
|
31
|
+
* client relays to a human must be reproducible and must not silently
|
|
32
|
+
* resolve to a different release, so `draft_improve_command` pins to the
|
|
33
|
+
* composing package's own version (`npx aibill@<version> …`). A version
|
|
34
|
+
* that is not a plain semver falls back to the unpinned published command
|
|
35
|
+
* rather than composing an unrunnable line. In source-preview builds the
|
|
36
|
+
* checkout command needs no pin.
|
|
37
|
+
*
|
|
38
|
+
* Release gate (n2): the coordinated release must also prove the pinned
|
|
39
|
+
* version EXISTS on the public registry and supports the composed flags —
|
|
40
|
+
* the packed-install gate described on AIBILL_IMPROVE_DELIVERY_V0 is the
|
|
41
|
+
* natural home for that check; QA 24 asserts only that the pin is present.
|
|
42
|
+
*/
|
|
43
|
+
export function aibillPinnedCommandV0(args, version, delivery = AIBILL_IMPROVE_DELIVERY_V0) {
|
|
44
|
+
const commandArgs = args.trim();
|
|
45
|
+
if (delivery === "source_preview" || !pinnableVersionPattern.test(version)) {
|
|
46
|
+
return aibillCommandV0(commandArgs, delivery);
|
|
47
|
+
}
|
|
48
|
+
return `npx aibill@${version}${commandArgs.length > 0 ? ` ${commandArgs}` : ""}`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=runtimeCommands.js.map
|
package/dist/scanGuard.d.ts
CHANGED
|
@@ -33,7 +33,9 @@ export declare function resolveSafeStateDirectory(rootPath: string, options?: {
|
|
|
33
33
|
* inside a cloned repository, so validating only the parent directory is not
|
|
34
34
|
* enough: a committed child symlink must never expose an arbitrary local file.
|
|
35
35
|
*/
|
|
36
|
-
export declare function readSafeStateText(stateDir: string, fileName: string
|
|
36
|
+
export declare function readSafeStateText(stateDir: string, fileName: string, options?: {
|
|
37
|
+
maxBytes?: number;
|
|
38
|
+
}): Promise<string>;
|
|
37
39
|
/**
|
|
38
40
|
* Atomically replace one regular child file. The temporary file is created
|
|
39
41
|
* exclusively with mode 0600, and rename replaces a last-moment symlink
|
package/dist/scanGuard.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { constants, realpathSync } from "node:fs";
|
|
3
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
3
4
|
import { chmod, lstat, mkdir, open, realpath, rename, stat, unlink } from "node:fs/promises";
|
|
4
5
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { basename, join, resolve, sep } from "node:path";
|
|
6
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
|
+
import { promisify } from "node:util";
|
|
6
8
|
/**
|
|
7
9
|
* Shared unsafe-scan-root policy for EVERY scan entrypoint (CLI `scan`, MCP
|
|
8
10
|
* `scan_ai_spend`, and any future surface). Scanning the home directory, the
|
|
@@ -29,6 +31,7 @@ const systemRootDirectories = new Set([
|
|
|
29
31
|
"/sys",
|
|
30
32
|
"/dev"
|
|
31
33
|
]);
|
|
34
|
+
const execFile = promisify(execFileCallback);
|
|
32
35
|
export class UnsafeScanRootError extends Error {
|
|
33
36
|
rootPath;
|
|
34
37
|
constructor(rootPath, reason) {
|
|
@@ -127,9 +130,11 @@ export async function resolveSafeStateDirectory(rootPath, options = {}) {
|
|
|
127
130
|
return undefined;
|
|
128
131
|
throw error;
|
|
129
132
|
});
|
|
133
|
+
let createdStateDirectory = false;
|
|
130
134
|
if (!stateInfo && options.create) {
|
|
131
135
|
try {
|
|
132
136
|
await mkdir(statePath, { mode: 0o700 });
|
|
137
|
+
createdStateDirectory = true;
|
|
133
138
|
}
|
|
134
139
|
catch (error) {
|
|
135
140
|
// A concurrent creator is safe only after the same lstat checks below.
|
|
@@ -153,17 +158,145 @@ export async function resolveSafeStateDirectory(rootPath, options = {}) {
|
|
|
153
158
|
if (canonicalState !== statePath) {
|
|
154
159
|
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent does not resolve directly inside the approved root");
|
|
155
160
|
}
|
|
156
|
-
|
|
161
|
+
// Project state can contain private receipts and experiment evidence. A
|
|
162
|
+
// repository-authored marker or tracked state is not a trust boundary: the
|
|
163
|
+
// exact marker, private permissions, untracked status, and effective Git
|
|
164
|
+
// ignore rule must all be proven before either reads or writes proceed.
|
|
165
|
+
await assertPrivateStateGitBoundary(canonicalRoot, canonicalState, options.create === true);
|
|
166
|
+
const directoryMode = stateInfo.mode & 0o777;
|
|
167
|
+
if ((directoryMode & 0o077) !== 0) {
|
|
168
|
+
if (options.create !== true) {
|
|
169
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent permissions expose private state metadata to other users");
|
|
170
|
+
}
|
|
171
|
+
// Explicit create/write calls may safely migrate a legacy aibill-owned
|
|
172
|
+
// directory only after the Git privacy boundary above proved that neither
|
|
173
|
+
// it nor a child is tracked and that the exact private marker is effective.
|
|
174
|
+
// Read-only callers never change permissions implicitly.
|
|
175
|
+
await chmod(statePath, 0o700);
|
|
176
|
+
const securedInfo = await lstat(statePath);
|
|
177
|
+
if (!securedInfo.isDirectory() || securedInfo.isSymbolicLink() ||
|
|
178
|
+
securedInfo.dev !== stateInfo.dev || securedInfo.ino !== stateInfo.ino ||
|
|
179
|
+
(securedInfo.mode & 0o077) !== 0) {
|
|
180
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent permissions could not be secured without changing directory identity");
|
|
181
|
+
}
|
|
182
|
+
stateInfo = securedInfo;
|
|
183
|
+
}
|
|
184
|
+
else if (options.create && createdStateDirectory && directoryMode !== 0o700) {
|
|
185
|
+
// An unusually restrictive umask is still normalized for stable private
|
|
186
|
+
// access only on the directory this call created.
|
|
157
187
|
await chmod(statePath, 0o700);
|
|
158
188
|
}
|
|
159
189
|
return statePath;
|
|
160
190
|
}
|
|
191
|
+
async function assertPrivateStateGitBoundary(canonicalRoot, statePath, create) {
|
|
192
|
+
const gitEntryRoot = await findEnclosingGitEntryRoot(canonicalRoot);
|
|
193
|
+
const gitRoot = await resolveGitRoot(canonicalRoot, gitEntryRoot);
|
|
194
|
+
// Outside a Git worktree, the state files' own 0600 permissions and safe
|
|
195
|
+
// no-follow I/O are the privacy boundary. The ignore marker is specifically
|
|
196
|
+
// a repository-leak guard and is required only when Git can see this root.
|
|
197
|
+
if (!gitRoot)
|
|
198
|
+
return;
|
|
199
|
+
const relativeState = relative(gitRoot, statePath);
|
|
200
|
+
if (!relativeState || relativeState === ".." || relativeState.startsWith(`..${sep}`) ||
|
|
201
|
+
isAbsolute(relativeState)) {
|
|
202
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent is outside the resolved Git worktree");
|
|
203
|
+
}
|
|
204
|
+
const tracked = await execFile("git", ["-C", gitRoot, "ls-files", "--", relativeState], { encoding: "utf8", maxBuffer: 64 * 1024 }).then(({ stdout }) => stdout.trim()).catch(() => {
|
|
205
|
+
throw new UnsafeStateDirectoryError(statePath, "Git tracking status for .ai-spend-agent could not be verified");
|
|
206
|
+
});
|
|
207
|
+
if (tracked) {
|
|
208
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent or one of its children is already tracked by Git");
|
|
209
|
+
}
|
|
210
|
+
const markerPath = join(statePath, ".gitignore");
|
|
211
|
+
let handle;
|
|
212
|
+
try {
|
|
213
|
+
handle = await open(markerPath, constants.O_RDONLY | noFollowFlag());
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
if (!isNodeError(error, "ENOENT") || !create) {
|
|
217
|
+
if (isNodeError(error, "ELOOP")) {
|
|
218
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent/.gitignore is a symbolic link");
|
|
219
|
+
}
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
let writer;
|
|
223
|
+
try {
|
|
224
|
+
writer = await open(markerPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollowFlag(), 0o600);
|
|
225
|
+
await writer.writeFile("*\n", "utf8");
|
|
226
|
+
await writer.sync();
|
|
227
|
+
}
|
|
228
|
+
catch (createError) {
|
|
229
|
+
if (isNodeError(createError, "ELOOP")) {
|
|
230
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent/.gitignore is a symbolic link");
|
|
231
|
+
}
|
|
232
|
+
if (!isNodeError(createError, "EEXIST"))
|
|
233
|
+
throw createError;
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
await writer?.close().catch(() => undefined);
|
|
237
|
+
}
|
|
238
|
+
handle = await open(markerPath, constants.O_RDONLY | noFollowFlag());
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const info = await handle.stat();
|
|
242
|
+
if (!info.isFile() || (info.mode & 0o077) !== 0 || info.size !== 2) {
|
|
243
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent/.gitignore is not the private aibill marker");
|
|
244
|
+
}
|
|
245
|
+
const bytes = Buffer.alloc(2);
|
|
246
|
+
const { bytesRead } = await handle.read(bytes, 0, 2, 0);
|
|
247
|
+
if (bytesRead !== 2 || bytes.toString("utf8") !== "*\n") {
|
|
248
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent/.gitignore is not the exact private aibill marker");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
await handle.close().catch(() => undefined);
|
|
253
|
+
}
|
|
254
|
+
const relativeProbe = join(relativeState, "privacy-probe.json");
|
|
255
|
+
const ignored = await execFile("git", ["-C", gitRoot, "check-ignore", "--quiet", "--no-index", "--", relativeProbe], { maxBuffer: 64 * 1024 }).then(() => true).catch(() => false);
|
|
256
|
+
if (!ignored) {
|
|
257
|
+
throw new UnsafeStateDirectoryError(statePath, ".ai-spend-agent is not proven ignored by Git");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async function findEnclosingGitEntryRoot(path) {
|
|
261
|
+
let current = resolve(path);
|
|
262
|
+
while (true) {
|
|
263
|
+
const entry = await lstat(join(current, ".git")).catch((error) => {
|
|
264
|
+
if (isNodeError(error, "ENOENT") || isNodeError(error, "ENOTDIR"))
|
|
265
|
+
return undefined;
|
|
266
|
+
throw error;
|
|
267
|
+
});
|
|
268
|
+
if (entry)
|
|
269
|
+
return current;
|
|
270
|
+
const parent = dirname(current);
|
|
271
|
+
if (parent === current)
|
|
272
|
+
return undefined;
|
|
273
|
+
current = parent;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async function resolveGitRoot(canonicalRoot, gitEntryRoot) {
|
|
277
|
+
try {
|
|
278
|
+
const { stdout } = await execFile("git", ["-C", canonicalRoot, "rev-parse", "--show-toplevel"], { encoding: "utf8", maxBuffer: 64 * 1024 });
|
|
279
|
+
const reported = stdout.trim();
|
|
280
|
+
if (!reported || !isAbsolute(reported)) {
|
|
281
|
+
throw new UnsafeStateDirectoryError(canonicalRoot, "Git returned an invalid worktree root");
|
|
282
|
+
}
|
|
283
|
+
return await realpath(reported);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
if (error instanceof UnsafeStateDirectoryError)
|
|
287
|
+
throw error;
|
|
288
|
+
if (gitEntryRoot) {
|
|
289
|
+
throw new UnsafeStateDirectoryError(canonicalRoot, "the enclosing Git worktree could not be verified");
|
|
290
|
+
}
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
161
294
|
/**
|
|
162
295
|
* Read one regular child file without following a symbolic link. State can sit
|
|
163
296
|
* inside a cloned repository, so validating only the parent directory is not
|
|
164
297
|
* enough: a committed child symlink must never expose an arbitrary local file.
|
|
165
298
|
*/
|
|
166
|
-
export async function readSafeStateText(stateDir, fileName) {
|
|
299
|
+
export async function readSafeStateText(stateDir, fileName, options = {}) {
|
|
167
300
|
const filePath = await resolveSafeStateChild(stateDir, fileName, false);
|
|
168
301
|
let handle;
|
|
169
302
|
try {
|
|
@@ -172,7 +305,34 @@ export async function readSafeStateText(stateDir, fileName) {
|
|
|
172
305
|
if (!info.isFile()) {
|
|
173
306
|
throw new UnsafeStateFileError(filePath, "the state entry is not a regular file");
|
|
174
307
|
}
|
|
175
|
-
|
|
308
|
+
const maxBytes = options.maxBytes;
|
|
309
|
+
if (maxBytes === undefined)
|
|
310
|
+
return await handle.readFile("utf8");
|
|
311
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
312
|
+
throw new RangeError("maxBytes must be a non-negative safe integer");
|
|
313
|
+
}
|
|
314
|
+
if (info.size > maxBytes) {
|
|
315
|
+
throw new UnsafeStateFileError(filePath, `the state entry exceeds ${maxBytes} bytes`);
|
|
316
|
+
}
|
|
317
|
+
// Never use readFile for attacker-authored bounded state: a same-inode
|
|
318
|
+
// growth race could otherwise allocate the new whole size after fstat.
|
|
319
|
+
const buffer = Buffer.alloc(Math.min(maxBytes + 1, Math.max(1, info.size + 1)));
|
|
320
|
+
let offset = 0;
|
|
321
|
+
while (offset < buffer.length) {
|
|
322
|
+
const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);
|
|
323
|
+
if (bytesRead === 0)
|
|
324
|
+
break;
|
|
325
|
+
offset += bytesRead;
|
|
326
|
+
}
|
|
327
|
+
const after = await handle.stat();
|
|
328
|
+
if (offset > maxBytes || after.size > maxBytes) {
|
|
329
|
+
throw new UnsafeStateFileError(filePath, `the state entry exceeds ${maxBytes} bytes`);
|
|
330
|
+
}
|
|
331
|
+
if (after.size !== info.size || after.mtimeMs !== info.mtimeMs ||
|
|
332
|
+
after.ctimeMs !== info.ctimeMs) {
|
|
333
|
+
throw new UnsafeStateFileError(filePath, "the state entry changed while it was read");
|
|
334
|
+
}
|
|
335
|
+
return buffer.subarray(0, offset).toString("utf8");
|
|
176
336
|
}
|
|
177
337
|
catch (error) {
|
|
178
338
|
if (isNodeError(error, "ELOOP")) {
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const costConfidenceValues: readonly ["verified", "estimated", "detected_unverified", "missing"];
|
|
3
3
|
export declare const costConfidenceSchema: z.ZodEnum<{
|
|
4
|
-
verified: "verified";
|
|
5
4
|
estimated: "estimated";
|
|
5
|
+
verified: "verified";
|
|
6
6
|
detected_unverified: "detected_unverified";
|
|
7
7
|
missing: "missing";
|
|
8
8
|
}>;
|
|
@@ -12,12 +12,13 @@ export declare const spendSourceSchema: z.ZodObject<{
|
|
|
12
12
|
name: z.ZodString;
|
|
13
13
|
provider: z.ZodString;
|
|
14
14
|
confidence: z.ZodEnum<{
|
|
15
|
-
verified: "verified";
|
|
16
15
|
estimated: "estimated";
|
|
16
|
+
verified: "verified";
|
|
17
17
|
detected_unverified: "detected_unverified";
|
|
18
18
|
missing: "missing";
|
|
19
19
|
}>;
|
|
20
20
|
observedFrom: z.ZodString;
|
|
21
|
+
account: z.ZodOptional<z.ZodString>;
|
|
21
22
|
}, z.core.$strip>;
|
|
22
23
|
export type SpendSource = z.infer<typeof spendSourceSchema>;
|
|
23
24
|
/**
|
|
@@ -30,9 +31,9 @@ export type SpendSource = z.infer<typeof spendSourceSchema>;
|
|
|
30
31
|
*/
|
|
31
32
|
export declare const usageGranularityValues: readonly ["call", "invocation", "session", "daily_aggregate", "usage_bucket", "billing_bucket", "seat", "user_aggregate"];
|
|
32
33
|
export declare const usageGranularitySchema: z.ZodEnum<{
|
|
34
|
+
session: "session";
|
|
33
35
|
call: "call";
|
|
34
36
|
invocation: "invocation";
|
|
35
|
-
session: "session";
|
|
36
37
|
daily_aggregate: "daily_aggregate";
|
|
37
38
|
usage_bucket: "usage_bucket";
|
|
38
39
|
billing_bucket: "billing_bucket";
|
|
@@ -59,12 +60,13 @@ export declare const usageRecordSchema: z.ZodObject<{
|
|
|
59
60
|
name: z.ZodString;
|
|
60
61
|
provider: z.ZodString;
|
|
61
62
|
confidence: z.ZodEnum<{
|
|
62
|
-
verified: "verified";
|
|
63
63
|
estimated: "estimated";
|
|
64
|
+
verified: "verified";
|
|
64
65
|
detected_unverified: "detected_unverified";
|
|
65
66
|
missing: "missing";
|
|
66
67
|
}>;
|
|
67
68
|
observedFrom: z.ZodString;
|
|
69
|
+
account: z.ZodOptional<z.ZodString>;
|
|
68
70
|
}, z.core.$strip>;
|
|
69
71
|
model: z.ZodString;
|
|
70
72
|
inputTokens: z.ZodNumber;
|
|
@@ -89,8 +91,8 @@ export declare const usageRecordSchema: z.ZodObject<{
|
|
|
89
91
|
sourceVersions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
90
92
|
amountUsd: z.ZodNullable<z.ZodNumber>;
|
|
91
93
|
costConfidence: z.ZodEnum<{
|
|
92
|
-
verified: "verified";
|
|
93
94
|
estimated: "estimated";
|
|
95
|
+
verified: "verified";
|
|
94
96
|
detected_unverified: "detected_unverified";
|
|
95
97
|
missing: "missing";
|
|
96
98
|
}>;
|
|
@@ -104,9 +106,9 @@ export declare const usageRecordSchema: z.ZodObject<{
|
|
|
104
106
|
agentId: z.ZodOptional<z.ZodString>;
|
|
105
107
|
operation: z.ZodOptional<z.ZodString>;
|
|
106
108
|
usageGranularity: z.ZodOptional<z.ZodEnum<{
|
|
109
|
+
session: "session";
|
|
107
110
|
call: "call";
|
|
108
111
|
invocation: "invocation";
|
|
109
|
-
session: "session";
|
|
110
112
|
daily_aggregate: "daily_aggregate";
|
|
111
113
|
usage_bucket: "usage_bucket";
|
|
112
114
|
billing_bucket: "billing_bucket";
|
|
@@ -152,10 +154,10 @@ export declare function downgradeSampleUsageEvidence(records: UsageRecord[]): Us
|
|
|
152
154
|
export declare function spendComparisonKey(record: UsageRecord): string | undefined;
|
|
153
155
|
export declare const attributionCandidateSchema: z.ZodObject<{
|
|
154
156
|
entityType: z.ZodEnum<{
|
|
155
|
-
|
|
157
|
+
user: "user";
|
|
156
158
|
project: "project";
|
|
157
159
|
agent: "agent";
|
|
158
|
-
|
|
160
|
+
client: "client";
|
|
159
161
|
workspace: "workspace";
|
|
160
162
|
api_key: "api_key";
|
|
161
163
|
}>;
|
|
@@ -168,10 +170,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
168
170
|
usageRecordId: z.ZodString;
|
|
169
171
|
candidates: z.ZodArray<z.ZodObject<{
|
|
170
172
|
entityType: z.ZodEnum<{
|
|
171
|
-
|
|
173
|
+
user: "user";
|
|
172
174
|
project: "project";
|
|
173
175
|
agent: "agent";
|
|
174
|
-
|
|
176
|
+
client: "client";
|
|
175
177
|
workspace: "workspace";
|
|
176
178
|
api_key: "api_key";
|
|
177
179
|
}>;
|
|
@@ -181,10 +183,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
181
183
|
}, z.core.$strip>>;
|
|
182
184
|
selected: z.ZodOptional<z.ZodObject<{
|
|
183
185
|
entityType: z.ZodEnum<{
|
|
184
|
-
|
|
186
|
+
user: "user";
|
|
185
187
|
project: "project";
|
|
186
188
|
agent: "agent";
|
|
187
|
-
|
|
189
|
+
client: "client";
|
|
188
190
|
workspace: "workspace";
|
|
189
191
|
api_key: "api_key";
|
|
190
192
|
}>;
|
|
@@ -206,8 +208,8 @@ export declare const spendBreakdownEntrySchema: z.ZodObject<{
|
|
|
206
208
|
amountUsd: z.ZodNumber;
|
|
207
209
|
recordCount: z.ZodNumber;
|
|
208
210
|
confidence: z.ZodEnum<{
|
|
209
|
-
verified: "verified";
|
|
210
211
|
estimated: "estimated";
|
|
212
|
+
verified: "verified";
|
|
211
213
|
detected_unverified: "detected_unverified";
|
|
212
214
|
missing: "missing";
|
|
213
215
|
}>;
|
|
@@ -224,8 +226,8 @@ export declare const spendAnomalySchema: z.ZodObject<{
|
|
|
224
226
|
currentAmountUsd: z.ZodNumber;
|
|
225
227
|
multiplier: z.ZodNumber;
|
|
226
228
|
confidence: z.ZodEnum<{
|
|
227
|
-
verified: "verified";
|
|
228
229
|
estimated: "estimated";
|
|
230
|
+
verified: "verified";
|
|
229
231
|
detected_unverified: "detected_unverified";
|
|
230
232
|
missing: "missing";
|
|
231
233
|
}>;
|
|
@@ -241,8 +243,8 @@ export declare const workflowWatchEntrySchema: z.ZodObject<{
|
|
|
241
243
|
shareOfSpend: z.ZodNumber;
|
|
242
244
|
recordCount: z.ZodNumber;
|
|
243
245
|
confidence: z.ZodEnum<{
|
|
244
|
-
verified: "verified";
|
|
245
246
|
estimated: "estimated";
|
|
247
|
+
verified: "verified";
|
|
246
248
|
detected_unverified: "detected_unverified";
|
|
247
249
|
missing: "missing";
|
|
248
250
|
}>;
|
|
@@ -266,8 +268,8 @@ export declare const recommendationSchema: z.ZodObject<{
|
|
|
266
268
|
}>;
|
|
267
269
|
estimatedImpactUsd: z.ZodNumber;
|
|
268
270
|
confidence: z.ZodEnum<{
|
|
269
|
-
verified: "verified";
|
|
270
271
|
estimated: "estimated";
|
|
272
|
+
verified: "verified";
|
|
271
273
|
detected_unverified: "detected_unverified";
|
|
272
274
|
missing: "missing";
|
|
273
275
|
}>;
|
|
@@ -311,8 +313,8 @@ export declare const spendInsightSchema: z.ZodObject<{
|
|
|
311
313
|
affectedModels: z.ZodArray<z.ZodString>;
|
|
312
314
|
estimatedImpactUsd: z.ZodNumber;
|
|
313
315
|
confidence: z.ZodEnum<{
|
|
314
|
-
verified: "verified";
|
|
315
316
|
estimated: "estimated";
|
|
317
|
+
verified: "verified";
|
|
316
318
|
detected_unverified: "detected_unverified";
|
|
317
319
|
missing: "missing";
|
|
318
320
|
}>;
|
|
@@ -324,14 +326,14 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
324
326
|
totalUsd: z.ZodNumber;
|
|
325
327
|
recordCount: z.ZodNumber;
|
|
326
328
|
confidence: z.ZodEnum<{
|
|
327
|
-
verified: "verified";
|
|
328
329
|
estimated: "estimated";
|
|
330
|
+
verified: "verified";
|
|
329
331
|
detected_unverified: "detected_unverified";
|
|
330
332
|
missing: "missing";
|
|
331
333
|
}>;
|
|
332
334
|
confidenceBreakdown: z.ZodRecord<z.ZodEnum<{
|
|
333
|
-
verified: "verified";
|
|
334
335
|
estimated: "estimated";
|
|
336
|
+
verified: "verified";
|
|
335
337
|
detected_unverified: "detected_unverified";
|
|
336
338
|
missing: "missing";
|
|
337
339
|
}>, z.ZodNumber>;
|
|
@@ -340,8 +342,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
340
342
|
amountUsd: z.ZodNumber;
|
|
341
343
|
recordCount: z.ZodNumber;
|
|
342
344
|
confidence: z.ZodEnum<{
|
|
343
|
-
verified: "verified";
|
|
344
345
|
estimated: "estimated";
|
|
346
|
+
verified: "verified";
|
|
345
347
|
detected_unverified: "detected_unverified";
|
|
346
348
|
missing: "missing";
|
|
347
349
|
}>;
|
|
@@ -351,8 +353,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
351
353
|
amountUsd: z.ZodNumber;
|
|
352
354
|
recordCount: z.ZodNumber;
|
|
353
355
|
confidence: z.ZodEnum<{
|
|
354
|
-
verified: "verified";
|
|
355
356
|
estimated: "estimated";
|
|
357
|
+
verified: "verified";
|
|
356
358
|
detected_unverified: "detected_unverified";
|
|
357
359
|
missing: "missing";
|
|
358
360
|
}>;
|
|
@@ -362,8 +364,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
362
364
|
amountUsd: z.ZodNumber;
|
|
363
365
|
recordCount: z.ZodNumber;
|
|
364
366
|
confidence: z.ZodEnum<{
|
|
365
|
-
verified: "verified";
|
|
366
367
|
estimated: "estimated";
|
|
368
|
+
verified: "verified";
|
|
367
369
|
detected_unverified: "detected_unverified";
|
|
368
370
|
missing: "missing";
|
|
369
371
|
}>;
|
|
@@ -373,8 +375,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
373
375
|
amountUsd: z.ZodNumber;
|
|
374
376
|
recordCount: z.ZodNumber;
|
|
375
377
|
confidence: z.ZodEnum<{
|
|
376
|
-
verified: "verified";
|
|
377
378
|
estimated: "estimated";
|
|
379
|
+
verified: "verified";
|
|
378
380
|
detected_unverified: "detected_unverified";
|
|
379
381
|
missing: "missing";
|
|
380
382
|
}>;
|
|
@@ -384,8 +386,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
384
386
|
amountUsd: z.ZodNumber;
|
|
385
387
|
recordCount: z.ZodNumber;
|
|
386
388
|
confidence: z.ZodEnum<{
|
|
387
|
-
verified: "verified";
|
|
388
389
|
estimated: "estimated";
|
|
390
|
+
verified: "verified";
|
|
389
391
|
detected_unverified: "detected_unverified";
|
|
390
392
|
missing: "missing";
|
|
391
393
|
}>;
|
|
@@ -395,8 +397,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
395
397
|
amountUsd: z.ZodNumber;
|
|
396
398
|
recordCount: z.ZodNumber;
|
|
397
399
|
confidence: z.ZodEnum<{
|
|
398
|
-
verified: "verified";
|
|
399
400
|
estimated: "estimated";
|
|
401
|
+
verified: "verified";
|
|
400
402
|
detected_unverified: "detected_unverified";
|
|
401
403
|
missing: "missing";
|
|
402
404
|
}>;
|
|
@@ -406,8 +408,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
406
408
|
amountUsd: z.ZodNumber;
|
|
407
409
|
recordCount: z.ZodNumber;
|
|
408
410
|
confidence: z.ZodEnum<{
|
|
409
|
-
verified: "verified";
|
|
410
411
|
estimated: "estimated";
|
|
412
|
+
verified: "verified";
|
|
411
413
|
detected_unverified: "detected_unverified";
|
|
412
414
|
missing: "missing";
|
|
413
415
|
}>;
|
|
@@ -417,8 +419,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
417
419
|
amountUsd: z.ZodNumber;
|
|
418
420
|
recordCount: z.ZodNumber;
|
|
419
421
|
confidence: z.ZodEnum<{
|
|
420
|
-
verified: "verified";
|
|
421
422
|
estimated: "estimated";
|
|
423
|
+
verified: "verified";
|
|
422
424
|
detected_unverified: "detected_unverified";
|
|
423
425
|
missing: "missing";
|
|
424
426
|
}>;
|
|
@@ -433,8 +435,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
433
435
|
shareOfSpend: z.ZodNumber;
|
|
434
436
|
recordCount: z.ZodNumber;
|
|
435
437
|
confidence: z.ZodEnum<{
|
|
436
|
-
verified: "verified";
|
|
437
438
|
estimated: "estimated";
|
|
439
|
+
verified: "verified";
|
|
438
440
|
detected_unverified: "detected_unverified";
|
|
439
441
|
missing: "missing";
|
|
440
442
|
}>;
|
|
@@ -455,8 +457,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
455
457
|
currentAmountUsd: z.ZodNumber;
|
|
456
458
|
multiplier: z.ZodNumber;
|
|
457
459
|
confidence: z.ZodEnum<{
|
|
458
|
-
verified: "verified";
|
|
459
460
|
estimated: "estimated";
|
|
461
|
+
verified: "verified";
|
|
460
462
|
detected_unverified: "detected_unverified";
|
|
461
463
|
missing: "missing";
|
|
462
464
|
}>;
|
|
@@ -474,8 +476,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
474
476
|
}>;
|
|
475
477
|
estimatedImpactUsd: z.ZodNumber;
|
|
476
478
|
confidence: z.ZodEnum<{
|
|
477
|
-
verified: "verified";
|
|
478
479
|
estimated: "estimated";
|
|
480
|
+
verified: "verified";
|
|
479
481
|
detected_unverified: "detected_unverified";
|
|
480
482
|
missing: "missing";
|
|
481
483
|
}>;
|
|
@@ -512,8 +514,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
512
514
|
affectedModels: z.ZodArray<z.ZodString>;
|
|
513
515
|
estimatedImpactUsd: z.ZodNumber;
|
|
514
516
|
confidence: z.ZodEnum<{
|
|
515
|
-
verified: "verified";
|
|
516
517
|
estimated: "estimated";
|
|
518
|
+
verified: "verified";
|
|
517
519
|
detected_unverified: "detected_unverified";
|
|
518
520
|
missing: "missing";
|
|
519
521
|
}>;
|
package/dist/schema.js
CHANGED
|
@@ -11,7 +11,15 @@ export const spendSourceSchema = z.object({
|
|
|
11
11
|
name: z.string().min(1),
|
|
12
12
|
provider: z.string().min(1),
|
|
13
13
|
confidence: costConfidenceSchema,
|
|
14
|
-
observedFrom: z.string().min(1)
|
|
14
|
+
observedFrom: z.string().min(1),
|
|
15
|
+
/**
|
|
16
|
+
* Stable per-account identity within one provider (an organization, team,
|
|
17
|
+
* or enterprise slice). Derived from the user-chosen credential reference
|
|
18
|
+
* or an explicit --org/--enterprise/--account-id flag — never from secret
|
|
19
|
+
* material. Absent on local-agent records and on provider records synced
|
|
20
|
+
* before multi-account support (treated as one unnamed legacy slice).
|
|
21
|
+
*/
|
|
22
|
+
account: z.string().min(1).optional()
|
|
15
23
|
});
|
|
16
24
|
/**
|
|
17
25
|
* What one normalized usage record represents.
|