@thinkai/tai-api-contract 1.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/src/index.ts ADDED
@@ -0,0 +1,475 @@
1
+ /**
2
+ * API contract — shared DTOs and client types.
3
+ * The ONLY package the frontend may import.
4
+ */
5
+ export type { Score, Confidence } from "@thinkai/pip-core-types";
6
+
7
+ /** Run metadata (window, ruleset, created). M5.4: optional tenantId, personId. */
8
+ export interface RunMetadataDto {
9
+ runId: string;
10
+ windowStart: string;
11
+ windowEnd: string;
12
+ rulesetId: string;
13
+ rulesetVersion: string;
14
+ rulesetHash?: string;
15
+ createdAt?: string;
16
+ tenantId?: string;
17
+ personId?: string;
18
+ }
19
+
20
+ /**
21
+ * Single score result — metrics, dimension scores, UPI, band. M5.6: optional tenantId, personId.
22
+ * upi and dimensionScores are in [0, 1] (remapped from internal [-1, 1] for display).
23
+ * confidence (when present) is evidence-based certainty in [0, 1] for the run and (optionally) dimensions.
24
+ */
25
+ export interface ScoreDto {
26
+ runId: string;
27
+ rulesetId: string;
28
+ rulesetVersion: string;
29
+ windowEnd: string;
30
+ createdAt?: string;
31
+ tenantId?: string;
32
+ personId?: string;
33
+ /** Optional fingerprint of sources config used when this score was computed. */
34
+ sourcesVersion?: string;
35
+ metrics: Record<string, number>;
36
+ dimensionScores: Record<string, number>;
37
+ upi: number;
38
+ band: string;
39
+ /** Scoring confidence (evidence-based). Present when persisted for explainability. */
40
+ confidence?: SnapshotConfidenceDto;
41
+ /**
42
+ * Optional change marker for trend charts.
43
+ * - 'ruleset' when ruleset version/hash changed since previous point
44
+ * - 'sources' when sourcesVersion changed since previous point
45
+ */
46
+ changeMarker?: "ruleset" | "sources";
47
+ }
48
+
49
+ /** Paginated list of scores. For sparklines use ?order=asc&limit=31 so items are chronological (oldest first); plot items[].windowEnd vs items[].upi. */
50
+ export interface ScoresHistoryDto {
51
+ items: ScoreDto[];
52
+ total: number;
53
+ limit: number;
54
+ offset: number;
55
+ }
56
+
57
+ /** Scoring confidence and gates (for explainability). */
58
+ export interface SnapshotConfidenceDto {
59
+ overall: number;
60
+ delivery?: number;
61
+ quality?: number;
62
+ impact?: number;
63
+ collaboration?: number;
64
+ leverage?: number;
65
+ }
66
+
67
+ export interface SnapshotGatesDto {
68
+ dominance: { pass: boolean; maxAbs: number; S_max: number };
69
+ quality: { pass: boolean; qualityScore: number; theta_q: number; constrained: boolean };
70
+ confidence: { pass: boolean; confidenceOverall: number; theta_c: number; suppressed: boolean };
71
+ evidence: { pass: boolean; anyUnknown: boolean; emitIndex: boolean };
72
+ }
73
+
74
+ /**
75
+ * Explain: inputs (event IDs) → rules → outputs.
76
+ * snapshot.upi and snapshot.dimensionScores are in [0, 1] (remapped for display).
77
+ */
78
+ export interface ExplainDto {
79
+ runMetadata: RunMetadataDto;
80
+ snapshot: {
81
+ runId: string;
82
+ rulesetId: string;
83
+ rulesetVersion: string;
84
+ rulesetHash?: string;
85
+ metrics: Record<string, number>;
86
+ dimensionScores: Record<string, number>;
87
+ upi: number;
88
+ band: string;
89
+ /** Expected values used for normalization (m/expected). Present when persisted. */
90
+ expected?: Record<string, number>;
91
+ confidence?: SnapshotConfidenceDto;
92
+ gates?: SnapshotGatesDto;
93
+ };
94
+ eventIds: string[];
95
+ }
96
+
97
+ /**
98
+ * M6: Provenance graph — traceability chain (score → dimensions → metrics → rules → events). No recomputation.
99
+ * upi and dimensionScores are in [0, 1] (remapped for display).
100
+ */
101
+ export interface ProvenanceDto {
102
+ runId: string;
103
+ rulesetId: string;
104
+ rulesetVersion: string;
105
+ rulesetHash?: string;
106
+ windowStart: string;
107
+ windowEnd: string;
108
+ tenantId?: string;
109
+ personId?: string;
110
+ /** Dimension scores (output). */
111
+ dimensionScores: Record<string, number>;
112
+ /** Metrics that fed dimension scores. */
113
+ metrics: Record<string, number>;
114
+ /** Canonical event IDs that fed this run. */
115
+ eventIds: string[];
116
+ upi: number;
117
+ band: string;
118
+ /** Scoring confidence (evidence-based). Present when persisted for explainability. */
119
+ confidence?: SnapshotConfidenceDto;
120
+ /** Gate results (dominance, quality, confidence, evidence). Present when persisted for explainability. */
121
+ gates?: SnapshotGatesDto;
122
+ }
123
+
124
+ /**
125
+ * M6: Run comparison — why did this score change? (week-over-week deltas, no recomputation).
126
+ * bandA/bandB are from remapped UPI; delta.dimensionScores and delta.upiDelta are deltas in [0, 1] space.
127
+ */
128
+ export interface RunComparisonDto {
129
+ runIdA: string;
130
+ runIdB: string;
131
+ windowEndA: string;
132
+ windowEndB: string;
133
+ tenantId?: string;
134
+ personId?: string;
135
+ /** Deltas (B − A) for metrics and dimensionScores; upiDelta = upiB − upiA (in [0,1] space). */
136
+ delta: {
137
+ metrics: Record<string, number>;
138
+ dimensionScores: Record<string, number>;
139
+ upiDelta: number;
140
+ };
141
+ bandA: string;
142
+ bandB: string;
143
+ /** Event IDs only in run A. */
144
+ eventIdsOnlyInA: string[];
145
+ /** Event IDs only in run B. */
146
+ eventIdsOnlyInB: string[];
147
+ /** Event IDs in both runs. */
148
+ eventIdsInBoth: string[];
149
+ }
150
+
151
+ /** Compare view: one entity (person or team) with UPI and optional dimensions/metrics. */
152
+ export interface ComparisonItemDto {
153
+ id: string;
154
+ label: string;
155
+ type: "person" | "team";
156
+ upi: number | null;
157
+ band: string | null;
158
+ dimensionScores?: Record<string, number>;
159
+ metrics?: Record<string, number>;
160
+ }
161
+
162
+ /** Response for GET /tenants/:tenantId/compare (side-by-side comparison). */
163
+ export interface ComparisonResponseDto {
164
+ items: ComparisonItemDto[];
165
+ }
166
+
167
+ /** M7: Org chart tree node (recursive). */
168
+ export interface OrgChartTreeNodeDto {
169
+ type?: "org" | "team" | "person";
170
+ id: string;
171
+ name?: string;
172
+ label?: string;
173
+ children?: OrgChartTreeNodeDto[];
174
+ person_id?: string;
175
+ team?: string;
176
+ role?: string;
177
+ /** High-level role tag used for stack ranking filters. Defaults to contributor when absent. */
178
+ roleTag?: "contributor" | "enabler";
179
+ level?: string;
180
+ external_ids?: Record<string, string>;
181
+ }
182
+
183
+ /** Org chart tree node with computed scores (Option B, α=0.2). Used when ?withScores=true. */
184
+ export interface OrgChartTreeNodeWithScoreDto extends OrgChartTreeNodeDto {
185
+ score?: number | null;
186
+ individualUpi?: number | null;
187
+ children?: OrgChartTreeNodeWithScoreDto[];
188
+ }
189
+
190
+ /** M7: Org chart response (tree only). */
191
+ export interface OrgChartDto {
192
+ root: OrgChartTreeNodeDto;
193
+ }
194
+
195
+ /** Summary of a person derived from the tenant's org chart. */
196
+ export interface PersonSummaryDto {
197
+ /** Canonical person identifier (e.g. work email). */
198
+ personId: string;
199
+ team?: string;
200
+ role?: string;
201
+ /** High-level role tag used for stack ranking filters. Defaults to contributor when absent. */
202
+ roleTag?: "contributor" | "enabler";
203
+ level?: string;
204
+ }
205
+
206
+ /** Response for GET /tenants/:tenantId/persons (flat list of persons for a tenant). */
207
+ export interface GetTenantPersonsResponseDto {
208
+ persons: PersonSummaryDto[];
209
+ }
210
+
211
+ /** Tenant config: per-tenant data source entries (type + params; params may use "env:VAR" for secrets). */
212
+ export interface TenantSourceEntryDto {
213
+ type: string;
214
+ [key: string]: unknown;
215
+ }
216
+
217
+ /** Response for POST /tenants/:tenantId/sources/test (test connection; no save). 200 always; success false when connection/auth fails. */
218
+ export interface TestConnectionResponseDto {
219
+ success: boolean;
220
+ error?: string;
221
+ }
222
+
223
+ /** Tenant config response (sources; optionally org chart for Home; optionally region for data residency). */
224
+ export interface TenantConfigDto {
225
+ sources: TenantSourceEntryDto[];
226
+ orgChart?: OrgChartDto | null;
227
+ /** Region for data residency (us, eu, me). Null or absent = legacy/single-region. */
228
+ region?: string | null;
229
+ }
230
+
231
+ /** Response for GET /me/tenants: tenants the user is allowed to see (for dropdown). */
232
+ export interface TenantsForUserDto {
233
+ tenants: { tenantId: string }[];
234
+ }
235
+
236
+ /** Team member roles exposed to ThinkAI UI. */
237
+ export type TeamMemberRoleDto = "admin" | "editor" | "viewer";
238
+
239
+ /** Team member row for tenant member management UI. */
240
+ export interface TeamMemberDto {
241
+ id: string;
242
+ name: string;
243
+ email: string;
244
+ role: TeamMemberRoleDto;
245
+ }
246
+
247
+ /** Response for GET /tenants/:tenantId/members. */
248
+ export interface TeamMemberListDto {
249
+ members: TeamMemberDto[];
250
+ }
251
+
252
+ /** Body for POST /tenants/:tenantId/members/invite. */
253
+ export interface InviteTeamMemberBodyDto {
254
+ email: string;
255
+ role: TeamMemberRoleDto;
256
+ }
257
+
258
+ /** Body for PATCH /tenants/:tenantId/members/:memberId. */
259
+ export interface UpdateTeamMemberRoleBodyDto {
260
+ role: TeamMemberRoleDto;
261
+ }
262
+
263
+ /** M6: Single audit log entry (immutable). */
264
+ export interface AuditEntryDto {
265
+ id: string;
266
+ timestamp: string;
267
+ action: string;
268
+ tenantId?: string;
269
+ personId?: string;
270
+ payload: Record<string, unknown>;
271
+ }
272
+
273
+ /** M6: Paginated audit log. */
274
+ export interface AuditLogListDto {
275
+ items: AuditEntryDto[];
276
+ total: number;
277
+ limit: number;
278
+ offset: number;
279
+ }
280
+
281
+ /** CTO Dashboard: nested metric groups. All fields optional; only present when data exists. */
282
+ export interface DashboardProductivityMetricsDto {
283
+ velocity?: {
284
+ avgFirstCommitToPrDays?: number;
285
+ prsPerDeveloperPerWeek?: number;
286
+ avgPrMergeTimeHours?: number;
287
+ };
288
+ quality?: {
289
+ codeReviewHoursPerWeek?: number;
290
+ buildFailuresPerWeek?: number;
291
+ avgTimeToFixBuildHours?: number;
292
+ testCoveragePercent?: number;
293
+ escapedBugsCount?: number;
294
+ /** Avg cyclomatic complexity (from SonarQube). */
295
+ complexityAvg?: number;
296
+ /** Code smells count (from SonarQube). */
297
+ codeSmellsCount?: number;
298
+ /** Total violations count (from SonarQube). */
299
+ violationsCount?: number;
300
+ };
301
+ process?: {
302
+ planningTimeHoursPerWeek?: number;
303
+ contextSwitchingOverheadPercent?: number;
304
+ };
305
+ aiAdoption?: {
306
+ aiToolUsageRatePercent?: number;
307
+ aiAssistedContributionRatioPercent?: number;
308
+ };
309
+ readiness?: {
310
+ engineeringProcessReadinessPercent?: number;
311
+ };
312
+ /** Flow Framework metrics: flow time, flow load, flow efficiency. */
313
+ flow?: {
314
+ /** Median flow time in days (work started → completed); ticket or PR lifecycle. */
315
+ flowTimeDays?: number;
316
+ /** Average work-in-progress (WIP) count in window. */
317
+ flowLoad?: number;
318
+ /** Active work time / total flow time (0–100). Undefined when no wait/active data. */
319
+ flowEfficiencyPercent?: number;
320
+ };
321
+ /** DORA metrics: deployment frequency, lead time for changes, change failure rate, MTTR. See docs/DORA-Event-Attributes.md and docs/DORA-Benchmark-Bands.md. */
322
+ dora?: {
323
+ /** Deployments per week (production when environment present; else all). */
324
+ deploymentFrequencyPerWeek?: number;
325
+ /** DORA benchmark band for deployment frequency. See docs/DORA-Benchmark-Bands.md. */
326
+ deploymentFrequencyBand?: "elite" | "high" | "medium" | "low";
327
+ /** Median time from code change to production (hours). */
328
+ leadTimeForChangesHours?: number;
329
+ /** DORA benchmark band for lead time. See docs/DORA-Benchmark-Bands.md. */
330
+ leadTimeForChangesBand?: "elite" | "high" | "medium" | "low";
331
+ /** % of deployments causing incidents (or proxy: incidents / deployments). */
332
+ changeFailureRatePercent?: number;
333
+ /** DORA benchmark band for change failure rate. See docs/DORA-Benchmark-Bands.md. */
334
+ changeFailureRateBand?: "elite" | "high" | "medium" | "low";
335
+ /** Median time from incident.declared to incident.resolved (hours). */
336
+ meanTimeToRecoveryHours?: number;
337
+ /** DORA benchmark band for MTTR. See docs/DORA-Benchmark-Bands.md. */
338
+ meanTimeToRecoveryBand?: "elite" | "high" | "medium" | "low";
339
+ };
340
+ /** Resource allocation: how engineering time is distributed across work types. Percentages are 0–100 and typically sum to ~100. */
341
+ allocation?: {
342
+ /** Share of work attributed to roadmap/features (tickets and PRs not classified as maintenance/incidents/support). */
343
+ featurePercent?: number;
344
+ /** Share of work attributed to maintenance and tech debt reduction (when classification rules mark work as tech_debt). */
345
+ techDebtPercent?: number;
346
+ /** Share of work attributed to incident response (incidents, firefighting, production issues). */
347
+ incidentPercent?: number;
348
+ /** Share of work attributed to support/helpdesk/customer support-style work (when classification rules mark work as support). */
349
+ supportPercent?: number;
350
+ /** Share of work that does not fall into the above categories (uncategorized/other). */
351
+ otherPercent?: number;
352
+ };
353
+ /**
354
+ * Cycle time breakdown: per-person segment durations over the selected window.
355
+ * Stored in dashboard precompute; surfaced via GET /dashboard/cycle-time-breakdown.
356
+ */
357
+ cycleTimeBreakdown?: {
358
+ persons: {
359
+ personId: string;
360
+ codingHours?: number;
361
+ pickupHours?: number;
362
+ reviewHours?: number;
363
+ deployHours?: number;
364
+ totalHours?: number;
365
+ bottleneckSegment?: "coding" | "pickup" | "review" | "deploy";
366
+ }[];
367
+ };
368
+ }
369
+
370
+ /** Single per-person cycle time breakdown item for the CTO dashboard. Durations are in hours over the selected window. */
371
+ export interface CycleTimeBreakdownItemDto {
372
+ /** Canonical person identifier (e.g. work email). */
373
+ personId: string;
374
+ /** Human-readable label (e.g. name from org chart). */
375
+ label?: string;
376
+ /** Optional team label from org chart or person summary. */
377
+ team?: string;
378
+ /** Average time spent in the Coding segment (first commit/branch → PR opened), in hours. */
379
+ codingHours?: number;
380
+ /** Average time spent waiting for review (PR opened → first review), in hours. */
381
+ pickupHours?: number;
382
+ /** Average time spent in active review (first review → PR approved/merged), in hours. */
383
+ reviewHours?: number;
384
+ /** Average time from merge to deploy success, in hours. */
385
+ deployHours?: number;
386
+ /** Total average cycle time (sum of defined segment durations), in hours. */
387
+ totalHours?: number;
388
+ /** Segment contributing the largest share of total cycle time. */
389
+ bottleneckSegment?: "coding" | "pickup" | "review" | "deploy";
390
+ }
391
+
392
+ /** CTO Dashboard: precomputed cycle time breakdown per tenant/scope/window. */
393
+ export interface CycleTimeBreakdownDto {
394
+ tenantId: string;
395
+ windowStart: string;
396
+ windowEnd: string;
397
+ scope: "org" | "team" | "person";
398
+ items: CycleTimeBreakdownItemDto[];
399
+ }
400
+
401
+ /** CTO Dashboard: response for GET /tenants/:tenantId/dashboard/productivity */
402
+ export interface DashboardProductivityDto {
403
+ tenantId: string;
404
+ windowStart: string;
405
+ windowEnd: string;
406
+ scope: "org" | "team" | "person";
407
+ metrics: DashboardProductivityMetricsDto;
408
+ meta?: { sourceWindow?: string; computedAt?: string };
409
+ }
410
+
411
+ /** CTO Dashboard: time-series for GET /tenants/:tenantId/dashboard/productivity/history */
412
+ export interface DashboardProductivityHistoryDto {
413
+ buckets: {
414
+ bucketStart: string;
415
+ bucketEnd: string;
416
+ metrics: DashboardProductivityMetricsDto;
417
+ }[];
418
+ }
419
+
420
+ /** Appeal: user contests a misattributed event. Lifecycle: pending → approved | rejected. */
421
+ export interface AppealDto {
422
+ id: string;
423
+ tenantId: string;
424
+ personId: string;
425
+ eventId: string;
426
+ reason?: string | null;
427
+ status: "pending" | "approved" | "rejected";
428
+ createdAt: string;
429
+ resolvedAt?: string | null;
430
+ resolvedBy?: string | null;
431
+ }
432
+
433
+ /** Body for POST /tenants/:tenantId/appeals (submit appeal). */
434
+ export interface CreateAppealDto {
435
+ eventId: string;
436
+ reason?: string | null;
437
+ }
438
+
439
+ /** Feedback: general scoring concerns or rules review requests. For admin review; distinct from appeals. */
440
+ export interface FeedbackDto {
441
+ id: string;
442
+ tenantId: string;
443
+ personId: string;
444
+ category: "scoring_concern" | "rules_review";
445
+ message: string;
446
+ personIdContext?: string | null;
447
+ createdAt: string;
448
+ }
449
+
450
+ /** Body for POST /tenants/:tenantId/feedback (submit feedback). */
451
+ export interface CreateFeedbackDto {
452
+ category: "scoring_concern" | "rules_review";
453
+ message: string;
454
+ personIdContext?: string | null;
455
+ }
456
+
457
+ /** Export API: single event for JSON export (GET /tenants/:tenantId/export?type=events&format=json). */
458
+ export interface ExportEventDto {
459
+ eventId: string;
460
+ tenantId: string;
461
+ occurredAt: string;
462
+ source: string;
463
+ eventType: string;
464
+ entityRefs?: { kind: string; id: string }[];
465
+ attributes: Record<string, unknown>;
466
+ }
467
+
468
+ /** Export API: events JSON response. */
469
+ export interface ExportEventsResponseDto {
470
+ items: ExportEventDto[];
471
+ total: number;
472
+ }
473
+
474
+ /** OpenAPI path and operation typings (generated from `openapi/openapi.yaml`). */
475
+ export type { paths, operations } from "./generated/openapi.js";