@company-semantics/contracts 79.2.0 → 81.0.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/package.json +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +42 -33
- package/src/index.ts +2 -7
- package/src/org/README.md +1 -0
- package/src/org/schemas.ts +53 -48
- package/src/source-sync/README.md +6 -3
- package/src/source-sync/__tests__/schemas.test.ts +16 -1
- package/src/source-sync/candidate-totals.ts +42 -0
- package/src/source-sync/index.ts +5 -0
- package/src/source-sync/schemas.ts +25 -1
- package/src/usage/features.ts +56 -0
- package/src/usage/types.ts +5 -48
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '7e0656a03459' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '7e0656a03459e90e26051fe9aea403e95f0de11fea465f6a0f00ae5caf8b0aa7' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -7438,50 +7438,55 @@ export interface components {
|
|
|
7438
7438
|
};
|
|
7439
7439
|
OrgUsageResponse: {
|
|
7440
7440
|
period: {
|
|
7441
|
+
/** Format: date-time */
|
|
7441
7442
|
start: string;
|
|
7443
|
+
/** Format: date-time */
|
|
7442
7444
|
end: string;
|
|
7445
|
+
timezone: string;
|
|
7443
7446
|
};
|
|
7444
7447
|
summary: {
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
avgDurationMs: number;
|
|
7449
|
-
failureRate: number;
|
|
7448
|
+
requests: number;
|
|
7449
|
+
tokens: number;
|
|
7450
|
+
customerChargeMicros: string;
|
|
7450
7451
|
};
|
|
7451
7452
|
daily: {
|
|
7452
7453
|
date: string;
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
}[];
|
|
7456
|
-
byProfile: {
|
|
7457
|
-
profile: string;
|
|
7458
|
-
executions: number;
|
|
7459
|
-
percentOfTotal: number;
|
|
7460
|
-
totalCostUsd: string;
|
|
7461
|
-
avgDurationMs: number;
|
|
7462
|
-
failureRate: number;
|
|
7454
|
+
requests: number;
|
|
7455
|
+
customerChargeMicros: string;
|
|
7463
7456
|
}[];
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
favoriteProfile: string;
|
|
7457
|
+
byFeature: {
|
|
7458
|
+
requests: number;
|
|
7459
|
+
tokens: number;
|
|
7460
|
+
customerChargeMicros: string;
|
|
7461
|
+
/** @enum {string} */
|
|
7462
|
+
feature: "chat" | "chat_title" | "ingest_summarize" | "ingest_embedding" | "company_md_extraction" | "org_chart_import" | "org_structure_inference" | "source_sync_discovery" | "source_sync_digest" | "source_sync_synthesis" | "comment_agent_reply" | "slack_mention_reply" | "structure_review_interpret" | "retrieval_embedding" | "company_md_embedding" | "meeting_embedding" | "unattributed";
|
|
7471
7463
|
}[];
|
|
7472
7464
|
byModel: {
|
|
7473
|
-
|
|
7465
|
+
requests: number;
|
|
7466
|
+
tokens: number;
|
|
7467
|
+
customerChargeMicros: string;
|
|
7474
7468
|
provider: string;
|
|
7475
|
-
|
|
7476
|
-
estimatedCostUsd: string;
|
|
7477
|
-
requestCount: number;
|
|
7469
|
+
modelId: string;
|
|
7478
7470
|
}[];
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7471
|
+
byUser: {
|
|
7472
|
+
requests: number;
|
|
7473
|
+
tokens: number;
|
|
7474
|
+
customerChargeMicros: string;
|
|
7475
|
+
userId: string;
|
|
7476
|
+
email: string | null;
|
|
7484
7477
|
}[];
|
|
7478
|
+
chatRuns: {
|
|
7479
|
+
executions: number;
|
|
7480
|
+
avgDurationMs: number;
|
|
7481
|
+
failureRate: number;
|
|
7482
|
+
byProfile: {
|
|
7483
|
+
profile: string;
|
|
7484
|
+
executions: number;
|
|
7485
|
+
percentOfTotal: number;
|
|
7486
|
+
avgDurationMs: number;
|
|
7487
|
+
failureRate: number;
|
|
7488
|
+
}[];
|
|
7489
|
+
};
|
|
7485
7490
|
};
|
|
7486
7491
|
AccountDeletionEligibilityResponse: {
|
|
7487
7492
|
eligible: boolean;
|
|
@@ -8653,6 +8658,7 @@ export interface components {
|
|
|
8653
8658
|
/** @enum {string} */
|
|
8654
8659
|
recommendedUse: "attached" | "direction_only" | "excluded";
|
|
8655
8660
|
use: ("attached" | "direction_only" | "excluded") | null;
|
|
8661
|
+
included: boolean;
|
|
8656
8662
|
treatmentUnresolved: boolean;
|
|
8657
8663
|
matchReason: string;
|
|
8658
8664
|
unreadableReason: ("unsupported_type" | "needs_ocr" | "unsupported_kind") | null;
|
|
@@ -8690,7 +8696,11 @@ export interface components {
|
|
|
8690
8696
|
};
|
|
8691
8697
|
}[];
|
|
8692
8698
|
withheldQuestionCount: number;
|
|
8693
|
-
|
|
8699
|
+
totals: {
|
|
8700
|
+
files: number;
|
|
8701
|
+
filesIncluded: number;
|
|
8702
|
+
filesNeedingChoice: number;
|
|
8703
|
+
};
|
|
8694
8704
|
scopes: {
|
|
8695
8705
|
scopeKey: string;
|
|
8696
8706
|
/** @enum {string} */
|
|
@@ -14807,7 +14817,6 @@ export interface operations {
|
|
|
14807
14817
|
parameters: {
|
|
14808
14818
|
query?: {
|
|
14809
14819
|
period?: "current_month" | "last_30_days" | "last_7_days";
|
|
14810
|
-
tz?: string;
|
|
14811
14820
|
};
|
|
14812
14821
|
header?: never;
|
|
14813
14822
|
path: {
|
package/src/index.ts
CHANGED
|
@@ -1298,15 +1298,10 @@ export type {
|
|
|
1298
1298
|
ImpersonationSseEvent,
|
|
1299
1299
|
} from "./impersonation-events";
|
|
1300
1300
|
|
|
1301
|
-
//
|
|
1302
|
-
|
|
1301
|
+
// AI features (INV-AI-ATTRIBUTION) and the internal-admin telemetry usage types
|
|
1302
|
+
export { AI_FEATURES, AI_FEATURE_LABELS } from "./usage/features";
|
|
1303
1303
|
export type {
|
|
1304
1304
|
AiFeature,
|
|
1305
|
-
UsageSummary,
|
|
1306
|
-
DailyUsage,
|
|
1307
|
-
UsageByModel,
|
|
1308
|
-
UsageByFeature,
|
|
1309
|
-
UsageByUser,
|
|
1310
1305
|
// Unified usage types (PRD-00276/277)
|
|
1311
1306
|
UnifiedUsageSummary,
|
|
1312
1307
|
UnifiedDailyUsage,
|
package/src/org/README.md
CHANGED
package/src/org/schemas.ts
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { CursorPageSchema, IsoDateTime } from "../api/primitives";
|
|
15
|
-
import { AI_PAUSE_REASONS } from "../billing/vocabulary";
|
|
15
|
+
import { AI_PAUSE_REASONS, MoneyMicrosSchema } from "../billing/vocabulary";
|
|
16
|
+
import { AI_FEATURES } from "../usage/features";
|
|
16
17
|
import { OrgChartRoleSchema } from "../permissions/orgchart-roles";
|
|
17
18
|
import { PositionRefSchema } from "../identity/position-ref";
|
|
18
19
|
import {
|
|
@@ -1385,64 +1386,68 @@ export type AcknowledgeSystemEventResponse = z.infer<
|
|
|
1385
1386
|
|
|
1386
1387
|
// ---------------------------------------------------------------------------
|
|
1387
1388
|
// GET /api/orgs/:orgId/ai-usage
|
|
1388
|
-
//
|
|
1389
|
+
//
|
|
1390
|
+
// INV-USAGE-LEDGER: every request, token and dollar here is read from the
|
|
1391
|
+
// settled billing ledger (`billing_usage_events`) — one row per billed provider
|
|
1392
|
+
// call, whatever feature made it. A `request` is one settled billable provider
|
|
1393
|
+
// invocation (retries and fallbacks are separate requests), every amount is
|
|
1394
|
+
// the customer charge in integer micro-USD, and `tokens` is input + output +
|
|
1395
|
+
// cache read + cache write (reasoning is a subset of output, never added).
|
|
1396
|
+
// `chatRuns` alone comes from runtime telemetry: duration and failure
|
|
1397
|
+
// diagnostics for chat turns, carrying no money.
|
|
1389
1398
|
// ---------------------------------------------------------------------------
|
|
1390
1399
|
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
avgDurationMs: z.number(),
|
|
1396
|
-
failureRate: z.number(),
|
|
1397
|
-
});
|
|
1398
|
-
|
|
1399
|
-
const UnifiedDailyUsageSchema = z.object({
|
|
1400
|
-
date: z.string(),
|
|
1401
|
-
executions: z.number(),
|
|
1402
|
-
totalCostUsd: z.string(),
|
|
1400
|
+
const OrgAiUsageTotalsSchema = z.object({
|
|
1401
|
+
requests: z.number().int().min(0),
|
|
1402
|
+
tokens: z.number().int().min(0),
|
|
1403
|
+
customerChargeMicros: MoneyMicrosSchema,
|
|
1403
1404
|
});
|
|
1404
1405
|
|
|
1405
|
-
const
|
|
1406
|
+
const OrgChatRunProfileSchema = z.object({
|
|
1406
1407
|
profile: z.string(),
|
|
1407
|
-
executions: z.number(),
|
|
1408
|
+
executions: z.number().int().min(0),
|
|
1408
1409
|
percentOfTotal: z.number(),
|
|
1409
|
-
totalCostUsd: z.string(),
|
|
1410
1410
|
avgDurationMs: z.number(),
|
|
1411
1411
|
failureRate: z.number(),
|
|
1412
1412
|
});
|
|
1413
1413
|
|
|
1414
|
-
const UnifiedUserUsageSchema = z.object({
|
|
1415
|
-
userId: z.string(),
|
|
1416
|
-
email: z.string(),
|
|
1417
|
-
executions: z.number(),
|
|
1418
|
-
totalCostUsd: z.string(),
|
|
1419
|
-
totalTokens: z.number(),
|
|
1420
|
-
favoriteProfile: z.string(),
|
|
1421
|
-
});
|
|
1422
|
-
|
|
1423
|
-
const UnifiedModelUsageSchema = z.object({
|
|
1424
|
-
model: z.string(),
|
|
1425
|
-
provider: z.string(),
|
|
1426
|
-
totalTokens: z.number(),
|
|
1427
|
-
estimatedCostUsd: z.string(),
|
|
1428
|
-
requestCount: z.number(),
|
|
1429
|
-
});
|
|
1430
|
-
|
|
1431
|
-
const UnifiedFeatureUsageSchema = z.object({
|
|
1432
|
-
feature: z.string(),
|
|
1433
|
-
totalTokens: z.number(),
|
|
1434
|
-
estimatedCostUsd: z.string(),
|
|
1435
|
-
requestCount: z.number(),
|
|
1436
|
-
});
|
|
1437
|
-
|
|
1438
1414
|
export const OrgUsageResponseSchema = z.object({
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1415
|
+
/** The org's billing window: `start`/`end` in UTC, days bucketed in `timezone`. */
|
|
1416
|
+
period: z.object({
|
|
1417
|
+
start: IsoDateTime,
|
|
1418
|
+
end: IsoDateTime,
|
|
1419
|
+
timezone: z.string(),
|
|
1420
|
+
}),
|
|
1421
|
+
summary: OrgAiUsageTotalsSchema,
|
|
1422
|
+
daily: z.array(
|
|
1423
|
+
z.object({
|
|
1424
|
+
date: z.string(),
|
|
1425
|
+
requests: z.number().int().min(0),
|
|
1426
|
+
customerChargeMicros: MoneyMicrosSchema,
|
|
1427
|
+
}),
|
|
1428
|
+
),
|
|
1429
|
+
byFeature: z.array(
|
|
1430
|
+
OrgAiUsageTotalsSchema.extend({ feature: z.enum(AI_FEATURES) }),
|
|
1431
|
+
),
|
|
1432
|
+
byModel: z.array(
|
|
1433
|
+
OrgAiUsageTotalsSchema.extend({
|
|
1434
|
+
provider: z.string(),
|
|
1435
|
+
modelId: z.string(),
|
|
1436
|
+
}),
|
|
1437
|
+
),
|
|
1438
|
+
/** Attributed usage only: system work (no user) counts in the totals, not here. */
|
|
1439
|
+
byUser: z.array(
|
|
1440
|
+
OrgAiUsageTotalsSchema.extend({
|
|
1441
|
+
userId: z.string(),
|
|
1442
|
+
email: z.string().nullable(),
|
|
1443
|
+
}),
|
|
1444
|
+
),
|
|
1445
|
+
chatRuns: z.object({
|
|
1446
|
+
executions: z.number().int().min(0),
|
|
1447
|
+
avgDurationMs: z.number(),
|
|
1448
|
+
failureRate: z.number(),
|
|
1449
|
+
byProfile: z.array(OrgChatRunProfileSchema),
|
|
1450
|
+
}),
|
|
1446
1451
|
});
|
|
1447
1452
|
|
|
1448
1453
|
export type OrgUsageResponse = z.infer<typeof OrgUsageResponseSchema>;
|
|
@@ -127,10 +127,13 @@ way. This directory holds that shared spelling.
|
|
|
127
127
|
- `SourceSyncDiscoveryRunResponseSchema` / `SourceSyncDiscoveryRunResponse` —
|
|
128
128
|
the discovery run started, and the workflow narrating it.
|
|
129
129
|
- `SourceSyncCandidateItemSchema` / `SourceSyncCandidateItem` — one source
|
|
130
|
-
proposed for one target document
|
|
130
|
+
proposed for one target document, with the server's `included`.
|
|
131
131
|
- `SourceSyncCandidateSetResponseSchema` / `SourceSyncCandidateSetResponse` —
|
|
132
|
-
the draft set under review: its revision, items, presented questions
|
|
133
|
-
cost estimate.
|
|
132
|
+
the draft set under review: its revision, items, presented questions,
|
|
133
|
+
distinct-file `totals` and cost estimate.
|
|
134
|
+
- `SourceSyncCandidateTotalsSchema` / `SourceSyncCandidateTotals` and
|
|
135
|
+
`candidateTotalsOf(items)` — THE file count: distinct `sourceId`, never lines
|
|
136
|
+
(ADR-CONTRACTS-195).
|
|
134
137
|
- `SourceSyncCandidateItemPatchSchema` / `SourceSyncCandidateItemPatch` — one
|
|
135
138
|
edit to one candidate item (use, class or target); never empty.
|
|
136
139
|
- `SourceSyncAddCandidateRequestSchema` / `SourceSyncAddCandidateRequest` —
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { describe, expect, it } from "vitest";
|
|
17
17
|
import {
|
|
18
|
+
candidateTotalsOf,
|
|
18
19
|
SourceSyncCandidateItemSchema,
|
|
19
20
|
SourceSyncCandidateSetResponseSchema,
|
|
20
21
|
SourceSyncDiscoveryRunResponseSchema,
|
|
@@ -50,6 +51,7 @@ function candidateItem(overrides: Record<string, unknown> = {}) {
|
|
|
50
51
|
audienceFit: "fits",
|
|
51
52
|
recommendedUse: "direction_only",
|
|
52
53
|
use: null,
|
|
54
|
+
included: true,
|
|
53
55
|
treatmentUnresolved: false,
|
|
54
56
|
matchReason: "A board deck for the current quarter.",
|
|
55
57
|
unreadableReason: null,
|
|
@@ -96,7 +98,7 @@ function candidateSet(overrides: Record<string, unknown> = {}) {
|
|
|
96
98
|
items: [candidateItem()],
|
|
97
99
|
questions: [presentedQuestion()],
|
|
98
100
|
withheldQuestionCount: 0,
|
|
99
|
-
|
|
101
|
+
totals: { files: 1, filesIncluded: 1, filesNeedingChoice: 0 },
|
|
100
102
|
scopes: [],
|
|
101
103
|
estimate: {
|
|
102
104
|
lowMicroUsd: "250000",
|
|
@@ -279,3 +281,16 @@ describe("SourceSyncDiscoveryRunResponseSchema", () => {
|
|
|
279
281
|
).toBe(true);
|
|
280
282
|
});
|
|
281
283
|
});
|
|
284
|
+
|
|
285
|
+
describe("candidateTotalsOf", () => {
|
|
286
|
+
it("counts distinct files, never lines", () => {
|
|
287
|
+
expect(
|
|
288
|
+
candidateTotalsOf([
|
|
289
|
+
{ sourceId: "a", included: true, treatmentUnresolved: true },
|
|
290
|
+
{ sourceId: "a", included: true, treatmentUnresolved: false },
|
|
291
|
+
{ sourceId: "b", included: false, treatmentUnresolved: true },
|
|
292
|
+
{ sourceId: "c", included: true, treatmentUnresolved: false },
|
|
293
|
+
]),
|
|
294
|
+
).toEqual({ files: 3, filesIncluded: 2, filesNeedingChoice: 1 });
|
|
295
|
+
});
|
|
296
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE ONE FILE COUNT for a candidate review: distinct `sourceId`, never lines.
|
|
3
|
+
*
|
|
4
|
+
* A file routed to two company.md documents is two lines and ONE file. The
|
|
5
|
+
* server computes `SourceSyncCandidateSetResponse.totals` with this over every
|
|
6
|
+
* line the reader may see; a client that overlays an optimistic edit recomputes
|
|
7
|
+
* with the same function, so no surface counts "files" its own way.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
SourceSyncCandidateItem,
|
|
12
|
+
SourceSyncCandidateTotals,
|
|
13
|
+
} from "./schemas";
|
|
14
|
+
|
|
15
|
+
/** The item facts the count reads. */
|
|
16
|
+
export type CandidateTotalsInput = Pick<
|
|
17
|
+
SourceSyncCandidateItem,
|
|
18
|
+
"sourceId" | "included" | "treatmentUnresolved"
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Included when any of the file's lines is; in need of a choice when any
|
|
23
|
+
* included line's treatment is unresolved.
|
|
24
|
+
*/
|
|
25
|
+
export function candidateTotalsOf(
|
|
26
|
+
items: readonly CandidateTotalsInput[],
|
|
27
|
+
): SourceSyncCandidateTotals {
|
|
28
|
+
const files = new Set<string>();
|
|
29
|
+
const included = new Set<string>();
|
|
30
|
+
const needingChoice = new Set<string>();
|
|
31
|
+
for (const item of items) {
|
|
32
|
+
files.add(item.sourceId);
|
|
33
|
+
if (!item.included) continue;
|
|
34
|
+
included.add(item.sourceId);
|
|
35
|
+
if (item.treatmentUnresolved) needingChoice.add(item.sourceId);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
files: files.size,
|
|
39
|
+
filesIncluded: included.size,
|
|
40
|
+
filesNeedingChoice: needingChoice.size,
|
|
41
|
+
};
|
|
42
|
+
}
|
package/src/source-sync/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ export type {
|
|
|
48
48
|
SourceSyncDiscoveryRunResponse,
|
|
49
49
|
SourceSyncCandidateItem,
|
|
50
50
|
SourceSyncCandidateSetResponse,
|
|
51
|
+
SourceSyncCandidateTotals,
|
|
51
52
|
SourceSyncCandidateItemPatch,
|
|
52
53
|
SourceSyncAddCandidateRequest,
|
|
53
54
|
SourceSyncApproveRequest,
|
|
@@ -69,6 +70,7 @@ export {
|
|
|
69
70
|
SourceSyncDiscoveryRunResponseSchema,
|
|
70
71
|
SourceSyncCandidateItemSchema,
|
|
71
72
|
SourceSyncCandidateSetResponseSchema,
|
|
73
|
+
SourceSyncCandidateTotalsSchema,
|
|
72
74
|
SourceSyncCandidateItemPatchSchema,
|
|
73
75
|
SourceSyncAddCandidateRequestSchema,
|
|
74
76
|
SourceSyncApproveRequestSchema,
|
|
@@ -139,3 +141,6 @@ export {
|
|
|
139
141
|
SourceSyncReachUpgradeRequestSchema,
|
|
140
142
|
SourceSyncReachUpgradeResponseSchema,
|
|
141
143
|
} from "./reach";
|
|
144
|
+
|
|
145
|
+
export type { CandidateTotalsInput } from "./candidate-totals";
|
|
146
|
+
export { candidateTotalsOf } from "./candidate-totals";
|
|
@@ -118,6 +118,12 @@ export const SourceSyncCandidateItemSchema = z.object({
|
|
|
118
118
|
recommendedUse: z.enum(SOURCE_USES),
|
|
119
119
|
/** What the person has chosen; null until they (or an accepted default) decide. */
|
|
120
120
|
use: z.enum(SOURCE_USES).nullable(),
|
|
121
|
+
/**
|
|
122
|
+
* Whether this line is in the set that approval would land — the SERVER's
|
|
123
|
+
* answer, never derived from `use`. A decoy, a series' history or a line a
|
|
124
|
+
* person left out is false while its `use` is still null.
|
|
125
|
+
*/
|
|
126
|
+
included: z.boolean(),
|
|
121
127
|
/** True while a `widens` item — or one whose fit is not computed yet — has no person-chosen use: approval is refused until none remain. */
|
|
122
128
|
treatmentUnresolved: z.boolean(),
|
|
123
129
|
/** Product-owned copy saying why this file was proposed. Never model-authored prose. */
|
|
@@ -128,6 +134,16 @@ export const SourceSyncCandidateItemSchema = z.object({
|
|
|
128
134
|
.nullable(),
|
|
129
135
|
});
|
|
130
136
|
|
|
137
|
+
/** Distinct-file totals over every line of a candidate set the caller may see. */
|
|
138
|
+
export const SourceSyncCandidateTotalsSchema = z.object({
|
|
139
|
+
/** Files in the review: at least one line, included or left out. */
|
|
140
|
+
files: z.number().int().min(0),
|
|
141
|
+
/** Files with at least one included line. */
|
|
142
|
+
filesIncluded: z.number().int().min(0),
|
|
143
|
+
/** Files with at least one included line whose treatment is unresolved. */
|
|
144
|
+
filesNeedingChoice: z.number().int().min(0),
|
|
145
|
+
});
|
|
146
|
+
|
|
131
147
|
/** The draft set under review. `revision` moves on every edit and is echoed on approve. */
|
|
132
148
|
export const SourceSyncCandidateSetResponseSchema = z.object({
|
|
133
149
|
setId: z.string().uuid(),
|
|
@@ -138,7 +154,12 @@ export const SourceSyncCandidateSetResponseSchema = z.object({
|
|
|
138
154
|
/** The low-confidence routing, class and treatment questions, as presented. */
|
|
139
155
|
questions: z.array(DecisionQuestionPresentationSchema).max(50),
|
|
140
156
|
withheldQuestionCount: z.number().int().min(0),
|
|
141
|
-
|
|
157
|
+
/**
|
|
158
|
+
* The review's top-level totals, counted by DISTINCT FILE (`sourceId`) — never
|
|
159
|
+
* by line. A file routed to two documents is two lines and one file. Every
|
|
160
|
+
* surface reads these rather than counting `items`, which may be capped.
|
|
161
|
+
*/
|
|
162
|
+
totals: SourceSyncCandidateTotalsSchema,
|
|
142
163
|
/** Every scope of the set the caller may see, and where its approval stands. */
|
|
143
164
|
scopes: z.array(SourceSyncScopeApprovalProjectionSchema).max(500),
|
|
144
165
|
/** A RANGE, and the cap that is the promise. Null until the estimator has run. */
|
|
@@ -378,6 +399,9 @@ export type SourceSyncCandidateItem = z.infer<
|
|
|
378
399
|
export type SourceSyncCandidateSetResponse = z.infer<
|
|
379
400
|
typeof SourceSyncCandidateSetResponseSchema
|
|
380
401
|
>;
|
|
402
|
+
export type SourceSyncCandidateTotals = z.infer<
|
|
403
|
+
typeof SourceSyncCandidateTotalsSchema
|
|
404
|
+
>;
|
|
381
405
|
export type SourceSyncCandidateItemPatch = z.infer<
|
|
382
406
|
typeof SourceSyncCandidateItemPatchSchema
|
|
383
407
|
>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI features — what work caused a billed model call (INV-AI-ATTRIBUTION).
|
|
3
|
+
*
|
|
4
|
+
* The CANONICAL list is the backend's Postgres `ai_feature` enum; this mirror
|
|
5
|
+
* is checked against it by a backend type test in both directions, so a value
|
|
6
|
+
* added on one side without the other cannot typecheck. Order follows the
|
|
7
|
+
* enum's (append-only) order.
|
|
8
|
+
*
|
|
9
|
+
* `unattributed` is a failure-containment state, not an accepted feature: a
|
|
10
|
+
* production model call that reached the billing gate with no attribution.
|
|
11
|
+
*/
|
|
12
|
+
export const AI_FEATURES = [
|
|
13
|
+
"chat",
|
|
14
|
+
"chat_title",
|
|
15
|
+
"ingest_summarize",
|
|
16
|
+
"ingest_embedding",
|
|
17
|
+
"company_md_extraction",
|
|
18
|
+
"org_chart_import",
|
|
19
|
+
"org_structure_inference",
|
|
20
|
+
"source_sync_discovery",
|
|
21
|
+
"source_sync_digest",
|
|
22
|
+
"source_sync_synthesis",
|
|
23
|
+
"comment_agent_reply",
|
|
24
|
+
"slack_mention_reply",
|
|
25
|
+
"structure_review_interpret",
|
|
26
|
+
"retrieval_embedding",
|
|
27
|
+
"company_md_embedding",
|
|
28
|
+
"meeting_embedding",
|
|
29
|
+
"unattributed",
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export type AiFeature = (typeof AI_FEATURES)[number];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The one display name per feature, Sentence case. `satisfies` so a new
|
|
36
|
+
* feature cannot render as a raw enum value or go unlabeled.
|
|
37
|
+
*/
|
|
38
|
+
export const AI_FEATURE_LABELS = {
|
|
39
|
+
chat: "Chat",
|
|
40
|
+
chat_title: "Chat titles",
|
|
41
|
+
ingest_summarize: "Slack summaries",
|
|
42
|
+
ingest_embedding: "Slack indexing",
|
|
43
|
+
company_md_extraction: "Document extraction",
|
|
44
|
+
org_chart_import: "Org chart import",
|
|
45
|
+
org_structure_inference: "Org structure analysis",
|
|
46
|
+
source_sync_discovery: "Google Drive search",
|
|
47
|
+
source_sync_digest: "Google Drive file reading",
|
|
48
|
+
source_sync_synthesis: "Google Drive drafting",
|
|
49
|
+
comment_agent_reply: "Comment replies",
|
|
50
|
+
slack_mention_reply: "Slack replies",
|
|
51
|
+
structure_review_interpret: "Org review answers",
|
|
52
|
+
retrieval_embedding: "Search queries",
|
|
53
|
+
company_md_embedding: "Document indexing",
|
|
54
|
+
meeting_embedding: "Meeting indexing",
|
|
55
|
+
unattributed: "Unattributed",
|
|
56
|
+
} as const satisfies Record<AiFeature, string>;
|
package/src/usage/types.ts
CHANGED
|
@@ -1,55 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* AI usage telemetry types — the internal-admin usage views (runtime
|
|
3
|
+
* executions). The org-facing AI usage page reads the billing ledger instead;
|
|
4
|
+
* its shape is `OrgUsageResponseSchema` in `org/schemas.ts` (INV-USAGE-LEDGER).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
| "chat"
|
|
9
|
-
| "chat_title"
|
|
10
|
-
| "ingest_summarize"
|
|
11
|
-
| "ingest_embedding"
|
|
12
|
-
| "company_md_extraction";
|
|
7
|
+
import type { AiFeature } from "./features";
|
|
13
8
|
|
|
14
|
-
export
|
|
15
|
-
totalTokens: number;
|
|
16
|
-
totalInputTokens: number;
|
|
17
|
-
totalOutputTokens: number;
|
|
18
|
-
estimatedCostUsd: string;
|
|
19
|
-
requestCount: number;
|
|
20
|
-
periodStart: string; // UTC ISO8601
|
|
21
|
-
periodEnd: string; // UTC ISO8601
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface DailyUsage {
|
|
25
|
-
date: string; // YYYY-MM-DD
|
|
26
|
-
totalTokens: number;
|
|
27
|
-
estimatedCostUsd: string;
|
|
28
|
-
requestCount: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface UsageByModel {
|
|
32
|
-
model: string;
|
|
33
|
-
provider: string;
|
|
34
|
-
totalTokens: number;
|
|
35
|
-
estimatedCostUsd: string;
|
|
36
|
-
requestCount: number;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface UsageByFeature {
|
|
40
|
-
feature: AiFeature;
|
|
41
|
-
totalTokens: number;
|
|
42
|
-
estimatedCostUsd: string;
|
|
43
|
-
requestCount: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface UsageByUser {
|
|
47
|
-
userId: string;
|
|
48
|
-
email: string;
|
|
49
|
-
totalTokens: number;
|
|
50
|
-
estimatedCostUsd: string;
|
|
51
|
-
requestCount: number;
|
|
52
|
-
}
|
|
9
|
+
export type { AiFeature };
|
|
53
10
|
|
|
54
11
|
// ---------------------------------------------------------------------------
|
|
55
12
|
// Unified Usage Response (PRD-00276/277)
|