@thinkai/tai-api-contract 2.34.0-pr.810.e54f7dcd → 2.35.0-pr.811.d57af044
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/openapi.d.ts +490 -37
- package/dist/generated/openapi.d.ts.map +1 -1
- package/dist/index.d.ts +1 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/journeySteps.d.ts +29 -0
- package/dist/journeySteps.d.ts.map +1 -0
- package/dist/journeySteps.js +171 -0
- package/dist/journeySteps.js.map +1 -0
- package/dist/journeySteps.test.d.ts +2 -0
- package/dist/journeySteps.test.d.ts.map +1 -0
- package/dist/journeySteps.test.js +47 -0
- package/dist/journeySteps.test.js.map +1 -0
- package/openapi/openapi.yaml +738 -66
- package/package.json +2 -1
- package/src/generated/openapi.ts +490 -37
- package/src/index.ts +10 -23
- package/src/journeySteps.test.ts +60 -0
- package/src/journeySteps.ts +190 -0
package/src/index.ts
CHANGED
|
@@ -302,10 +302,6 @@ export interface DashboardProductivityMetricsDto {
|
|
|
302
302
|
quality?: {
|
|
303
303
|
codeReviewHoursPerWeek?: number;
|
|
304
304
|
buildFailuresPerWeek?: number;
|
|
305
|
-
/** Share of completed CI runs that failed (GitHub Actions / Checks). */
|
|
306
|
-
ciBuildFailRatePercent?: number;
|
|
307
|
-
/** Count of build.failed events in the bucket window. */
|
|
308
|
-
ciBuildFailuresCount?: number;
|
|
309
305
|
avgTimeToFixBuildHours?: number;
|
|
310
306
|
testCoveragePercent?: number;
|
|
311
307
|
escapedBugsCount?: number;
|
|
@@ -464,10 +460,6 @@ export interface ProductivityInsightsAggregatedTeamWeekDto {
|
|
|
464
460
|
weekLabel?: string;
|
|
465
461
|
mergedPrCount: number;
|
|
466
462
|
failRatePercent: number;
|
|
467
|
-
/** GitHub Actions CI build failure rate (failed / (failed + passed) * 100). */
|
|
468
|
-
ciFailRatePercent?: number;
|
|
469
|
-
/** Count of build.failed events in the bucket when CI ingest is enabled. */
|
|
470
|
-
ciBuildFailuresCount?: number;
|
|
471
463
|
avgReviewWaitHours?: number;
|
|
472
464
|
avgApprovalHours?: number;
|
|
473
465
|
avgTimeToMergeHours?: number;
|
|
@@ -492,8 +484,6 @@ export interface ProductivityInsightsAggregatedTeamsDto {
|
|
|
492
484
|
range: "4w" | "8w" | "q" | "12w" | "16w";
|
|
493
485
|
scope: ProductivityInsightsAggregatedTeamsScopeDto;
|
|
494
486
|
generatedAt: string;
|
|
495
|
-
/** True when the workspace has opted in to GitHub Actions CI ingest. */
|
|
496
|
-
githubActionsCiEnabled?: boolean;
|
|
497
487
|
teams: ProductivityInsightsAggregatedTeamRowDto[];
|
|
498
488
|
}
|
|
499
489
|
|
|
@@ -535,7 +525,6 @@ export interface ProductivityInsightsTeamLeaderItemDto {
|
|
|
535
525
|
subtitle?: string;
|
|
536
526
|
periodAvgMergedPrs: number;
|
|
537
527
|
avgFailRatePercent?: number;
|
|
538
|
-
avgCiFailRatePercent?: number;
|
|
539
528
|
}
|
|
540
529
|
|
|
541
530
|
/** Period summary for a single-team drilldown. */
|
|
@@ -543,8 +532,6 @@ export interface ProductivityInsightsTeamSummaryDto {
|
|
|
543
532
|
totalMergedPrs: number;
|
|
544
533
|
avgMergedPrsPerWeek: number;
|
|
545
534
|
avgFailRatePercent: number;
|
|
546
|
-
/** Average GitHub Actions CI build failure rate over the range when CI ingest is enabled. */
|
|
547
|
-
avgCiFailRatePercent?: number;
|
|
548
535
|
headcount: number;
|
|
549
536
|
}
|
|
550
537
|
|
|
@@ -573,8 +560,6 @@ export interface ProductivityInsightsTeamsResponseDto {
|
|
|
573
560
|
year?: number;
|
|
574
561
|
scope: ProductivityInsightsAggregatedTeamsScopeDto;
|
|
575
562
|
generatedAt: string;
|
|
576
|
-
/** True when the workspace has opted in to GitHub Actions CI ingest. */
|
|
577
|
-
githubActionsCiEnabled?: boolean;
|
|
578
563
|
/** Top-ranked teams when `teamId` query is omitted. */
|
|
579
564
|
items?: ProductivityInsightsTeamLeaderItemDto[];
|
|
580
565
|
/** Single-team drilldown when `teamId` query is set. */
|
|
@@ -628,14 +613,10 @@ export interface ProductivityInsightsContributorActivitySummaryDto {
|
|
|
628
613
|
avgMergedPrsPerWeek: number;
|
|
629
614
|
totalAbsenceDays: number;
|
|
630
615
|
avgFailRate: number;
|
|
631
|
-
/** Average GitHub Actions CI build failure rate over the range when CI ingest is enabled. */
|
|
632
|
-
avgCiFailRate?: number;
|
|
633
616
|
mergedPrDelta: number;
|
|
634
617
|
avgMergedPrsPerWeekDelta: number;
|
|
635
618
|
totalAbsenceDaysDelta: number;
|
|
636
619
|
avgFailRateDelta: number;
|
|
637
|
-
/** Change in average CI build failure rate vs the prior comparison window. */
|
|
638
|
-
avgCiFailRateDelta?: number;
|
|
639
620
|
}
|
|
640
621
|
|
|
641
622
|
/** Response for GET /workspaces/:workspaceId/insights/productivity-insights/contributors/:contributorId */
|
|
@@ -647,8 +628,6 @@ export interface ProductivityInsightsContributorDetailDto {
|
|
|
647
628
|
year?: number;
|
|
648
629
|
scope: ProductivityInsightsAggregatedTeamsScopeDto;
|
|
649
630
|
generatedAt: string;
|
|
650
|
-
/** True when the workspace has opted in to GitHub Actions CI ingest. */
|
|
651
|
-
githubActionsCiEnabled?: boolean;
|
|
652
631
|
contributor: ProductivityInsightsContributorProfileDto;
|
|
653
632
|
weeks: ProductivityInsightsContributorActivityWeekDto[];
|
|
654
633
|
summary: ProductivityInsightsContributorActivitySummaryDto;
|
|
@@ -663,7 +642,6 @@ export interface ProductivityInsightsNeedsAttentionItemDto {
|
|
|
663
642
|
subtitle?: string;
|
|
664
643
|
periodAvgMergedPrs: number;
|
|
665
644
|
avgFailRatePercent?: number;
|
|
666
|
-
avgCiFailRatePercent?: number;
|
|
667
645
|
}
|
|
668
646
|
|
|
669
647
|
/** Response for GET /workspaces/:workspaceId/insights/productivity-insights/needs-attention */
|
|
@@ -673,7 +651,6 @@ export interface ProductivityInsightsNeedsAttentionDto {
|
|
|
673
651
|
scope: ProductivityInsightsAggregatedTeamsScopeDto;
|
|
674
652
|
entityType: "teams" | "contributors";
|
|
675
653
|
generatedAt: string;
|
|
676
|
-
githubActionsCiEnabled?: boolean;
|
|
677
654
|
items: ProductivityInsightsNeedsAttentionItemDto[];
|
|
678
655
|
}
|
|
679
656
|
|
|
@@ -1107,3 +1084,13 @@ export interface HrisTriggerSyncResultDto {
|
|
|
1107
1084
|
/** Per-provider results when one or more HRIS sources are configured. */
|
|
1108
1085
|
providers?: HrisProviderSyncResultDto[];
|
|
1109
1086
|
}
|
|
1087
|
+
|
|
1088
|
+
export {
|
|
1089
|
+
JOURNEY_STEP_IDS,
|
|
1090
|
+
JOURNEY_STEP_REGISTRY,
|
|
1091
|
+
normalizeStepIdList,
|
|
1092
|
+
normalizeToStepId,
|
|
1093
|
+
parseJourneyStepInputs,
|
|
1094
|
+
stepIdToRoute,
|
|
1095
|
+
type JourneyStepId,
|
|
1096
|
+
} from "./journeySteps.js";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for journey step ID registry and normalization.
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
JOURNEY_STEP_IDS,
|
|
6
|
+
normalizeStepIdList,
|
|
7
|
+
normalizeToStepId,
|
|
8
|
+
parseJourneyStepInputs,
|
|
9
|
+
stepIdToRoute,
|
|
10
|
+
} from "./journeySteps.js";
|
|
11
|
+
|
|
12
|
+
if (normalizeToStepId("/phase-a/p1") !== "onboarding.workspace") {
|
|
13
|
+
throw new Error("expected /phase-a/p1 → onboarding.workspace");
|
|
14
|
+
}
|
|
15
|
+
if (normalizeToStepId("/onboarding/workspace") !== "onboarding.workspace") {
|
|
16
|
+
throw new Error("expected semantic route alias");
|
|
17
|
+
}
|
|
18
|
+
if (normalizeToStepId("readiness.dashboard") !== "readiness.dashboard") {
|
|
19
|
+
throw new Error("expected canonical step id passthrough");
|
|
20
|
+
}
|
|
21
|
+
if (normalizeToStepId("/unknown/route") !== null) {
|
|
22
|
+
throw new Error("unknown route should normalize to null");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const deduped = normalizeStepIdList(["/phase-a/p1", "/onboarding/workspace", "/phase-a/p1"]);
|
|
26
|
+
if (deduped.length !== 1 || deduped[0] !== "onboarding.workspace") {
|
|
27
|
+
throw new Error(`dedupe failed: ${JSON.stringify(deduped)}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const droppedUnknown = parseJourneyStepInputs(["/phase-a/p1", "/stale/route"]);
|
|
31
|
+
if (droppedUnknown.error || droppedUnknown.value?.length !== 1) {
|
|
32
|
+
throw new Error(`unknown should drop silently by default: ${JSON.stringify(droppedUnknown)}`);
|
|
33
|
+
}
|
|
34
|
+
if (droppedUnknown.unknown?.length !== 1 || droppedUnknown.unknown[0] !== "/stale/route") {
|
|
35
|
+
throw new Error("unknown list should report dropped entries");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const rejectUnknown = parseJourneyStepInputs(["/stale/route"], { rejectUnknown: true });
|
|
39
|
+
if (rejectUnknown.error !== "Unknown journey step" || rejectUnknown.unknown?.[0] !== "/stale/route") {
|
|
40
|
+
throw new Error(`rejectUnknown failed: ${JSON.stringify(rejectUnknown)}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const blankStep = parseJourneyStepInputs([" "], { rejectUnknown: true });
|
|
44
|
+
if (blankStep.error !== "Each completed step must be a non-empty string") {
|
|
45
|
+
throw new Error(`blank step guard failed: ${JSON.stringify(blankStep)}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const atCap = parseJourneyStepInputs(JOURNEY_STEP_IDS, { rejectUnknown: true });
|
|
49
|
+
if (atCap.error || atCap.value?.length !== JOURNEY_STEP_IDS.length) {
|
|
50
|
+
throw new Error(`full registry parse failed: ${JSON.stringify(atCap)}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (stepIdToRoute("onboarding.workspace") !== "/onboarding/workspace") {
|
|
54
|
+
throw new Error("stepIdToRoute failed for onboarding.workspace");
|
|
55
|
+
}
|
|
56
|
+
if (stepIdToRoute("not.a.step") !== null) {
|
|
57
|
+
throw new Error("stepIdToRoute should return null for unknown id");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log("journeySteps.test: OK");
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical journey step IDs — stable keys for journey progress storage.
|
|
3
|
+
* Semantic routes are the SPA URL surface; legacy /phase-* paths normalize here.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const JOURNEY_STEP_IDS = [
|
|
7
|
+
"onboarding.workspace",
|
|
8
|
+
"onboarding.ai_readiness",
|
|
9
|
+
"onboarding.integrations",
|
|
10
|
+
"readiness.dashboard",
|
|
11
|
+
"readiness.fix_queue",
|
|
12
|
+
"readiness.activity",
|
|
13
|
+
"insights.productivity",
|
|
14
|
+
"insights.productivity.by_user",
|
|
15
|
+
"insights.productivity.by_team",
|
|
16
|
+
"insights.ai_usage",
|
|
17
|
+
"insights.ai_usage.by_user",
|
|
18
|
+
"insights.ai_usage.by_team",
|
|
19
|
+
"insights.ai_usage.by_model",
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
export type JourneyStepId = (typeof JOURNEY_STEP_IDS)[number];
|
|
23
|
+
|
|
24
|
+
type StepDef = {
|
|
25
|
+
id: JourneyStepId;
|
|
26
|
+
route: string;
|
|
27
|
+
aliases: readonly string[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Registry order matches product journey A → R → C. */
|
|
31
|
+
export const JOURNEY_STEP_REGISTRY: readonly StepDef[] = [
|
|
32
|
+
{
|
|
33
|
+
id: "onboarding.workspace",
|
|
34
|
+
route: "/onboarding/workspace",
|
|
35
|
+
aliases: ["/phase-a/p1"],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "onboarding.ai_readiness",
|
|
39
|
+
route: "/onboarding/ai-readiness",
|
|
40
|
+
aliases: ["/phase-a/p2"],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "onboarding.integrations",
|
|
44
|
+
route: "/onboarding/integrations",
|
|
45
|
+
aliases: [
|
|
46
|
+
"/onboarding/platform",
|
|
47
|
+
"/phase-a/p3",
|
|
48
|
+
"/phase-a/p4",
|
|
49
|
+
"/phase-a/p5",
|
|
50
|
+
"/phase-a/p6",
|
|
51
|
+
"/phase-a/p7",
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "readiness.dashboard",
|
|
56
|
+
route: "/agentic-foundation",
|
|
57
|
+
aliases: ["/phase-r/r1"],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "readiness.fix_queue",
|
|
61
|
+
route: "/agentic-foundation/fix-queue",
|
|
62
|
+
aliases: ["/phase-r/r3"],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "readiness.activity",
|
|
66
|
+
route: "/agentic-foundation/activity",
|
|
67
|
+
aliases: ["/phase-r/r4"],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "insights.productivity",
|
|
71
|
+
route: "/insights/productivity",
|
|
72
|
+
aliases: ["/insights/productivity/overview", "/phase-c/c4"],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "insights.productivity.by_user",
|
|
76
|
+
route: "/insights/productivity/by-user",
|
|
77
|
+
aliases: ["/phase-c/c4/by-user"],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "insights.productivity.by_team",
|
|
81
|
+
route: "/insights/productivity/by-team",
|
|
82
|
+
aliases: ["/phase-c/c4/by-team"],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "insights.ai_usage",
|
|
86
|
+
route: "/insights/ai-usage",
|
|
87
|
+
aliases: ["/insights/ai-usage/overview", "/phase-c/c7"],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "insights.ai_usage.by_user",
|
|
91
|
+
route: "/insights/ai-usage/by-user",
|
|
92
|
+
aliases: ["/phase-c/c7/ai-usage-by-user"],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "insights.ai_usage.by_team",
|
|
96
|
+
route: "/insights/ai-usage/by-team",
|
|
97
|
+
aliases: ["/phase-c/c7/ai-usage-by-team"],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "insights.ai_usage.by_model",
|
|
101
|
+
route: "/insights/ai-usage/by-model",
|
|
102
|
+
aliases: ["/phase-c/c7/costs-by-llm-model"],
|
|
103
|
+
},
|
|
104
|
+
] as const;
|
|
105
|
+
|
|
106
|
+
const STEP_ID_SET = new Set<string>(JOURNEY_STEP_IDS);
|
|
107
|
+
|
|
108
|
+
const ALIAS_TO_STEP_ID = new Map<string, JourneyStepId>();
|
|
109
|
+
|
|
110
|
+
for (const def of JOURNEY_STEP_REGISTRY) {
|
|
111
|
+
ALIAS_TO_STEP_ID.set(def.id, def.id);
|
|
112
|
+
ALIAS_TO_STEP_ID.set(def.route, def.id);
|
|
113
|
+
for (const alias of def.aliases) {
|
|
114
|
+
ALIAS_TO_STEP_ID.set(alias, def.id);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const STEP_ID_TO_ROUTE = new Map<JourneyStepId, string>(
|
|
119
|
+
JOURNEY_STEP_REGISTRY.map((d) => [d.id, d.route]),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
function normalizePathKey(input: string): string {
|
|
123
|
+
const trimmed = input.trim();
|
|
124
|
+
if (!trimmed) return "";
|
|
125
|
+
const noQuery = trimmed.split("?")[0]?.split("#")[0] ?? trimmed;
|
|
126
|
+
const collapsed = noQuery.replace(/\/+$/, "") || "/";
|
|
127
|
+
return collapsed;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Map step ID, semantic route, or legacy path to a canonical step ID. */
|
|
131
|
+
export function normalizeToStepId(input: string): JourneyStepId | null {
|
|
132
|
+
const key = normalizePathKey(input);
|
|
133
|
+
if (!key) return null;
|
|
134
|
+
return ALIAS_TO_STEP_ID.get(key) ?? null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Resolve a canonical step ID to its primary semantic SPA route. */
|
|
138
|
+
export function stepIdToRoute(stepId: string): string | null {
|
|
139
|
+
if (!STEP_ID_SET.has(stepId)) return null;
|
|
140
|
+
return STEP_ID_TO_ROUTE.get(stepId as JourneyStepId) ?? null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Normalize an array of mixed inputs to deduped canonical step IDs (drops unknowns). */
|
|
144
|
+
export function normalizeStepIdList(inputs: readonly string[]): JourneyStepId[] {
|
|
145
|
+
const out = new Set<JourneyStepId>();
|
|
146
|
+
for (const raw of inputs) {
|
|
147
|
+
const id = normalizeToStepId(raw);
|
|
148
|
+
if (id) out.add(id);
|
|
149
|
+
}
|
|
150
|
+
return [...out];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Accept step IDs and legacy/route strings; returns normalized IDs or an error message. */
|
|
154
|
+
export function parseJourneyStepInputs(
|
|
155
|
+
inputs: readonly string[],
|
|
156
|
+
options?: { rejectUnknown?: boolean },
|
|
157
|
+
): { value?: JourneyStepId[]; error?: string; unknown?: string[] } {
|
|
158
|
+
const rejectUnknown = options?.rejectUnknown === true;
|
|
159
|
+
const unknown: string[] = [];
|
|
160
|
+
const out = new Set<JourneyStepId>();
|
|
161
|
+
for (const raw of inputs) {
|
|
162
|
+
if (typeof raw !== "string") {
|
|
163
|
+
return { error: "Each completed step must be a string" };
|
|
164
|
+
}
|
|
165
|
+
const trimmed = raw.trim();
|
|
166
|
+
if (!trimmed) {
|
|
167
|
+
return { error: "Each completed step must be a non-empty string" };
|
|
168
|
+
}
|
|
169
|
+
if (trimmed.length > 128) {
|
|
170
|
+
return { error: "Each completed step must be at most 128 characters" };
|
|
171
|
+
}
|
|
172
|
+
const id = normalizeToStepId(trimmed);
|
|
173
|
+
if (!id) {
|
|
174
|
+
unknown.push(trimmed);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
out.add(id);
|
|
178
|
+
}
|
|
179
|
+
if (rejectUnknown && unknown.length > 0) {
|
|
180
|
+
return { error: "Unknown journey step", unknown };
|
|
181
|
+
}
|
|
182
|
+
const value = [...out];
|
|
183
|
+
if (value.length > 256) {
|
|
184
|
+
return { error: "completedStepIds must have at most 256 entries" };
|
|
185
|
+
}
|
|
186
|
+
if (unknown.length > 0) {
|
|
187
|
+
return { value, unknown };
|
|
188
|
+
}
|
|
189
|
+
return { value };
|
|
190
|
+
}
|