@taodigital/app-brain 0.0.0-stage → 1.2.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.
Files changed (47) hide show
  1. package/README.md +13 -2
  2. package/dist/archetype-classification.d.ts +54 -0
  3. package/dist/archetype-classification.js +95 -0
  4. package/dist/cloud-storage.d.ts +106 -0
  5. package/dist/cloud-storage.js +539 -0
  6. package/dist/email.d.ts +21 -0
  7. package/dist/email.js +234 -0
  8. package/dist/grounding.d.ts +30 -0
  9. package/dist/grounding.js +138 -0
  10. package/dist/index.d.ts +13 -0
  11. package/dist/index.js +37 -0
  12. package/dist/nav-graph.d.ts +42 -0
  13. package/dist/nav-graph.js +248 -0
  14. package/dist/notifications.d.ts +26 -0
  15. package/dist/notifications.js +105 -0
  16. package/dist/orphan-classification.d.ts +32 -0
  17. package/dist/orphan-classification.js +40 -0
  18. package/dist/repository.d.ts +3185 -0
  19. package/dist/repository.js +5927 -0
  20. package/dist/run-notifications.d.ts +16 -0
  21. package/dist/run-notifications.js +244 -0
  22. package/dist/skill-file-kinds.d.ts +5 -0
  23. package/dist/skill-file-kinds.js +50 -0
  24. package/dist/token-ledger.d.ts +23 -0
  25. package/dist/token-ledger.js +178 -0
  26. package/dist/traceability.d.ts +213 -0
  27. package/dist/traceability.js +339 -0
  28. package/package.json +24 -4
  29. package/src/archetype-classification.test.ts +91 -0
  30. package/src/archetype-classification.ts +132 -0
  31. package/src/cloud-storage.test.ts +83 -0
  32. package/src/cloud-storage.ts +627 -0
  33. package/src/email.ts +268 -0
  34. package/src/grounding.ts +172 -0
  35. package/src/index.ts +18 -0
  36. package/src/nav-graph.ts +295 -0
  37. package/src/notifications.ts +156 -0
  38. package/src/orphan-classification.test.ts +61 -0
  39. package/src/orphan-classification.ts +65 -0
  40. package/src/orphan-screens.test.ts +88 -0
  41. package/src/repository.ts +7397 -0
  42. package/src/run-notifications.ts +281 -0
  43. package/src/skill-file-kinds.ts +51 -0
  44. package/src/token-ledger.ts +209 -0
  45. package/src/traceability.test.ts +75 -0
  46. package/src/traceability.ts +477 -0
  47. package/tsconfig.json +9 -0
@@ -0,0 +1,3185 @@
1
+ import { Prisma } from '@taodigital/database';
2
+ import type { TestKind } from '@taodigital/core';
3
+ export interface AppBrainConfig {
4
+ maxSelectiveSkillFiles?: number;
5
+ minRerankScore?: number;
6
+ chunkCharLimit?: number;
7
+ chunkOverlap?: number;
8
+ minChunkLength?: number;
9
+ minChunkWindow?: number;
10
+ bm25TopK?: number;
11
+ maxChunkChars?: number;
12
+ bm25CandidatePool?: number;
13
+ rerankSafetyCap?: number;
14
+ kgMaxHops?: number;
15
+ kgMaxSuggestions?: number;
16
+ kgJourneyMaxHops?: number;
17
+ crossProjectPatternLimit?: number;
18
+ flakyWindowSize?: number;
19
+ flakyMinRunsToScore?: number;
20
+ quarantineMinRuns?: number;
21
+ quarantineThreshold?: number;
22
+ coverageWeights?: {
23
+ CRITICAL: number;
24
+ HIGH: number;
25
+ MEDIUM: number;
26
+ LOW: number;
27
+ };
28
+ targetCoveragePercent?: number;
29
+ sharedRunTtlHours?: number;
30
+ }
31
+ type TruthDiffSegmentKind = 'MATCH' | 'WEAK_MATCH' | 'DOCUMENTED_NOT_OBSERVED' | 'OBSERVED_NOT_DOCUMENTED';
32
+ type TruthDiffSegment = {
33
+ id: string;
34
+ kind: TruthDiffSegmentKind;
35
+ title: string;
36
+ confidence: number;
37
+ requirement?: {
38
+ id: string;
39
+ reqId: string;
40
+ title: string;
41
+ priority: string;
42
+ type: string;
43
+ sourceSection?: string | null;
44
+ };
45
+ crawlEvidence?: Array<{
46
+ id?: string;
47
+ role?: string | null;
48
+ text: string;
49
+ }>;
50
+ requirementEvidence?: string[];
51
+ recommendation: string;
52
+ };
53
+ export interface CredentialProfile {
54
+ id: string;
55
+ name: string;
56
+ username: string;
57
+ password?: string;
58
+ totpSecret?: string;
59
+ storageState?: string;
60
+ color?: string;
61
+ }
62
+ export type LLMTaskType = 'crawl' | 'testGeneration' | 'testScriptGen' | 'testScriptPatch' | 'testPlan' | 'testRunner' | 'elementExtraction' | 'linkDiscovery' | 'screenNaming' | 'appBrain' | 'requirementsExtraction' | 'visualDiff';
63
+ export interface RequirementUpsertInput {
64
+ reqId: string;
65
+ title: string;
66
+ actor?: string | null;
67
+ type?: string;
68
+ priority?: string;
69
+ acceptanceCriteria?: string[];
70
+ businessRules?: string[];
71
+ relatedScreenHints?: string[];
72
+ sourceSection?: string | null;
73
+ derived?: boolean;
74
+ source?: string;
75
+ status?: string;
76
+ externalMeta?: any;
77
+ }
78
+ /**
79
+ * The Zeta Core repository: read/write the project's permanent knowledge.
80
+ * Everything goes through withTenant(), so the database enforces isolation.
81
+ */
82
+ export declare const AppBrain: {
83
+ /** Delete all elements (and their fingerprints) for a screen — call before re-saving on recrawl. */
84
+ clearScreenElements(tenantId: string, projectId: string, screenId: string): Promise<void>;
85
+ /** Add functional elements (buttons/inputs) parsed from the crawler. */
86
+ saveElements(tenantId: string, projectId: string, screenId: string, elements: any[]): Promise<Prisma.BatchPayload | undefined>;
87
+ /**
88
+ * (KAG) Knowledge Graph Traversal: Returns the structural DOM path
89
+ * bridging a start screen to an end screen.
90
+ */
91
+ /**
92
+ * BFS multi-hop traversal of the KAG navigation graph.
93
+ * Returns the shortest path of screen→element→screen edges from start to end,
94
+ * or null if unreachable within maxHops.
95
+ */
96
+ getScreenTransitionGraph(tenantId: string, projectId: string, startScreenId: string, endScreenId: string, maxHops?: number, cfg?: AppBrainConfig): Promise<Array<{
97
+ fromScreenId: string;
98
+ toScreenId: string;
99
+ viaElementMeaning: string;
100
+ }> | null>;
101
+ /**
102
+ * Suggests promising cross-screen "journey" pairs to generate a KAG test
103
+ * for, so a user doesn't have to already know both screen ids before
104
+ * calling generate-flow. Classifies every active screen with a rule-based
105
+ * archetype (no LLM needed — see archetype-classification.ts), narrows to
106
+ * the small set of interesting archetype-pair rules, and only then pays
107
+ * for a graph-reachability BFS (getScreenTransitionGraph) on that narrowed
108
+ * candidate list — not on every screen pair in the project.
109
+ */
110
+ suggestJourneys(tenantId: string, projectId: string, maxSuggestions?: number, cfg?: AppBrainConfig): Promise<{
111
+ suggestions: import("./archetype-classification.js").JourneyCandidate[];
112
+ }>;
113
+ /** Set which screen an element navigates to (called during crawl post-processing). */
114
+ setElementTransition(tenantId: string, screenId: string, elementMeaning: string, toScreenId: string): Promise<Prisma.BatchPayload>;
115
+ /** Save (or update) a discovered screen. Upserts on canonical URL to prevent accumulation
116
+ * across re-crawls and to ensure projects crawling the same app share screen identity
117
+ * (ZETA-084: inconsistent screen mapping; ZETA-079: inconsistent element discovery). */
118
+ saveScreen(tenantId: string, projectId: string, name: string, url?: string, jobId?: string, discoveryMethod?: "ENTRY_POINT" | "LINK_FOLLOW" | "SEED_URL" | "SKYVERN_AI" | "SITEMAP" | "INTERACTIVE_PROBE"): Promise<any>;
119
+ updateScreenDomSnapshot(tenantId: string, screenId: string, domSnapshotPath: string, domSnapshotCloudUrl?: string): Promise<void>;
120
+ updateScreenRequiresAuth(tenantId: string, screenId: string, requiresAuth: boolean): Promise<void>;
121
+ updateScreenMarkdownPath(tenantId: string, screenId: string, markdownPath: string): Promise<void>;
122
+ /** Persist the extracted-markdown file path + metadata, and BM25-index its content for RAG search. */
123
+ updateScreenAiSummary(tenantId: string, screenId: string, aiSummary: string): Promise<void>;
124
+ updateScreenMarkdown(tenantId: string, projectId: string, screenId: string, data: {
125
+ markdownPath: string;
126
+ markdownMethod: string;
127
+ markdownWordCount: number;
128
+ markdown: string;
129
+ heading: string;
130
+ aiSummary?: string;
131
+ }): Promise<void>;
132
+ /** Read back a screen's previously-extracted markdown content, if any. */
133
+ getScreenMarkdown(tenantId: string, screenId: string): Promise<{
134
+ markdownPath: string;
135
+ markdownContent: string | null;
136
+ markdownMethod: string | null;
137
+ markdownWordCount: number | null;
138
+ } | null>;
139
+ /**
140
+ * Compare intended truth (uploaded requirements) with implemented truth
141
+ * (crawl markdown + captured elements) for a single screen.
142
+ */
143
+ compareScreenToRequirements(tenantId: string, screenId: string): Promise<{
144
+ generatedAt: string;
145
+ projectId: any;
146
+ screen: {
147
+ id: any;
148
+ name: any;
149
+ url: any;
150
+ markdownMethod: any;
151
+ markdownWordCount: any;
152
+ };
153
+ summary: {
154
+ requirements: number;
155
+ capturedElements: any;
156
+ matches: number;
157
+ weakMatches: number;
158
+ documentedNotObserved: number;
159
+ observedNotDocumented: number;
160
+ };
161
+ gainedInformation: {
162
+ fromCrawl: any;
163
+ fromDocuments: string[];
164
+ generationGuidance: string[];
165
+ };
166
+ segments: TruthDiffSegment[];
167
+ } | null>;
168
+ /** Returns screen info needed to regenerate markdown from a DOM snapshot or captured elements. */
169
+ getScreenForMarkdownRegen(tenantId: string, screenId: string): Promise<{
170
+ projectId: string;
171
+ name: string;
172
+ url: string | null;
173
+ domSnapshotPath: string | null;
174
+ elements: Array<{
175
+ meaning: string | null;
176
+ text: string | null;
177
+ role: string | null;
178
+ expectedData: string | null;
179
+ notes: string | null;
180
+ isInteractive: boolean | null;
181
+ parentLandmark: string | null;
182
+ ariaState: unknown;
183
+ selector: string | null;
184
+ }>;
185
+ } | null>;
186
+ updateScreenA11y(tenantId: string, screenId: string, data: {
187
+ a11yScore?: number;
188
+ }): Promise<void>;
189
+ /**
190
+ * Phase 4.4: Persist axe-core violations for a screen.
191
+ * Deletes existing violations for the screen first (idempotent on re-crawl).
192
+ */
193
+ saveA11yViolations(tenantId: string, projectId: string, screenId: string, violations: Array<{
194
+ impact: string;
195
+ rule: string;
196
+ description: string;
197
+ nodeHtml?: string;
198
+ wcagTags?: string[];
199
+ }>, runId?: string): Promise<void>;
200
+ /** Reset all screens to ACTIVE for a new crawl — non-destructive replacement for clearScreens(). */
201
+ prepareForRecrawl(tenantId: string, projectId: string, jobId?: string): Promise<void>;
202
+ /** Mark screens not re-discovered in the latest crawl as PENDING_REMOVAL. */
203
+ markRemovedScreens(tenantId: string, projectId: string, discoveredUrls: string[], jobId?: string): Promise<any>;
204
+ /**
205
+ * Orphan-page report: classifies every ACTIVE screen as REACHABLE (has an
206
+ * inbound KAG edge, or is the crawl's entry point), GRAPH_ORPHAN (link-followed
207
+ * but nothing currently points to it), SITEMAP_ORPHAN (only reached because it
208
+ * was a configured seed URL — a true SEO-style orphan), or UNVERIFIED_ORPHAN
209
+ * (Skyvern-discovered, not yet covered by DOM link-wiring).
210
+ * See docs/research/25-orphan-page-detection-industry-comparison.md.
211
+ */
212
+ getOrphanScreens(tenantId: string, projectId: string): Promise<import("./orphan-classification.js").OrphanReport>;
213
+ /** G12: URL template coverage — groups screens by URL pattern to surface sampled vs. missing page types.
214
+ * e.g. /invoice/abc-123 and /invoice/xyz-456 both map to template /invoice/:id (sampleCount: 2). */
215
+ getTemplateCoverage(tenantId: string, projectId: string): Promise<{
216
+ templates: {
217
+ template: string;
218
+ sampleCount: number;
219
+ exampleScreenId: any;
220
+ exampleUrl: any;
221
+ exampleName: any;
222
+ }[];
223
+ totalTemplates: number;
224
+ totalScreens: any;
225
+ }>;
226
+ /** Return ACTIVE and PENDING_REMOVAL screens for a project diff view. */
227
+ getScreenDiff(tenantId: string, projectId: string): Promise<{
228
+ active: any;
229
+ pendingRemoval: any;
230
+ }>;
231
+ /** Hard-delete PENDING_REMOVAL screens (user confirmed removal). */
232
+ acceptRemovals(tenantId: string, projectId: string, screenIds?: string[]): Promise<{
233
+ deleted: any;
234
+ }>;
235
+ /** Restore PENDING_REMOVAL screens to ACTIVE (user chose to keep them). */
236
+ dismissRemovals(tenantId: string, projectId: string, screenIds?: string[]): Promise<{
237
+ restored: any;
238
+ }>;
239
+ /** Delete all screen groups for a project (called before re-grouping). */
240
+ clearScreenGroups(tenantId: string, projectId: string): Promise<Prisma.BatchPayload>;
241
+ /** Save a ScreenGroup record. */
242
+ saveScreenGroup(tenantId: string, projectId: string, data: {
243
+ name: string;
244
+ slug: string;
245
+ icon: string;
246
+ color: string;
247
+ description?: string;
248
+ order: number;
249
+ crawlJobId?: string;
250
+ }): Promise<{
251
+ tenantId: string;
252
+ id: string;
253
+ projectId: string;
254
+ name: string;
255
+ slug: string;
256
+ description: string | null;
257
+ color: string;
258
+ icon: string;
259
+ order: number;
260
+ crawlJobId: string | null;
261
+ createdAt: Date;
262
+ updatedAt: Date;
263
+ }>;
264
+ /** Assign multiple screens to a group by setting their groupId. */
265
+ assignScreensToGroup(tenantId: string, screenIds: string[], groupId: string): Promise<Prisma.BatchPayload>;
266
+ /** Get all screen groups for a project, including screen count. */
267
+ getScreenGroups(tenantId: string, projectId: string): Promise<any[]>;
268
+ /** Save an element with its MEANING and Phase 1 enriched metadata. */
269
+ saveElement(tenantId: string, projectId: string, screenId: string, meaning: string, role: string, expectedData?: string, notes?: string, text?: string, isInteractive?: boolean, boundingRect?: Record<string, number>, ariaState?: Record<string, string>, parentLandmark?: string, viewport?: string): Promise<{
270
+ tenantId: string;
271
+ screenId: string | null;
272
+ meaning: string | null;
273
+ id: string;
274
+ projectId: string;
275
+ role: string | null;
276
+ expectedData: string | null;
277
+ notes: string | null;
278
+ text: string | null;
279
+ isInteractive: boolean | null;
280
+ boundingRect: Prisma.JsonValue | null;
281
+ ariaState: Prisma.JsonValue | null;
282
+ parentLandmark: string | null;
283
+ viewport: string;
284
+ selector: string | null;
285
+ pageUrl: string | null;
286
+ screenshotUrl: string | null;
287
+ source: string;
288
+ metadata: Prisma.JsonValue | null;
289
+ transitionsToScreenId: string | null;
290
+ }>;
291
+ /** Phase 2: Update element hash; set elementHashChangedAt only when hash actually changes. */
292
+ updateElementHash(tenantId: string, screenId: string, newHash: string, previousHash: string | null): Promise<void>;
293
+ /** Phase 2: Return screens whose element hash changed after the most recent test generation. */
294
+ getScreensWithStaleTests(tenantId: string, projectId: string): Promise<any>;
295
+ /** Phase 3: Get storage path of previous screenshot version for pHash comparison. */
296
+ getPreviousScreenshotPath(tenantId: string, screenId: string, version: number): Promise<string | null>;
297
+ /** Phase 3: Upsert a ScreenDiff record after pHash comparison. */
298
+ saveScreenDiff(tenantId: string, projectId: string, screenId: string, fromVersion: number, toVersion: number, pHashDistance: number, changeLevel: string): Promise<void>;
299
+ /** Phase 3: Get most recent ScreenDiff for a screen. */
300
+ getLatestScreenDiff(tenantId: string, screenId: string): Promise<any>;
301
+ /** Phase 5: Replace probe observations for a screen (re-probe overwrites). */
302
+ saveProbeObservations(tenantId: string, projectId: string, screenId: string, observations: Array<{
303
+ elementMeaning: string;
304
+ observationType: string;
305
+ observedText: string;
306
+ triggeredBy: string;
307
+ }>): Promise<void>;
308
+ /** Save dynamic virtual screens discovered during interactive probing */
309
+ saveVirtualScreens(tenantId: string, projectId: string, parentScreenId: string, screens: Array<{
310
+ stateId: string;
311
+ triggerAction: string;
312
+ overlayType: string;
313
+ domSnapshotUrl?: string;
314
+ screenshotUrl?: string;
315
+ }>): Promise<void>;
316
+ /** Phase 5: Load probe observations for a screen (used by grounding). */
317
+ getProbeObservations(tenantId: string, screenId: string): Promise<any>;
318
+ /** Phase 4: Return full navigation graph with test coverage overlay. */
319
+ getCoverageGraph(tenantId: string, projectId: string): Promise<{
320
+ nodes: any;
321
+ edges: {
322
+ covered: boolean;
323
+ from: string;
324
+ to: string;
325
+ linkText?: string;
326
+ discoveryMethod: string;
327
+ }[];
328
+ stats: {
329
+ totalScreens: any;
330
+ coveredScreens: any;
331
+ coveragePercent: number;
332
+ };
333
+ }>;
334
+ /** Save a screenshot record linked to the screen and project. */
335
+ saveScreenshot(tenantId: string, projectId: string, screenId: string, storagePath: string, version?: number, viewport?: string, cloudUrl?: string, dpr?: number): Promise<{
336
+ tenantId: string;
337
+ screenId: string;
338
+ id: string;
339
+ projectId: string;
340
+ viewport: string;
341
+ version: number;
342
+ storagePath: string;
343
+ cloudUrl: string | null;
344
+ capturedAt: Date;
345
+ dpr: number | null;
346
+ }>;
347
+ /** Update cloudUrl on an existing screenshot after successful cloud upload. */
348
+ updateScreenshotCloudUrl(tenantId: string, screenshotId: string, cloudUrl: string): Promise<{
349
+ tenantId: string;
350
+ screenId: string;
351
+ id: string;
352
+ projectId: string;
353
+ viewport: string;
354
+ version: number;
355
+ storagePath: string;
356
+ cloudUrl: string | null;
357
+ capturedAt: Date;
358
+ dpr: number | null;
359
+ }>;
360
+ /** Delete all flow edges for a project — call before re-crawl to prevent accumulation. */
361
+ clearFlows(tenantId: string, projectId: string): Promise<Prisma.BatchPayload>;
362
+ /** Save a directed screen-map edge as a simple two-step flow. */
363
+ saveFlow(tenantId: string, projectId: string, name: string, fromScreenId: string, toScreenId: string): Promise<{
364
+ tenantId: string;
365
+ id: string;
366
+ projectId: string;
367
+ name: string;
368
+ steps: Prisma.JsonValue;
369
+ }>;
370
+ /** Resolve slug or CUID to the canonical project CUID. Returns null if not found. */
371
+ resolveProjectId(tenantId: string, idOrSlug: string): Promise<string | null>;
372
+ /** Save crawl credentials for a project (password + TOTP secret + storageState encrypted). */
373
+ saveCrawlConfig(tenantId: string, projectId: string, creds: {
374
+ appUrl?: string;
375
+ username?: string;
376
+ password?: string;
377
+ totpSecret?: string;
378
+ /** Playwright storageState JSON string — encrypted at rest, used to skip AI login. */
379
+ storageState?: string | null;
380
+ maxScreens?: number;
381
+ deviceProfiles?: string[];
382
+ /** Enable interactive probing during crawls (form submits, modal triggers). */
383
+ interactive?: boolean;
384
+ /** Seed the crawl queue from the project's sitemap.xml, in addition to link-following. */
385
+ useSitemap?: boolean;
386
+ /** Explicit sitemap URL override — defaults to `${appUrl origin}/sitemap.xml`. */
387
+ sitemapUrl?: string | null;
388
+ /** Crawl queue traversal order: BFS (default) | DFS | BEST_FIRST. */
389
+ traversalStrategy?: "BFS" | "DFS" | "BEST_FIRST";
390
+ /** BEST_FIRST only: URLs containing any of these (case-insensitive) are prioritized. */
391
+ bestFirstKeywords?: string[] | null;
392
+ /** HTTP Basic Auth credentials — stored in plain text (not secrets, just HTTP layer auth). */
393
+ httpBasicUsername?: string;
394
+ httpBasicPassword?: string;
395
+ /** Bearer/API token — encrypted at rest, injected as HTTP header, skips AI login. */
396
+ authToken?: string | null;
397
+ /** Header name for authToken (default: 'Authorization'). */
398
+ authTokenHeader?: string;
399
+ /** Prefix for authToken value (default: 'Bearer '). */
400
+ authTokenPrefix?: string;
401
+ /** CAPTCHA solving API key — encrypted at rest. */
402
+ captchaSolverApiKey?: string | null;
403
+ /** CAPTCHA solving service provider. */
404
+ captchaSolverProvider?: "2captcha" | "capsolver";
405
+ /** Named credential profiles for multi-role crawling. */
406
+ credentialProfiles?: Array<{
407
+ id?: string;
408
+ name: string;
409
+ username: string;
410
+ password?: string;
411
+ totpSecret?: string;
412
+ storageState?: string;
413
+ color?: string;
414
+ }>;
415
+ /** Path to a Chrome user data directory for profile inheritance (local deployments only). */
416
+ chromeProfilePath?: string;
417
+ /** GAP 5: MailSlurp API key for email OTP auto-retrieval — encrypted at rest. */
418
+ mailslurpApiKey?: string | null;
419
+ /** GAP 5: MailSlurp inbox ID (UUID). */
420
+ mailslurpInboxId?: string;
421
+ /** GAP 18: Workspace SSO provider ID. */
422
+ ssoProviderId?: string | null;
423
+ /** GAP 17: User ID of the person who captured/updated the session (audit log). */
424
+ sessionCapturedBy?: string;
425
+ /** Natural language login instructions for Stagehand AI login. */
426
+ loginInstructions?: string | null;
427
+ /** Recorded login steps from the Chrome extension step recorder. */
428
+ loginRecipe?: Array<{
429
+ action: string;
430
+ selector: string;
431
+ value?: string;
432
+ ms?: number;
433
+ }> | null;
434
+ /** HTTP/HTTPS/SOCKS5 proxy URL for routing browser through a proxy (e.g. residential proxy for bot-protected sites). */
435
+ proxyUrl?: string | null;
436
+ /** Use Camoufox (Firefox) as a Cloudflare cookie bootstrapper before the Chromium crawl. */
437
+ useFirefox?: boolean;
438
+ /** Additional seed URLs to prepend to the crawl queue (in addition to appUrl). */
439
+ seedUrls?: string[] | null;
440
+ /** URLs found by the last pre-crawl discovery (sitemap + robots) — stored for next-run diff. */
441
+ discoveredUrls?: string[] | null;
442
+ /** ISO timestamp of the last pre-crawl discovery run — gates the crawl button. */
443
+ discoveredAt?: string | null;
444
+ /** URL the browser landed on after successful login — used as Phase 3 discovery start URL. */
445
+ postLoginUrl?: string | null;
446
+ /** Error message from the last failed Validate & Capture attempt — shown on page reload. */
447
+ lastValidationError?: string | null;
448
+ /** ISO timestamp of the last failed Validate & Capture attempt. */
449
+ lastValidationErrorAt?: string | null;
450
+ /** Desktop viewport width in pixels (default: 1440). */
451
+ viewportWidth?: number | null;
452
+ /** Desktop viewport height in pixels (default: 900). */
453
+ viewportHeight?: number | null;
454
+ }): Promise<any>;
455
+ /** Get crawl credentials for a project (decrypted). */
456
+ getCrawlConfig(tenantId: string, projectId: string): Promise<{
457
+ appUrl?: string;
458
+ username?: string;
459
+ password?: string;
460
+ totpSecret?: string;
461
+ storageState?: string;
462
+ maxScreens?: number;
463
+ deviceProfiles?: string[];
464
+ /** Enable interactive probing during crawls (form submits, modal triggers). */
465
+ interactive?: boolean;
466
+ /** Seed the crawl queue from the project's sitemap.xml, in addition to link-following. */
467
+ useSitemap?: boolean;
468
+ /** Explicit sitemap URL override — defaults to `${appUrl origin}/sitemap.xml`. */
469
+ sitemapUrl?: string;
470
+ /** Crawl queue traversal order: BFS (default) | DFS | BEST_FIRST. */
471
+ traversalStrategy?: "BFS" | "DFS" | "BEST_FIRST";
472
+ /** BEST_FIRST only: URLs containing any of these (case-insensitive) are prioritized. */
473
+ bestFirstKeywords?: string[];
474
+ httpBasicUsername?: string;
475
+ httpBasicPassword?: string;
476
+ authToken?: string;
477
+ authTokenHeader?: string;
478
+ authTokenPrefix?: string;
479
+ captchaSolverApiKey?: string;
480
+ captchaSolverProvider?: "2captcha" | "capsolver";
481
+ credentialProfiles?: CredentialProfile[];
482
+ /** GAP 3: Path to Chrome user data directory for profile inheritance (local only). */
483
+ chromeProfilePath?: string;
484
+ /** GAP 5: MailSlurp API key (decrypted). */
485
+ mailslurpApiKey?: string;
486
+ /** GAP 5: MailSlurp inbox ID. */
487
+ mailslurpInboxId?: string;
488
+ /** GAP 18: Workspace SSO provider ID. */
489
+ ssoProviderId?: string;
490
+ /** GAP 17: ISO timestamp of when the session was last captured. */
491
+ sessionCapturedAt?: string;
492
+ /** GAP 17: User ID of who captured the session. */
493
+ sessionCapturedBy?: string;
494
+ /** Natural language login instructions for Stagehand AI login. */
495
+ loginInstructions?: string;
496
+ /** Recorded login steps from the Chrome extension step recorder. */
497
+ loginRecipe?: Array<{
498
+ action: string;
499
+ selector: string;
500
+ value?: string;
501
+ ms?: number;
502
+ }>;
503
+ /** HTTP/HTTPS/SOCKS5 proxy URL for bot-protected sites. */
504
+ proxyUrl?: string;
505
+ /** Use Camoufox (Firefox) as a Cloudflare cookie bootstrapper before the Chromium crawl. */
506
+ useFirefox?: boolean;
507
+ /** Additional seed URLs prepended to the crawl queue. */
508
+ seedUrls?: string[];
509
+ /** URLs found by the last pre-crawl discovery — used for next-run diff. */
510
+ discoveredUrls?: string[];
511
+ /** ISO timestamp of the last pre-crawl discovery run. */
512
+ discoveredAt?: string;
513
+ /** URL the browser landed on after successful login — used as Phase 3 discovery start URL. */
514
+ postLoginUrl?: string;
515
+ /** Error message from the last failed Validate & Capture attempt. */
516
+ lastValidationError?: string;
517
+ /** ISO timestamp of the last failed Validate & Capture attempt. */
518
+ lastValidationErrorAt?: string;
519
+ /** Desktop viewport width in pixels (default: 1440). */
520
+ viewportWidth?: number;
521
+ /** Desktop viewport height in pixels (default: 900). */
522
+ viewportHeight?: number;
523
+ } | null>;
524
+ /** Upsert a named credential profile inside crawlConfig.credentialProfiles. */
525
+ upsertCredentialProfile(tenantId: string, projectId: string, profile: {
526
+ id?: string;
527
+ name: string;
528
+ username: string;
529
+ password?: string;
530
+ totpSecret?: string;
531
+ storageState?: string;
532
+ color?: string;
533
+ }): Promise<void>;
534
+ /** Delete a named credential profile by id. */
535
+ deleteCredentialProfile(tenantId: string, projectId: string, profileId: string): Promise<void>;
536
+ /** Read a project with deduped screens (latest version per URL). Includes element diff vs prev version. */
537
+ getProjectMap(tenantId: string, projectIdOrSlug: string): Promise<{
538
+ screens: {
539
+ hasChange: boolean;
540
+ addedElements: string[];
541
+ removedElements: string[];
542
+ elementCountPrev: number;
543
+ totalVersions: number;
544
+ elements: {
545
+ tenantId: string;
546
+ screenId: string | null;
547
+ meaning: string | null;
548
+ id: string;
549
+ projectId: string;
550
+ role: string | null;
551
+ expectedData: string | null;
552
+ notes: string | null;
553
+ text: string | null;
554
+ isInteractive: boolean | null;
555
+ boundingRect: Prisma.JsonValue | null;
556
+ ariaState: Prisma.JsonValue | null;
557
+ parentLandmark: string | null;
558
+ viewport: string;
559
+ selector: string | null;
560
+ pageUrl: string | null;
561
+ screenshotUrl: string | null;
562
+ source: string;
563
+ metadata: Prisma.JsonValue | null;
564
+ transitionsToScreenId: string | null;
565
+ }[];
566
+ screenshots: {
567
+ tenantId: string;
568
+ screenId: string;
569
+ id: string;
570
+ projectId: string;
571
+ viewport: string;
572
+ version: number;
573
+ storagePath: string;
574
+ cloudUrl: string | null;
575
+ capturedAt: Date;
576
+ dpr: number | null;
577
+ }[];
578
+ screenDiffs: {
579
+ pHashDistance: number;
580
+ changeLevel: string;
581
+ }[];
582
+ tenantId: string;
583
+ id: string;
584
+ projectId: string;
585
+ name: string;
586
+ url: string | null;
587
+ version: number;
588
+ groupId: string | null;
589
+ figmaMatch: number | null;
590
+ a11yScore: number | null;
591
+ lastSeenAt: Date;
592
+ status: string;
593
+ lastCrawlJobId: string | null;
594
+ removedAt: Date | null;
595
+ removalReason: string | null;
596
+ discoveryMethod: string;
597
+ elementHash: string | null;
598
+ elementHashChangedAt: Date | null;
599
+ domSnapshotPath: string | null;
600
+ domSnapshotCloudUrl: string | null;
601
+ requiresAuth: boolean;
602
+ depth: number | null;
603
+ parentUrl: string | null;
604
+ roleAccess: string[];
605
+ markdownPath: string | null;
606
+ markdownContent: string | null;
607
+ markdownMethod: string | null;
608
+ markdownWordCount: number | null;
609
+ aiSummary: string | null;
610
+ }[];
611
+ flows: {
612
+ tenantId: string;
613
+ id: string;
614
+ projectId: string;
615
+ name: string;
616
+ steps: Prisma.JsonValue;
617
+ }[];
618
+ tenantId: string;
619
+ id: string;
620
+ name: string;
621
+ status: string;
622
+ slug: string;
623
+ description: string | null;
624
+ createdAt: Date;
625
+ updatedAt: Date;
626
+ appUrl: string | null;
627
+ figmaUrl: string | null;
628
+ appType: string | null;
629
+ crawlConfig: Prisma.JsonValue | null;
630
+ ciFailureThreshold: number | null;
631
+ slaThreshold: number | null;
632
+ visualDiffThreshold: number | null;
633
+ oversized: boolean;
634
+ restricted: boolean;
635
+ vsaEnabled: boolean;
636
+ workspaceId: string | null;
637
+ } | null>;
638
+ /** Read everything known about one screen (used by grounding). */
639
+ getScreenWithElements(tenantId: string, screenId: string): Promise<({
640
+ elements: {
641
+ tenantId: string;
642
+ screenId: string | null;
643
+ meaning: string | null;
644
+ id: string;
645
+ projectId: string;
646
+ role: string | null;
647
+ expectedData: string | null;
648
+ notes: string | null;
649
+ text: string | null;
650
+ isInteractive: boolean | null;
651
+ boundingRect: Prisma.JsonValue | null;
652
+ ariaState: Prisma.JsonValue | null;
653
+ parentLandmark: string | null;
654
+ viewport: string;
655
+ selector: string | null;
656
+ pageUrl: string | null;
657
+ screenshotUrl: string | null;
658
+ source: string;
659
+ metadata: Prisma.JsonValue | null;
660
+ transitionsToScreenId: string | null;
661
+ }[];
662
+ } & {
663
+ tenantId: string;
664
+ id: string;
665
+ projectId: string;
666
+ name: string;
667
+ url: string | null;
668
+ version: number;
669
+ groupId: string | null;
670
+ figmaMatch: number | null;
671
+ a11yScore: number | null;
672
+ lastSeenAt: Date;
673
+ status: string;
674
+ lastCrawlJobId: string | null;
675
+ removedAt: Date | null;
676
+ removalReason: string | null;
677
+ discoveryMethod: string;
678
+ elementHash: string | null;
679
+ elementHashChangedAt: Date | null;
680
+ domSnapshotPath: string | null;
681
+ domSnapshotCloudUrl: string | null;
682
+ requiresAuth: boolean;
683
+ depth: number | null;
684
+ parentUrl: string | null;
685
+ roleAccess: string[];
686
+ markdownPath: string | null;
687
+ markdownContent: string | null;
688
+ markdownMethod: string | null;
689
+ markdownWordCount: number | null;
690
+ aiSummary: string | null;
691
+ }) | null>;
692
+ getScreenOutgoingTransitions(tenantId: string, screenId: string, limit?: number): Promise<unknown>;
693
+ getLatestScreenshot(tenantId: string, screenId: string, viewport?: string): Promise<{
694
+ tenantId: string;
695
+ screenId: string;
696
+ id: string;
697
+ projectId: string;
698
+ viewport: string;
699
+ version: number;
700
+ storagePath: string;
701
+ cloudUrl: string | null;
702
+ capturedAt: Date;
703
+ dpr: number | null;
704
+ } | null>;
705
+ getScreenshotViewports(tenantId: string, screenId: string): Promise<string[]>;
706
+ /** Load one skill file by kind (backward-compat accessor). */
707
+ getSkillFile(tenantId: string, projectId: string, kind: string): Promise<{
708
+ tenantId: string;
709
+ id: string;
710
+ projectId: string;
711
+ source: string;
712
+ version: number;
713
+ updatedAt: Date;
714
+ kind: string;
715
+ scope: string;
716
+ summary: string | null;
717
+ tags: Prisma.JsonValue;
718
+ screenIds: Prisma.JsonValue;
719
+ content: string;
720
+ structuredData: Prisma.JsonValue;
721
+ provenance: Prisma.JsonValue;
722
+ confidence: number | null;
723
+ validationStatus: string;
724
+ validationSummary: string | null;
725
+ staleStatus: string;
726
+ staleReason: string | null;
727
+ generatorVersion: string | null;
728
+ approvedBy: string | null;
729
+ approvalStatus: string;
730
+ priorContent: string | null;
731
+ priorVersion: number | null;
732
+ priorGeneratedAt: Date | null;
733
+ changesSummary: string | null;
734
+ lastGenPromptTokens: number | null;
735
+ lastGenCompletionTokens: number | null;
736
+ lastGenCostUsd: number | null;
737
+ lastGenModel: string | null;
738
+ lastGenAt: Date | null;
739
+ } | null>;
740
+ /**
741
+ * Upsert a taxonomy skill file.
742
+ * Key: (projectId, kind, scope). Each project has exactly one file per kind.
743
+ * On update: version increments, content + summary + tags replaced, approvedBy cleared.
744
+ */
745
+ upsertSkillFile(tenantId: string, projectId: string, kind: string, content: string, opts?: {
746
+ summary?: string;
747
+ tags?: string[];
748
+ screenIds?: string[];
749
+ scope?: string;
750
+ source?: string;
751
+ approvedBy?: string;
752
+ approvalStatus?: string;
753
+ structuredData?: unknown;
754
+ provenance?: unknown[];
755
+ confidence?: number | null;
756
+ validationStatus?: string;
757
+ validationSummary?: string | null;
758
+ staleStatus?: string;
759
+ staleReason?: string | null;
760
+ generatorVersion?: string;
761
+ }): Promise<{
762
+ tenantId: string;
763
+ id: string;
764
+ projectId: string;
765
+ source: string;
766
+ version: number;
767
+ updatedAt: Date;
768
+ kind: string;
769
+ scope: string;
770
+ summary: string | null;
771
+ tags: Prisma.JsonValue;
772
+ screenIds: Prisma.JsonValue;
773
+ content: string;
774
+ structuredData: Prisma.JsonValue;
775
+ provenance: Prisma.JsonValue;
776
+ confidence: number | null;
777
+ validationStatus: string;
778
+ validationSummary: string | null;
779
+ staleStatus: string;
780
+ staleReason: string | null;
781
+ generatorVersion: string | null;
782
+ approvedBy: string | null;
783
+ approvalStatus: string;
784
+ priorContent: string | null;
785
+ priorVersion: number | null;
786
+ priorGeneratedAt: Date | null;
787
+ changesSummary: string | null;
788
+ lastGenPromptTokens: number | null;
789
+ lastGenCompletionTokens: number | null;
790
+ lastGenCostUsd: number | null;
791
+ lastGenModel: string | null;
792
+ lastGenAt: Date | null;
793
+ }>;
794
+ /** Mark a skill file as human-approved. */
795
+ approveSkillFile(tenantId: string, skillFileId: string, approverId: string): Promise<{
796
+ tenantId: string;
797
+ id: string;
798
+ projectId: string;
799
+ source: string;
800
+ version: number;
801
+ updatedAt: Date;
802
+ kind: string;
803
+ scope: string;
804
+ summary: string | null;
805
+ tags: Prisma.JsonValue;
806
+ screenIds: Prisma.JsonValue;
807
+ content: string;
808
+ structuredData: Prisma.JsonValue;
809
+ provenance: Prisma.JsonValue;
810
+ confidence: number | null;
811
+ validationStatus: string;
812
+ validationSummary: string | null;
813
+ staleStatus: string;
814
+ staleReason: string | null;
815
+ generatorVersion: string | null;
816
+ approvedBy: string | null;
817
+ approvalStatus: string;
818
+ priorContent: string | null;
819
+ priorVersion: number | null;
820
+ priorGeneratedAt: Date | null;
821
+ changesSummary: string | null;
822
+ lastGenPromptTokens: number | null;
823
+ lastGenCompletionTokens: number | null;
824
+ lastGenCostUsd: number | null;
825
+ lastGenModel: string | null;
826
+ lastGenAt: Date | null;
827
+ }>;
828
+ getSkillFileVersions(tenantId: string, projectId: string, kind: string): Promise<any>;
829
+ getSkillFileVersion(tenantId: string, projectId: string, kind: string, versionNum: number): Promise<any>;
830
+ /**
831
+ * Live data for the COVERAGE_MATRIX skill file: active screens with element
832
+ * counts, plus TestCase counts grouped by (screenId, kind). Retired test
833
+ * cases are excluded.
834
+ */
835
+ getCoverageMatrixData(tenantId: string, projectId: string): Promise<{
836
+ screens: {
837
+ id: any;
838
+ name: any;
839
+ url: any;
840
+ elementCount: any;
841
+ }[];
842
+ counts: {
843
+ screenId: any;
844
+ kind: any;
845
+ count: any;
846
+ }[];
847
+ }>;
848
+ /** Delete skill files matching given kinds — used to purge legacy kind names after migration. */
849
+ deleteSkillFilesByKinds(tenantId: string, projectId: string, kinds: string[]): Promise<Prisma.BatchPayload | undefined>;
850
+ /** List all skill files for a project, optionally filtered by kind. */
851
+ listSkillFiles(tenantId: string, projectId: string, kinds?: string[]): Promise<{
852
+ tenantId: string;
853
+ id: string;
854
+ projectId: string;
855
+ source: string;
856
+ version: number;
857
+ updatedAt: Date;
858
+ kind: string;
859
+ scope: string;
860
+ summary: string | null;
861
+ tags: Prisma.JsonValue;
862
+ screenIds: Prisma.JsonValue;
863
+ content: string;
864
+ structuredData: Prisma.JsonValue;
865
+ provenance: Prisma.JsonValue;
866
+ confidence: number | null;
867
+ validationStatus: string;
868
+ validationSummary: string | null;
869
+ staleStatus: string;
870
+ staleReason: string | null;
871
+ generatorVersion: string | null;
872
+ approvedBy: string | null;
873
+ approvalStatus: string;
874
+ priorContent: string | null;
875
+ priorVersion: number | null;
876
+ priorGeneratedAt: Date | null;
877
+ changesSummary: string | null;
878
+ lastGenPromptTokens: number | null;
879
+ lastGenCompletionTokens: number | null;
880
+ lastGenCostUsd: number | null;
881
+ lastGenModel: string | null;
882
+ lastGenAt: Date | null;
883
+ }[]>;
884
+ /**
885
+ * Retrieve skill files relevant for a specific screen — the graduated strategy:
886
+ *
887
+ * Global tier (always): PLATFORM, FUNCTION, DESIGN, THEME
888
+ * Selective tier: COMPONENT + ELEMENT files where screenIds is empty (applies to all)
889
+ * OR screenIds contains this screenId
890
+ *
891
+ * Callers with < 50 screens: use this method (loads all relevant).
892
+ * Callers with 300+ screens: add vector search on top of this for COMPONENT/ELEMENT.
893
+ */
894
+ getRelevantSkillFilesForScreen(tenantId: string, projectId: string, screenId: string): Promise<{
895
+ tenantId: string;
896
+ id: string;
897
+ projectId: string;
898
+ source: string;
899
+ version: number;
900
+ updatedAt: Date;
901
+ kind: string;
902
+ scope: string;
903
+ summary: string | null;
904
+ tags: Prisma.JsonValue;
905
+ screenIds: Prisma.JsonValue;
906
+ content: string;
907
+ structuredData: Prisma.JsonValue;
908
+ provenance: Prisma.JsonValue;
909
+ confidence: number | null;
910
+ validationStatus: string;
911
+ validationSummary: string | null;
912
+ staleStatus: string;
913
+ staleReason: string | null;
914
+ generatorVersion: string | null;
915
+ approvedBy: string | null;
916
+ approvalStatus: string;
917
+ priorContent: string | null;
918
+ priorVersion: number | null;
919
+ priorGeneratedAt: Date | null;
920
+ changesSummary: string | null;
921
+ lastGenPromptTokens: number | null;
922
+ lastGenCompletionTokens: number | null;
923
+ lastGenCostUsd: number | null;
924
+ lastGenModel: string | null;
925
+ lastGenAt: Date | null;
926
+ }[]>;
927
+ listArtifacts(tenantId: string, projectId: string, opts?: {
928
+ limit?: number;
929
+ offset?: number;
930
+ }): Promise<unknown>;
931
+ createArtifact(tenantId: string, projectId: string, data: {
932
+ name: string;
933
+ originalName: string;
934
+ mimeType: string;
935
+ size: number;
936
+ content: string;
937
+ kind: string;
938
+ uploadedBy: string;
939
+ storageKey?: string;
940
+ }): Promise<any>;
941
+ reindexArtifact(tenantId: string, projectId: string, artifactId: string, content: string, name: string): Promise<void>;
942
+ getArtifactUsage(tenantId: string, projectId: string, artifactId: string): Promise<{
943
+ testCaseCount: any;
944
+ suiteCount: number;
945
+ suites: {
946
+ id: string;
947
+ name: string;
948
+ count: number;
949
+ }[];
950
+ artifactName: any;
951
+ versionNum: any;
952
+ }>;
953
+ deleteArtifact(tenantId: string, artifactId: string, opts?: {
954
+ action?: "keep" | "retire" | "delete";
955
+ projectId?: string;
956
+ }): Promise<{
957
+ deletedArtifact: any;
958
+ testCasesAffected: number;
959
+ action: "keep" | "retire" | "delete";
960
+ }>;
961
+ updateArtifactIntelligence(tenantId: string, artifactId: string, data: {
962
+ summary?: string;
963
+ keywords?: string[];
964
+ docType?: string;
965
+ chunkCount?: number;
966
+ }): Promise<unknown>;
967
+ enrichChunkKeywords(tenantId: string, projectId: string, artifactId: string, content: string, name: string, keywords: string[]): Promise<void>;
968
+ /** Artifact ↔ test-case coverage matrix — pure DB, no LLM. */
969
+ getArtifactCoverage(tenantId: string, projectId: string): Promise<{
970
+ totalTestCases: any;
971
+ coveredByArtifact: number;
972
+ orphaned: number;
973
+ coveragePct: number;
974
+ artifacts: {
975
+ id: string;
976
+ name: string;
977
+ originalName: string;
978
+ kind: string;
979
+ testCases: {
980
+ id: string;
981
+ title: string;
982
+ status: string;
983
+ }[];
984
+ }[];
985
+ orphanedCases: {
986
+ id: string;
987
+ title: string;
988
+ status: string;
989
+ }[];
990
+ }>;
991
+ /**
992
+ * BM25 knowledge retrieval using Postgres full-text search (tsvector + GIN).
993
+ * Searches KnowledgeChunk rows indexed from skill files and artifacts.
994
+ * No LLM calls, no embeddings. <5ms per query.
995
+ *
996
+ * Falls back to in-memory scan of skill files if KnowledgeChunk table is empty
997
+ * (e.g. before first re-index after the 0014 migration).
998
+ */
999
+ searchKnowledge(tenantId: string, projectId: string, query: string, topK?: number, maxChunkChars?: number, cfg?: AppBrainConfig): Promise<{
1000
+ label: any;
1001
+ source: "SKILL_FILE";
1002
+ sourceId: any;
1003
+ score: number;
1004
+ content: any;
1005
+ }[] | {
1006
+ label: string;
1007
+ source: "SKILL_FILE" | "ARTIFACT";
1008
+ sourceId: string;
1009
+ score: any;
1010
+ content: string;
1011
+ }[]>;
1012
+ /** Returns aggregated skill file content from all OTHER projects in the tenant — for cross-project pattern learning */
1013
+ getCrossProjectPatterns(tenantId: string, currentProjectId: string, limit?: number): Promise<string>;
1014
+ listTestSuites(tenantId: string, projectId: string): Promise<any[]>;
1015
+ getTestSuiteWithCases(tenantId: string, suiteId: string): Promise<({
1016
+ testCases: ({
1017
+ screen: {
1018
+ roleAccess: string[];
1019
+ } | null;
1020
+ testData: {
1021
+ tenantId: string;
1022
+ id: string;
1023
+ projectId: string;
1024
+ source: string;
1025
+ kind: string;
1026
+ testCaseId: string;
1027
+ payload: Prisma.JsonValue;
1028
+ supersededAt: Date | null;
1029
+ }[];
1030
+ } & {
1031
+ tenantId: string;
1032
+ screenId: string | null;
1033
+ id: string;
1034
+ projectId: string;
1035
+ source: string;
1036
+ status: string;
1037
+ createdAt: Date;
1038
+ updatedAt: Date;
1039
+ steps: Prisma.JsonValue;
1040
+ kind: string;
1041
+ tags: Prisma.JsonValue;
1042
+ approvedBy: string | null;
1043
+ suiteId: string;
1044
+ ref: string | null;
1045
+ title: string;
1046
+ priority: string;
1047
+ preconditions: string | null;
1048
+ expectedResult: string | null;
1049
+ gherkin: string | null;
1050
+ approvedAt: Date | null;
1051
+ groundedOn: Prisma.JsonValue;
1052
+ caseRefs: Prisma.JsonValue;
1053
+ viewports: Prisma.JsonValue;
1054
+ viewportConstraint: string;
1055
+ techniqueOfRecord: string | null;
1056
+ traceTo: string | null;
1057
+ traceToExtra: Prisma.JsonValue;
1058
+ testDataInline: Prisma.JsonValue | null;
1059
+ dataNote: string | null;
1060
+ scriptNote: string | null;
1061
+ structuredSteps: Prisma.JsonValue | null;
1062
+ artifactId: string | null;
1063
+ scenarioId: string | null;
1064
+ suspect: boolean;
1065
+ suspectReason: string | null;
1066
+ flakyScore: number;
1067
+ consecutiveFlakes: number;
1068
+ lastFlakyAt: Date | null;
1069
+ quarantined: boolean;
1070
+ quarantinedAt: Date | null;
1071
+ quarantineReason: string | null;
1072
+ genPromptTokens: number | null;
1073
+ genCompletionTokens: number | null;
1074
+ genCostUsd: number | null;
1075
+ genModel: string | null;
1076
+ dataSetId: string | null;
1077
+ repoTargetPath: string | null;
1078
+ stepsKey: string | null;
1079
+ gherkinKey: string | null;
1080
+ contentHash: string | null;
1081
+ groundingHash: string | null;
1082
+ locatorsJson: Prisma.JsonValue | null;
1083
+ locatorsExtractedAt: Date | null;
1084
+ captureStepScreenshots: Prisma.JsonValue | null;
1085
+ })[];
1086
+ } & {
1087
+ tenantId: string;
1088
+ id: string;
1089
+ projectId: string;
1090
+ name: string;
1091
+ createdAt: Date;
1092
+ genModel: string | null;
1093
+ category: import(".prisma/client").$Enums.SuiteCategory | null;
1094
+ parentId: string | null;
1095
+ ownerUserId: string | null;
1096
+ genTotalTokens: number | null;
1097
+ genTotalCostUsd: number | null;
1098
+ genAt: Date | null;
1099
+ genScreenCount: number | null;
1100
+ coverageReport: Prisma.JsonValue | null;
1101
+ }) | null>;
1102
+ listTestCases(tenantId: string, projectId: string, filters?: {
1103
+ status?: string;
1104
+ suiteId?: string;
1105
+ kind?: string;
1106
+ priority?: string;
1107
+ screenIds?: string[];
1108
+ limit?: number;
1109
+ offset?: number;
1110
+ }): Promise<any[]>;
1111
+ listTestCaseSummaries(tenantId: string, projectId: string, limit?: number): Promise<Array<{
1112
+ id: string;
1113
+ ref?: string | null;
1114
+ title: string;
1115
+ screenId?: string | null;
1116
+ groundedOn: string[];
1117
+ steps: unknown;
1118
+ }>>;
1119
+ getScreenTestCounts(tenantId: string, projectId: string): Promise<Record<string, {
1120
+ total: number;
1121
+ approved: number;
1122
+ pending: number;
1123
+ suiteId?: string;
1124
+ }>>;
1125
+ getChangedScreenIds(tenantId: string, projectId: string, sinceDate?: Date): Promise<string[]>;
1126
+ getSuiteLastGenAt(tenantId: string, suiteId: string): Promise<Date | null>;
1127
+ getTestCaseWithContext(tenantId: string, testCaseId: string): Promise<({
1128
+ testData: {
1129
+ tenantId: string;
1130
+ id: string;
1131
+ projectId: string;
1132
+ source: string;
1133
+ kind: string;
1134
+ testCaseId: string;
1135
+ payload: Prisma.JsonValue;
1136
+ supersededAt: Date | null;
1137
+ }[];
1138
+ suite: {
1139
+ tenantId: string;
1140
+ id: string;
1141
+ projectId: string;
1142
+ name: string;
1143
+ createdAt: Date;
1144
+ genModel: string | null;
1145
+ category: import(".prisma/client").$Enums.SuiteCategory | null;
1146
+ parentId: string | null;
1147
+ ownerUserId: string | null;
1148
+ genTotalTokens: number | null;
1149
+ genTotalCostUsd: number | null;
1150
+ genAt: Date | null;
1151
+ genScreenCount: number | null;
1152
+ coverageReport: Prisma.JsonValue | null;
1153
+ };
1154
+ scripts: {
1155
+ tenantId: string;
1156
+ id: string;
1157
+ projectId: string;
1158
+ validationStatus: string;
1159
+ testCaseId: string;
1160
+ language: string;
1161
+ code: string;
1162
+ filePath: string | null;
1163
+ codeKey: string | null;
1164
+ validationErrors: string | null;
1165
+ pushedToPR: boolean;
1166
+ pushedPRId: string | null;
1167
+ }[];
1168
+ dataSet: ({
1169
+ rows: {
1170
+ data: Prisma.JsonValue;
1171
+ tenantId: string;
1172
+ id: string;
1173
+ dataSetId: string;
1174
+ rowIndex: number;
1175
+ }[];
1176
+ } & {
1177
+ tenantId: string;
1178
+ id: string;
1179
+ projectId: string;
1180
+ name: string;
1181
+ createdAt: Date;
1182
+ classification: string;
1183
+ environment: string | null;
1184
+ }) | null;
1185
+ } & {
1186
+ tenantId: string;
1187
+ screenId: string | null;
1188
+ id: string;
1189
+ projectId: string;
1190
+ source: string;
1191
+ status: string;
1192
+ createdAt: Date;
1193
+ updatedAt: Date;
1194
+ steps: Prisma.JsonValue;
1195
+ kind: string;
1196
+ tags: Prisma.JsonValue;
1197
+ approvedBy: string | null;
1198
+ suiteId: string;
1199
+ ref: string | null;
1200
+ title: string;
1201
+ priority: string;
1202
+ preconditions: string | null;
1203
+ expectedResult: string | null;
1204
+ gherkin: string | null;
1205
+ approvedAt: Date | null;
1206
+ groundedOn: Prisma.JsonValue;
1207
+ caseRefs: Prisma.JsonValue;
1208
+ viewports: Prisma.JsonValue;
1209
+ viewportConstraint: string;
1210
+ techniqueOfRecord: string | null;
1211
+ traceTo: string | null;
1212
+ traceToExtra: Prisma.JsonValue;
1213
+ testDataInline: Prisma.JsonValue | null;
1214
+ dataNote: string | null;
1215
+ scriptNote: string | null;
1216
+ structuredSteps: Prisma.JsonValue | null;
1217
+ artifactId: string | null;
1218
+ scenarioId: string | null;
1219
+ suspect: boolean;
1220
+ suspectReason: string | null;
1221
+ flakyScore: number;
1222
+ consecutiveFlakes: number;
1223
+ lastFlakyAt: Date | null;
1224
+ quarantined: boolean;
1225
+ quarantinedAt: Date | null;
1226
+ quarantineReason: string | null;
1227
+ genPromptTokens: number | null;
1228
+ genCompletionTokens: number | null;
1229
+ genCostUsd: number | null;
1230
+ genModel: string | null;
1231
+ dataSetId: string | null;
1232
+ repoTargetPath: string | null;
1233
+ stepsKey: string | null;
1234
+ gherkinKey: string | null;
1235
+ contentHash: string | null;
1236
+ groundingHash: string | null;
1237
+ locatorsJson: Prisma.JsonValue | null;
1238
+ locatorsExtractedAt: Date | null;
1239
+ captureStepScreenshots: Prisma.JsonValue | null;
1240
+ }) | null>;
1241
+ reviewTestCase(tenantId: string, testCaseId: string, opts: {
1242
+ status: string;
1243
+ updatedTitle?: string;
1244
+ updatedSteps?: string[];
1245
+ updatedPreconditions?: string | null;
1246
+ updatedExpectedResult?: string | null;
1247
+ updatedKind?: string;
1248
+ updatedPriority?: string;
1249
+ updatedTags?: string[];
1250
+ note?: string;
1251
+ reviewerId: string;
1252
+ approvedBy?: string | null;
1253
+ approvedAt?: Date | null;
1254
+ }): Promise<{
1255
+ tenantId: string;
1256
+ screenId: string | null;
1257
+ id: string;
1258
+ projectId: string;
1259
+ source: string;
1260
+ status: string;
1261
+ createdAt: Date;
1262
+ updatedAt: Date;
1263
+ steps: Prisma.JsonValue;
1264
+ kind: string;
1265
+ tags: Prisma.JsonValue;
1266
+ approvedBy: string | null;
1267
+ suiteId: string;
1268
+ ref: string | null;
1269
+ title: string;
1270
+ priority: string;
1271
+ preconditions: string | null;
1272
+ expectedResult: string | null;
1273
+ gherkin: string | null;
1274
+ approvedAt: Date | null;
1275
+ groundedOn: Prisma.JsonValue;
1276
+ caseRefs: Prisma.JsonValue;
1277
+ viewports: Prisma.JsonValue;
1278
+ viewportConstraint: string;
1279
+ techniqueOfRecord: string | null;
1280
+ traceTo: string | null;
1281
+ traceToExtra: Prisma.JsonValue;
1282
+ testDataInline: Prisma.JsonValue | null;
1283
+ dataNote: string | null;
1284
+ scriptNote: string | null;
1285
+ structuredSteps: Prisma.JsonValue | null;
1286
+ artifactId: string | null;
1287
+ scenarioId: string | null;
1288
+ suspect: boolean;
1289
+ suspectReason: string | null;
1290
+ flakyScore: number;
1291
+ consecutiveFlakes: number;
1292
+ lastFlakyAt: Date | null;
1293
+ quarantined: boolean;
1294
+ quarantinedAt: Date | null;
1295
+ quarantineReason: string | null;
1296
+ genPromptTokens: number | null;
1297
+ genCompletionTokens: number | null;
1298
+ genCostUsd: number | null;
1299
+ genModel: string | null;
1300
+ dataSetId: string | null;
1301
+ repoTargetPath: string | null;
1302
+ stepsKey: string | null;
1303
+ gherkinKey: string | null;
1304
+ contentHash: string | null;
1305
+ groundingHash: string | null;
1306
+ locatorsJson: Prisma.JsonValue | null;
1307
+ locatorsExtractedAt: Date | null;
1308
+ captureStepScreenshots: Prisma.JsonValue | null;
1309
+ }>;
1310
+ bulkReviewTestCases(tenantId: string, projectId: string, ids: string[], status: string, reviewerId: string): Promise<Prisma.BatchPayload>;
1311
+ addTestCaseToSuite(tenantId: string, projectId: string, suiteId: string, testCase: {
1312
+ title: string;
1313
+ kind: TestKind;
1314
+ steps: unknown[];
1315
+ groundedOn: string[] | Array<{
1316
+ id: string;
1317
+ text: string;
1318
+ }>;
1319
+ ref?: string;
1320
+ screenId?: string | null;
1321
+ priority?: string;
1322
+ preconditions?: string | null;
1323
+ expectedResult?: string | null;
1324
+ tags?: string[];
1325
+ source?: string;
1326
+ }): Promise<{
1327
+ tenantId: string;
1328
+ screenId: string | null;
1329
+ id: string;
1330
+ projectId: string;
1331
+ source: string;
1332
+ status: string;
1333
+ createdAt: Date;
1334
+ updatedAt: Date;
1335
+ steps: Prisma.JsonValue;
1336
+ kind: string;
1337
+ tags: Prisma.JsonValue;
1338
+ approvedBy: string | null;
1339
+ suiteId: string;
1340
+ ref: string | null;
1341
+ title: string;
1342
+ priority: string;
1343
+ preconditions: string | null;
1344
+ expectedResult: string | null;
1345
+ gherkin: string | null;
1346
+ approvedAt: Date | null;
1347
+ groundedOn: Prisma.JsonValue;
1348
+ caseRefs: Prisma.JsonValue;
1349
+ viewports: Prisma.JsonValue;
1350
+ viewportConstraint: string;
1351
+ techniqueOfRecord: string | null;
1352
+ traceTo: string | null;
1353
+ traceToExtra: Prisma.JsonValue;
1354
+ testDataInline: Prisma.JsonValue | null;
1355
+ dataNote: string | null;
1356
+ scriptNote: string | null;
1357
+ structuredSteps: Prisma.JsonValue | null;
1358
+ artifactId: string | null;
1359
+ scenarioId: string | null;
1360
+ suspect: boolean;
1361
+ suspectReason: string | null;
1362
+ flakyScore: number;
1363
+ consecutiveFlakes: number;
1364
+ lastFlakyAt: Date | null;
1365
+ quarantined: boolean;
1366
+ quarantinedAt: Date | null;
1367
+ quarantineReason: string | null;
1368
+ genPromptTokens: number | null;
1369
+ genCompletionTokens: number | null;
1370
+ genCostUsd: number | null;
1371
+ genModel: string | null;
1372
+ dataSetId: string | null;
1373
+ repoTargetPath: string | null;
1374
+ stepsKey: string | null;
1375
+ gherkinKey: string | null;
1376
+ contentHash: string | null;
1377
+ groundingHash: string | null;
1378
+ locatorsJson: Prisma.JsonValue | null;
1379
+ locatorsExtractedAt: Date | null;
1380
+ captureStepScreenshots: Prisma.JsonValue | null;
1381
+ }>;
1382
+ saveTestData(tenantId: string, projectId: string, testCaseId: string, dataSets: Array<{
1383
+ label: string;
1384
+ category: string;
1385
+ values: Record<string, string>;
1386
+ expectedOutcome: string;
1387
+ source?: string;
1388
+ }>): Promise<{
1389
+ tenantId: string;
1390
+ id: string;
1391
+ projectId: string;
1392
+ source: string;
1393
+ kind: string;
1394
+ testCaseId: string;
1395
+ payload: Prisma.JsonValue;
1396
+ supersededAt: Date | null;
1397
+ }[]>;
1398
+ /**
1399
+ * R2/H4: Rebuild TestCase.gherkin as a data-driven `Scenario Outline: + Examples:` table
1400
+ * using all non-FALLBACK TestData rows for the given test case.
1401
+ * Called after any TestData row is added or updated.
1402
+ */
1403
+ rebuildGherkinOutlineForCase(tenantId: string, testCaseId: string): Promise<void>;
1404
+ getTestDataForCase(tenantId: string, testCaseId: string): Promise<{
1405
+ tenantId: string;
1406
+ id: string;
1407
+ projectId: string;
1408
+ source: string;
1409
+ kind: string;
1410
+ testCaseId: string;
1411
+ payload: Prisma.JsonValue;
1412
+ supersededAt: Date | null;
1413
+ }[]>;
1414
+ /** Returns screen elements (with notes/selector fields) needed to compute Playwright locators. */
1415
+ getScreenElementsForLocators(tenantId: string, screenId: string): Promise<Array<{
1416
+ role: string | null;
1417
+ meaning: string | null;
1418
+ text: string | null;
1419
+ notes: string | null;
1420
+ selector: string | null;
1421
+ isInteractive: boolean | null;
1422
+ ariaState: Record<string, unknown> | null;
1423
+ }>>;
1424
+ /** Persist computed Playwright locators on a test case. */
1425
+ saveLocatorsJson(tenantId: string, testCaseId: string, locators: Array<{
1426
+ key: string;
1427
+ expression: string;
1428
+ elementType: string;
1429
+ confidence: string;
1430
+ source: string;
1431
+ description: string;
1432
+ }>): Promise<void>;
1433
+ /** Get stored locators for a test case (may be null if not yet extracted). */
1434
+ getLocatorsJson(tenantId: string, testCaseId: string): Promise<Array<{
1435
+ key: string;
1436
+ expression: string;
1437
+ elementType: string;
1438
+ confidence: string;
1439
+ source: string;
1440
+ description: string;
1441
+ }> | null>;
1442
+ linkOrCreateSharedDataSet(tenantId: string, projectId: string, testCaseId: string, fieldKeys: string[], dataSets: Array<{
1443
+ label: string;
1444
+ category: string;
1445
+ values: Record<string, string>;
1446
+ expectedOutcome: string;
1447
+ }>): Promise<void>;
1448
+ getFieldsForCase(tenantId: string, projectId: string, groundedOn: string[]): Promise<import("@taodigital/core").FieldSchema[]>;
1449
+ upsertAppField(tenantId: string, projectId: string, field: import("@taodigital/core").FieldSchema): Promise<{
1450
+ tenantId: string;
1451
+ id: string;
1452
+ projectId: string;
1453
+ notes: string | null;
1454
+ status: string;
1455
+ createdAt: Date;
1456
+ updatedAt: Date;
1457
+ fieldKey: string;
1458
+ label: string;
1459
+ elementRole: string;
1460
+ dataBearing: boolean;
1461
+ dataType: string;
1462
+ required: boolean;
1463
+ minLength: number | null;
1464
+ maxLength: number | null;
1465
+ minValue: number | null;
1466
+ maxValue: number | null;
1467
+ precision: number | null;
1468
+ regex: string | null;
1469
+ enumValues: Prisma.JsonValue | null;
1470
+ piiClass: string;
1471
+ exampleValue: string | null;
1472
+ relationGroup: string | null;
1473
+ }>;
1474
+ listAppFields(tenantId: string, projectId: string): Promise<{
1475
+ tenantId: string;
1476
+ id: string;
1477
+ projectId: string;
1478
+ notes: string | null;
1479
+ status: string;
1480
+ createdAt: Date;
1481
+ updatedAt: Date;
1482
+ fieldKey: string;
1483
+ label: string;
1484
+ elementRole: string;
1485
+ dataBearing: boolean;
1486
+ dataType: string;
1487
+ required: boolean;
1488
+ minLength: number | null;
1489
+ maxLength: number | null;
1490
+ minValue: number | null;
1491
+ maxValue: number | null;
1492
+ precision: number | null;
1493
+ regex: string | null;
1494
+ enumValues: Prisma.JsonValue | null;
1495
+ piiClass: string;
1496
+ exampleValue: string | null;
1497
+ relationGroup: string | null;
1498
+ }[]>;
1499
+ confirmAppField(tenantId: string, projectId: string, fieldKey: string, patch: Partial<import("@taodigital/core").FieldSchema>): Promise<{
1500
+ tenantId: string;
1501
+ id: string;
1502
+ projectId: string;
1503
+ notes: string | null;
1504
+ status: string;
1505
+ createdAt: Date;
1506
+ updatedAt: Date;
1507
+ fieldKey: string;
1508
+ label: string;
1509
+ elementRole: string;
1510
+ dataBearing: boolean;
1511
+ dataType: string;
1512
+ required: boolean;
1513
+ minLength: number | null;
1514
+ maxLength: number | null;
1515
+ minValue: number | null;
1516
+ maxValue: number | null;
1517
+ precision: number | null;
1518
+ regex: string | null;
1519
+ enumValues: Prisma.JsonValue | null;
1520
+ piiClass: string;
1521
+ exampleValue: string | null;
1522
+ relationGroup: string | null;
1523
+ }>;
1524
+ deleteAppField(tenantId: string, projectId: string, fieldKey: string): Promise<{
1525
+ tenantId: string;
1526
+ id: string;
1527
+ projectId: string;
1528
+ notes: string | null;
1529
+ status: string;
1530
+ createdAt: Date;
1531
+ updatedAt: Date;
1532
+ fieldKey: string;
1533
+ label: string;
1534
+ elementRole: string;
1535
+ dataBearing: boolean;
1536
+ dataType: string;
1537
+ required: boolean;
1538
+ minLength: number | null;
1539
+ maxLength: number | null;
1540
+ minValue: number | null;
1541
+ maxValue: number | null;
1542
+ precision: number | null;
1543
+ regex: string | null;
1544
+ enumValues: Prisma.JsonValue | null;
1545
+ piiClass: string;
1546
+ exampleValue: string | null;
1547
+ relationGroup: string | null;
1548
+ }>;
1549
+ saveTestScript(tenantId: string, projectId: string, testCaseId: string, language: string, code: string, filePath?: string): Promise<{
1550
+ tenantId: string;
1551
+ id: string;
1552
+ projectId: string;
1553
+ validationStatus: string;
1554
+ testCaseId: string;
1555
+ language: string;
1556
+ code: string;
1557
+ filePath: string | null;
1558
+ codeKey: string | null;
1559
+ validationErrors: string | null;
1560
+ pushedToPR: boolean;
1561
+ pushedPRId: string | null;
1562
+ }>;
1563
+ getTestScript(tenantId: string, testCaseId: string, language: string): Promise<{
1564
+ tenantId: string;
1565
+ id: string;
1566
+ projectId: string;
1567
+ validationStatus: string;
1568
+ testCaseId: string;
1569
+ language: string;
1570
+ code: string;
1571
+ filePath: string | null;
1572
+ codeKey: string | null;
1573
+ validationErrors: string | null;
1574
+ pushedToPR: boolean;
1575
+ pushedPRId: string | null;
1576
+ } | null>;
1577
+ getTestScriptVersions(tenantId: string, testCaseId: string, language: string): Promise<unknown>;
1578
+ getTestScriptVersion(tenantId: string, testCaseId: string, language: string, versionNum: number): Promise<unknown>;
1579
+ saveScriptBundle(tenantId: string, projectId: string, scopeType: "SUITE" | "PACK", scopeId: string, language: string, data: {
1580
+ files: unknown;
1581
+ basePage?: string | null;
1582
+ caseCount?: number;
1583
+ }): Promise<unknown>;
1584
+ getScriptBundle(tenantId: string, projectId: string, scopeType: "SUITE" | "PACK", scopeId: string, language: string): Promise<unknown>;
1585
+ deleteScriptBundle(tenantId: string, projectId: string, scopeType: "SUITE" | "PACK", scopeId: string, language: string): Promise<void>;
1586
+ deleteTestScriptsForSuite(tenantId: string, suiteId: string, language: string): Promise<void>;
1587
+ createTestRun(tenantId: string, projectId: string, opts: {
1588
+ suiteId?: string;
1589
+ trigger: string;
1590
+ config?: any;
1591
+ totalShards?: number;
1592
+ githubRepo?: string;
1593
+ githubSha?: string;
1594
+ githubRef?: string;
1595
+ triggeredBy?: string;
1596
+ }): Promise<{
1597
+ tenantId: string;
1598
+ id: string;
1599
+ projectId: string;
1600
+ status: string;
1601
+ summary: Prisma.JsonValue | null;
1602
+ suiteId: string | null;
1603
+ trigger: string;
1604
+ config: Prisma.JsonValue | null;
1605
+ totalShards: number | null;
1606
+ githubRepo: string | null;
1607
+ githubSha: string | null;
1608
+ githubRef: string | null;
1609
+ triggeredBy: string | null;
1610
+ startedAt: Date;
1611
+ finishedAt: Date | null;
1612
+ }>;
1613
+ createTestRunShards(tenantId: string, runId: string, shards: Array<{
1614
+ shardIndex: number;
1615
+ browser: string;
1616
+ }>): Promise<Prisma.BatchPayload>;
1617
+ getTestRun(tenantId: string, runId: string): Promise<({
1618
+ results: {
1619
+ error: string | null;
1620
+ tenantId: string;
1621
+ id: string;
1622
+ status: string;
1623
+ testCaseId: string;
1624
+ runId: string;
1625
+ duration: number | null;
1626
+ screenshotPath: string | null;
1627
+ logs: Prisma.JsonValue | null;
1628
+ healedAt: Date | null;
1629
+ failureAnalysis: string | null;
1630
+ failureAnalysisAt: Date | null;
1631
+ dataRowId: string | null;
1632
+ dataRowIndex: number | null;
1633
+ }[];
1634
+ } & {
1635
+ tenantId: string;
1636
+ id: string;
1637
+ projectId: string;
1638
+ status: string;
1639
+ summary: Prisma.JsonValue | null;
1640
+ suiteId: string | null;
1641
+ trigger: string;
1642
+ config: Prisma.JsonValue | null;
1643
+ totalShards: number | null;
1644
+ githubRepo: string | null;
1645
+ githubSha: string | null;
1646
+ githubRef: string | null;
1647
+ triggeredBy: string | null;
1648
+ startedAt: Date;
1649
+ finishedAt: Date | null;
1650
+ }) | null>;
1651
+ listTestRuns(tenantId: string, projectId: string): Promise<{
1652
+ tenantId: string;
1653
+ id: string;
1654
+ projectId: string;
1655
+ status: string;
1656
+ summary: Prisma.JsonValue | null;
1657
+ suiteId: string | null;
1658
+ trigger: string;
1659
+ config: Prisma.JsonValue | null;
1660
+ totalShards: number | null;
1661
+ githubRepo: string | null;
1662
+ githubSha: string | null;
1663
+ githubRef: string | null;
1664
+ triggeredBy: string | null;
1665
+ startedAt: Date;
1666
+ finishedAt: Date | null;
1667
+ }[]>;
1668
+ saveTestResult(tenantId: string, result: {
1669
+ runId: string;
1670
+ testCaseId: string;
1671
+ status: string;
1672
+ duration?: number;
1673
+ error?: string;
1674
+ failureAnalysis?: string;
1675
+ failureAnalysisAt?: Date;
1676
+ dataRowId?: string;
1677
+ dataRowIndex?: number;
1678
+ logs?: Record<string, unknown>;
1679
+ }): Promise<{
1680
+ error: string | null;
1681
+ tenantId: string;
1682
+ id: string;
1683
+ status: string;
1684
+ testCaseId: string;
1685
+ runId: string;
1686
+ duration: number | null;
1687
+ screenshotPath: string | null;
1688
+ logs: Prisma.JsonValue | null;
1689
+ healedAt: Date | null;
1690
+ failureAnalysis: string | null;
1691
+ failureAnalysisAt: Date | null;
1692
+ dataRowId: string | null;
1693
+ dataRowIndex: number | null;
1694
+ }>;
1695
+ completeTestRun(tenantId: string, runId: string, summary: {
1696
+ total: number;
1697
+ passed: number;
1698
+ failed: number;
1699
+ skipped: number;
1700
+ duration: number;
1701
+ }): Promise<{
1702
+ tenantId: string;
1703
+ id: string;
1704
+ projectId: string;
1705
+ status: string;
1706
+ summary: Prisma.JsonValue | null;
1707
+ suiteId: string | null;
1708
+ trigger: string;
1709
+ config: Prisma.JsonValue | null;
1710
+ totalShards: number | null;
1711
+ githubRepo: string | null;
1712
+ githubSha: string | null;
1713
+ githubRef: string | null;
1714
+ triggeredBy: string | null;
1715
+ startedAt: Date;
1716
+ finishedAt: Date | null;
1717
+ }>;
1718
+ failTestRun(tenantId: string, runId: string, error: string): Promise<{
1719
+ tenantId: string;
1720
+ id: string;
1721
+ projectId: string;
1722
+ status: string;
1723
+ summary: Prisma.JsonValue | null;
1724
+ suiteId: string | null;
1725
+ trigger: string;
1726
+ config: Prisma.JsonValue | null;
1727
+ totalShards: number | null;
1728
+ githubRepo: string | null;
1729
+ githubSha: string | null;
1730
+ githubRef: string | null;
1731
+ triggeredBy: string | null;
1732
+ startedAt: Date;
1733
+ finishedAt: Date | null;
1734
+ }>;
1735
+ cancelTestRun(tenantId: string, runId: string): Promise<{
1736
+ tenantId: string;
1737
+ id: string;
1738
+ projectId: string;
1739
+ status: string;
1740
+ summary: Prisma.JsonValue | null;
1741
+ suiteId: string | null;
1742
+ trigger: string;
1743
+ config: Prisma.JsonValue | null;
1744
+ totalShards: number | null;
1745
+ githubRepo: string | null;
1746
+ githubSha: string | null;
1747
+ githubRef: string | null;
1748
+ triggeredBy: string | null;
1749
+ startedAt: Date;
1750
+ finishedAt: Date | null;
1751
+ }>;
1752
+ getTestCaseForExecution(tenantId: string, testCaseId: string): Promise<({
1753
+ screen: ({
1754
+ project: {
1755
+ tenantId: string;
1756
+ id: string;
1757
+ name: string;
1758
+ status: string;
1759
+ slug: string;
1760
+ description: string | null;
1761
+ createdAt: Date;
1762
+ updatedAt: Date;
1763
+ appUrl: string | null;
1764
+ figmaUrl: string | null;
1765
+ appType: string | null;
1766
+ crawlConfig: Prisma.JsonValue | null;
1767
+ ciFailureThreshold: number | null;
1768
+ slaThreshold: number | null;
1769
+ visualDiffThreshold: number | null;
1770
+ oversized: boolean;
1771
+ restricted: boolean;
1772
+ vsaEnabled: boolean;
1773
+ workspaceId: string | null;
1774
+ };
1775
+ } & {
1776
+ tenantId: string;
1777
+ id: string;
1778
+ projectId: string;
1779
+ name: string;
1780
+ url: string | null;
1781
+ version: number;
1782
+ groupId: string | null;
1783
+ figmaMatch: number | null;
1784
+ a11yScore: number | null;
1785
+ lastSeenAt: Date;
1786
+ status: string;
1787
+ lastCrawlJobId: string | null;
1788
+ removedAt: Date | null;
1789
+ removalReason: string | null;
1790
+ discoveryMethod: string;
1791
+ elementHash: string | null;
1792
+ elementHashChangedAt: Date | null;
1793
+ domSnapshotPath: string | null;
1794
+ domSnapshotCloudUrl: string | null;
1795
+ requiresAuth: boolean;
1796
+ depth: number | null;
1797
+ parentUrl: string | null;
1798
+ roleAccess: string[];
1799
+ markdownPath: string | null;
1800
+ markdownContent: string | null;
1801
+ markdownMethod: string | null;
1802
+ markdownWordCount: number | null;
1803
+ aiSummary: string | null;
1804
+ }) | null;
1805
+ dataSet: ({
1806
+ rows: {
1807
+ data: Prisma.JsonValue;
1808
+ tenantId: string;
1809
+ id: string;
1810
+ dataSetId: string;
1811
+ rowIndex: number;
1812
+ }[];
1813
+ } & {
1814
+ tenantId: string;
1815
+ id: string;
1816
+ projectId: string;
1817
+ name: string;
1818
+ createdAt: Date;
1819
+ classification: string;
1820
+ environment: string | null;
1821
+ }) | null;
1822
+ } & {
1823
+ tenantId: string;
1824
+ screenId: string | null;
1825
+ id: string;
1826
+ projectId: string;
1827
+ source: string;
1828
+ status: string;
1829
+ createdAt: Date;
1830
+ updatedAt: Date;
1831
+ steps: Prisma.JsonValue;
1832
+ kind: string;
1833
+ tags: Prisma.JsonValue;
1834
+ approvedBy: string | null;
1835
+ suiteId: string;
1836
+ ref: string | null;
1837
+ title: string;
1838
+ priority: string;
1839
+ preconditions: string | null;
1840
+ expectedResult: string | null;
1841
+ gherkin: string | null;
1842
+ approvedAt: Date | null;
1843
+ groundedOn: Prisma.JsonValue;
1844
+ caseRefs: Prisma.JsonValue;
1845
+ viewports: Prisma.JsonValue;
1846
+ viewportConstraint: string;
1847
+ techniqueOfRecord: string | null;
1848
+ traceTo: string | null;
1849
+ traceToExtra: Prisma.JsonValue;
1850
+ testDataInline: Prisma.JsonValue | null;
1851
+ dataNote: string | null;
1852
+ scriptNote: string | null;
1853
+ structuredSteps: Prisma.JsonValue | null;
1854
+ artifactId: string | null;
1855
+ scenarioId: string | null;
1856
+ suspect: boolean;
1857
+ suspectReason: string | null;
1858
+ flakyScore: number;
1859
+ consecutiveFlakes: number;
1860
+ lastFlakyAt: Date | null;
1861
+ quarantined: boolean;
1862
+ quarantinedAt: Date | null;
1863
+ quarantineReason: string | null;
1864
+ genPromptTokens: number | null;
1865
+ genCompletionTokens: number | null;
1866
+ genCostUsd: number | null;
1867
+ genModel: string | null;
1868
+ dataSetId: string | null;
1869
+ repoTargetPath: string | null;
1870
+ stepsKey: string | null;
1871
+ gherkinKey: string | null;
1872
+ contentHash: string | null;
1873
+ groundingHash: string | null;
1874
+ locatorsJson: Prisma.JsonValue | null;
1875
+ locatorsExtractedAt: Date | null;
1876
+ captureStepScreenshots: Prisma.JsonValue | null;
1877
+ }) | null>;
1878
+ createTestCaseRun(tenantId: string, testCaseId: string, viewport: string, browser?: string): Promise<unknown>;
1879
+ updateTestCaseRun(tenantId: string, runId: string, data: {
1880
+ status?: string;
1881
+ executionPlan?: any;
1882
+ stepResults?: any;
1883
+ totalDuration?: number;
1884
+ errorStep?: number | null;
1885
+ errorMessage?: string;
1886
+ screenshotPath?: string | null;
1887
+ completedAt?: Date;
1888
+ }): Promise<unknown>;
1889
+ getTestCaseRun(tenantId: string, runId: string): Promise<unknown>;
1890
+ /**
1891
+ * Recomputes rolling flakiness stats for a test case from its run history and
1892
+ * writes them back to TestCase. Call after every settled TestCaseRun (PASSED/
1893
+ * FAILED/FLAKY/ERROR) — ERROR rows are excluded from the window itself (infra
1894
+ * failures, not app flakiness) but recording still runs so stats stay current.
1895
+ *
1896
+ * flakyScore: fraction of the last WINDOW_SIZE non-ERROR outcomes that were
1897
+ * FAILED or FLAKY (0.0-1.0) — see docs/13-TEST-FLAKINESS-DETECTION.md.
1898
+ * consecutiveFlakes: current losing streak (FAILED/FLAKY), reset by a PASS.
1899
+ * Auto-quarantine requires BOTH a high flaky score AND at least one PASSED in
1900
+ * the window — a consistently-failing test is a bug, not flakiness, and must
1901
+ * not be silently hidden from the suite by quarantine.
1902
+ */
1903
+ recordFlakinessOutcome(tenantId: string, testCaseId: string): Promise<{
1904
+ tenantId: string;
1905
+ screenId: string | null;
1906
+ id: string;
1907
+ projectId: string;
1908
+ source: string;
1909
+ status: string;
1910
+ createdAt: Date;
1911
+ updatedAt: Date;
1912
+ steps: Prisma.JsonValue;
1913
+ kind: string;
1914
+ tags: Prisma.JsonValue;
1915
+ approvedBy: string | null;
1916
+ suiteId: string;
1917
+ ref: string | null;
1918
+ title: string;
1919
+ priority: string;
1920
+ preconditions: string | null;
1921
+ expectedResult: string | null;
1922
+ gherkin: string | null;
1923
+ approvedAt: Date | null;
1924
+ groundedOn: Prisma.JsonValue;
1925
+ caseRefs: Prisma.JsonValue;
1926
+ viewports: Prisma.JsonValue;
1927
+ viewportConstraint: string;
1928
+ techniqueOfRecord: string | null;
1929
+ traceTo: string | null;
1930
+ traceToExtra: Prisma.JsonValue;
1931
+ testDataInline: Prisma.JsonValue | null;
1932
+ dataNote: string | null;
1933
+ scriptNote: string | null;
1934
+ structuredSteps: Prisma.JsonValue | null;
1935
+ artifactId: string | null;
1936
+ scenarioId: string | null;
1937
+ suspect: boolean;
1938
+ suspectReason: string | null;
1939
+ flakyScore: number;
1940
+ consecutiveFlakes: number;
1941
+ lastFlakyAt: Date | null;
1942
+ quarantined: boolean;
1943
+ quarantinedAt: Date | null;
1944
+ quarantineReason: string | null;
1945
+ genPromptTokens: number | null;
1946
+ genCompletionTokens: number | null;
1947
+ genCostUsd: number | null;
1948
+ genModel: string | null;
1949
+ dataSetId: string | null;
1950
+ repoTargetPath: string | null;
1951
+ stepsKey: string | null;
1952
+ gherkinKey: string | null;
1953
+ contentHash: string | null;
1954
+ groundingHash: string | null;
1955
+ locatorsJson: Prisma.JsonValue | null;
1956
+ locatorsExtractedAt: Date | null;
1957
+ captureStepScreenshots: Prisma.JsonValue | null;
1958
+ } | null>;
1959
+ /** Human-driven quarantine toggle. Auto-quarantine never clears itself — lifting it is always explicit. */
1960
+ setTestCaseQuarantine(tenantId: string, testCaseId: string, quarantined: boolean, reason?: string): Promise<{
1961
+ tenantId: string;
1962
+ screenId: string | null;
1963
+ id: string;
1964
+ projectId: string;
1965
+ source: string;
1966
+ status: string;
1967
+ createdAt: Date;
1968
+ updatedAt: Date;
1969
+ steps: Prisma.JsonValue;
1970
+ kind: string;
1971
+ tags: Prisma.JsonValue;
1972
+ approvedBy: string | null;
1973
+ suiteId: string;
1974
+ ref: string | null;
1975
+ title: string;
1976
+ priority: string;
1977
+ preconditions: string | null;
1978
+ expectedResult: string | null;
1979
+ gherkin: string | null;
1980
+ approvedAt: Date | null;
1981
+ groundedOn: Prisma.JsonValue;
1982
+ caseRefs: Prisma.JsonValue;
1983
+ viewports: Prisma.JsonValue;
1984
+ viewportConstraint: string;
1985
+ techniqueOfRecord: string | null;
1986
+ traceTo: string | null;
1987
+ traceToExtra: Prisma.JsonValue;
1988
+ testDataInline: Prisma.JsonValue | null;
1989
+ dataNote: string | null;
1990
+ scriptNote: string | null;
1991
+ structuredSteps: Prisma.JsonValue | null;
1992
+ artifactId: string | null;
1993
+ scenarioId: string | null;
1994
+ suspect: boolean;
1995
+ suspectReason: string | null;
1996
+ flakyScore: number;
1997
+ consecutiveFlakes: number;
1998
+ lastFlakyAt: Date | null;
1999
+ quarantined: boolean;
2000
+ quarantinedAt: Date | null;
2001
+ quarantineReason: string | null;
2002
+ genPromptTokens: number | null;
2003
+ genCompletionTokens: number | null;
2004
+ genCostUsd: number | null;
2005
+ genModel: string | null;
2006
+ dataSetId: string | null;
2007
+ repoTargetPath: string | null;
2008
+ stepsKey: string | null;
2009
+ gherkinKey: string | null;
2010
+ contentHash: string | null;
2011
+ groundingHash: string | null;
2012
+ locatorsJson: Prisma.JsonValue | null;
2013
+ locatorsExtractedAt: Date | null;
2014
+ captureStepScreenshots: Prisma.JsonValue | null;
2015
+ }>;
2016
+ saveVisualBaseline(tenantId: string, projectId: string, screenId: string, storagePath: string, viewport: string, approvedBy?: string, gitBranch?: string, cloudUrl?: string): Promise<{
2017
+ tenantId: string;
2018
+ screenId: string;
2019
+ id: string;
2020
+ projectId: string;
2021
+ viewport: string;
2022
+ createdAt: Date;
2023
+ storagePath: string;
2024
+ cloudUrl: string | null;
2025
+ approvedBy: string | null;
2026
+ browser: string;
2027
+ gitBranch: string;
2028
+ gitCommit: string | null;
2029
+ ignoredRegions: Prisma.JsonValue;
2030
+ elementSnapshot: Prisma.JsonValue | null;
2031
+ }>;
2032
+ ensureVisualBaselinesForProject(tenantId: string, projectId: string, gitBranch?: string): Promise<{
2033
+ created: number;
2034
+ }>;
2035
+ getVisualBaseline(tenantId: string, screenId: string, viewport: string, gitBranch?: string): Promise<{
2036
+ tenantId: string;
2037
+ screenId: string;
2038
+ id: string;
2039
+ projectId: string;
2040
+ viewport: string;
2041
+ createdAt: Date;
2042
+ storagePath: string;
2043
+ cloudUrl: string | null;
2044
+ approvedBy: string | null;
2045
+ browser: string;
2046
+ gitBranch: string;
2047
+ gitCommit: string | null;
2048
+ ignoredRegions: Prisma.JsonValue;
2049
+ elementSnapshot: Prisma.JsonValue | null;
2050
+ } | null>;
2051
+ updateBaselineIgnoredRegions(tenantId: string, screenId: string, viewport: string, regions: Array<{
2052
+ x: number;
2053
+ y: number;
2054
+ width: number;
2055
+ height: number;
2056
+ label?: string;
2057
+ }>, gitBranch?: string): Promise<void>;
2058
+ getVisualBaselineForDiff(tenantId: string, screenId: string, viewport: string, gitBranch?: string): Promise<{
2059
+ id: string;
2060
+ storagePath: string;
2061
+ cloudUrl?: string;
2062
+ ignoredRegions: Array<{
2063
+ x: number;
2064
+ y: number;
2065
+ width: number;
2066
+ height: number;
2067
+ }>;
2068
+ } | null>;
2069
+ saveVisualDiff(tenantId: string, diff: {
2070
+ baselineId: string;
2071
+ newScreenshotPath: string;
2072
+ newScreenshotCloudUrl?: string;
2073
+ diffImageCloudUrl?: string;
2074
+ diffScore: number;
2075
+ changeType: string;
2076
+ changes: any[];
2077
+ recommendation: string;
2078
+ bugReportDraft?: any;
2079
+ aiSummary?: string;
2080
+ }): Promise<{
2081
+ tenantId: string;
2082
+ id: string;
2083
+ status: string;
2084
+ aiSummary: string | null;
2085
+ createdAt: Date;
2086
+ baselineId: string;
2087
+ newScreenshotPath: string;
2088
+ newScreenshotCloudUrl: string | null;
2089
+ diffImageCloudUrl: string | null;
2090
+ diffScore: number;
2091
+ changeType: string;
2092
+ changes: Prisma.JsonValue | null;
2093
+ recommendation: string | null;
2094
+ bugReportDraft: Prisma.JsonValue | null;
2095
+ reviewedBy: string | null;
2096
+ }>;
2097
+ saveElementFingerprint(tenantId: string, projectId: string, elementId: string, selectors: any[]): Promise<{
2098
+ tenantId: string;
2099
+ id: string;
2100
+ projectId: string;
2101
+ elementId: string;
2102
+ selectors: Prisma.JsonValue;
2103
+ lastHealedAt: Date | null;
2104
+ healCount: number;
2105
+ }>;
2106
+ getElementFingerprint(tenantId: string, elementId: string): Promise<{
2107
+ tenantId: string;
2108
+ id: string;
2109
+ projectId: string;
2110
+ elementId: string;
2111
+ selectors: Prisma.JsonValue;
2112
+ lastHealedAt: Date | null;
2113
+ healCount: number;
2114
+ } | null>;
2115
+ recordElementHeal(tenantId: string, elementId: string): Promise<{
2116
+ tenantId: string;
2117
+ id: string;
2118
+ projectId: string;
2119
+ elementId: string;
2120
+ selectors: Prisma.JsonValue;
2121
+ lastHealedAt: Date | null;
2122
+ healCount: number;
2123
+ }>;
2124
+ getCoverageReport(tenantId: string, projectId: string): Promise<{
2125
+ totalScreens: number;
2126
+ screensWithTests: number;
2127
+ coveragePercent: number;
2128
+ screenDesignCoveragePercent: number;
2129
+ screenApprovedCoveragePercent: number;
2130
+ screensWithApprovedTests: number;
2131
+ totalTestCases: number;
2132
+ approvedTestCases: number;
2133
+ pendingReviewCases: number;
2134
+ targetCoverage: number;
2135
+ gapToTarget: number;
2136
+ status: string;
2137
+ byKind: Record<string, number> | undefined;
2138
+ byPriority: Record<string, number> | undefined;
2139
+ byTechnique: Record<string, number> | undefined;
2140
+ gaps: string[] | undefined;
2141
+ requirementsTotal: number | null;
2142
+ requirementsCovered: number | null;
2143
+ requirementCoveragePercent: number | null;
2144
+ requirementDesignCoveragePercent: number | null;
2145
+ requirementApprovedTraceCoveragePercent: number | null;
2146
+ requirementsWithLinkedTests: number | null;
2147
+ requirementsWithApprovedLinkedTests: number | null;
2148
+ requirementsUntested: number | null;
2149
+ requirementsUnbuilt: number | null;
2150
+ riskWeightedCoverage: number | null;
2151
+ untestedRequirements: {
2152
+ reqId: string;
2153
+ title: string;
2154
+ priority: string;
2155
+ }[] | undefined;
2156
+ unbuiltRequirements: {
2157
+ reqId: string;
2158
+ title: string;
2159
+ priority: string;
2160
+ }[] | undefined;
2161
+ requirementsMissingTrace: {
2162
+ reqId: string;
2163
+ title: string;
2164
+ priority: string;
2165
+ }[] | undefined;
2166
+ }>;
2167
+ /**
2168
+ * Persist generated tests as pending review; callers must pass grounded facts.
2169
+ *
2170
+ * Dedup layers (in order):
2171
+ * 1. contentHash — SHA-256 of normalized(title+steps+expectedResult+preconditions)
2172
+ * 2. groundingHash — SHA-256 of sorted(groundedOn IDs) + kind (structural dedup)
2173
+ * 3. titleJaccard — stopword-stripped token overlap > SEMANTIC_DEDUP_THRESHOLD (default 0.6)
2174
+ * 4. exact title — fallback for pre-migration rows that have no stored hash
2175
+ *
2176
+ * mode='append' (default) — skips duplicates, inserts fresh cases.
2177
+ * mode='replace' — caller must delete existing cases before calling (see deleteScreenTestCases).
2178
+ * mode='edit' — for hash/similarity matches, upserts existing case (updates steps/gherkin/
2179
+ * expectedResult, snapshots old version, clears suspect flag).
2180
+ */
2181
+ saveGeneratedTestCases(tenantId: string, projectId: string, suiteName: string, cases: Array<{
2182
+ title: string;
2183
+ kind: TestKind;
2184
+ priority?: string;
2185
+ preconditions?: string;
2186
+ steps: unknown[];
2187
+ expectedResult?: string;
2188
+ groundedOn: string[];
2189
+ gherkin?: string;
2190
+ tags?: string[];
2191
+ caseRefs?: string[];
2192
+ source?: string;
2193
+ screenId?: string;
2194
+ ref?: string;
2195
+ techniqueOfRecord?: string;
2196
+ traceTo?: string;
2197
+ testDataInline?: Record<string, unknown>;
2198
+ viewportConstraint?: string;
2199
+ dataNote?: string;
2200
+ scriptNote?: string;
2201
+ structuredSteps?: Array<{
2202
+ action: string;
2203
+ target?: string;
2204
+ data?: string;
2205
+ }>;
2206
+ }>, usageStats?: {
2207
+ totalTokens: number;
2208
+ estimatedCostUsd: number;
2209
+ model: string;
2210
+ screenCount?: number;
2211
+ coverageReport?: unknown;
2212
+ }, mode?: "append" | "replace" | "edit"): Promise<{
2213
+ suite: {
2214
+ tenantId: string;
2215
+ id: string;
2216
+ projectId: string;
2217
+ name: string;
2218
+ createdAt: Date;
2219
+ genModel: string | null;
2220
+ category: import(".prisma/client").$Enums.SuiteCategory | null;
2221
+ parentId: string | null;
2222
+ ownerUserId: string | null;
2223
+ genTotalTokens: number | null;
2224
+ genTotalCostUsd: number | null;
2225
+ genAt: Date | null;
2226
+ genScreenCount: number | null;
2227
+ coverageReport: Prisma.JsonValue | null;
2228
+ };
2229
+ testCases: {
2230
+ tenantId: string;
2231
+ screenId: string | null;
2232
+ id: string;
2233
+ projectId: string;
2234
+ source: string;
2235
+ status: string;
2236
+ createdAt: Date;
2237
+ updatedAt: Date;
2238
+ steps: Prisma.JsonValue;
2239
+ kind: string;
2240
+ tags: Prisma.JsonValue;
2241
+ approvedBy: string | null;
2242
+ suiteId: string;
2243
+ ref: string | null;
2244
+ title: string;
2245
+ priority: string;
2246
+ preconditions: string | null;
2247
+ expectedResult: string | null;
2248
+ gherkin: string | null;
2249
+ approvedAt: Date | null;
2250
+ groundedOn: Prisma.JsonValue;
2251
+ caseRefs: Prisma.JsonValue;
2252
+ viewports: Prisma.JsonValue;
2253
+ viewportConstraint: string;
2254
+ techniqueOfRecord: string | null;
2255
+ traceTo: string | null;
2256
+ traceToExtra: Prisma.JsonValue;
2257
+ testDataInline: Prisma.JsonValue | null;
2258
+ dataNote: string | null;
2259
+ scriptNote: string | null;
2260
+ structuredSteps: Prisma.JsonValue | null;
2261
+ artifactId: string | null;
2262
+ scenarioId: string | null;
2263
+ suspect: boolean;
2264
+ suspectReason: string | null;
2265
+ flakyScore: number;
2266
+ consecutiveFlakes: number;
2267
+ lastFlakyAt: Date | null;
2268
+ quarantined: boolean;
2269
+ quarantinedAt: Date | null;
2270
+ quarantineReason: string | null;
2271
+ genPromptTokens: number | null;
2272
+ genCompletionTokens: number | null;
2273
+ genCostUsd: number | null;
2274
+ genModel: string | null;
2275
+ dataSetId: string | null;
2276
+ repoTargetPath: string | null;
2277
+ stepsKey: string | null;
2278
+ gherkinKey: string | null;
2279
+ contentHash: string | null;
2280
+ groundingHash: string | null;
2281
+ locatorsJson: Prisma.JsonValue | null;
2282
+ locatorsExtractedAt: Date | null;
2283
+ captureStepScreenshots: Prisma.JsonValue | null;
2284
+ }[];
2285
+ }>;
2286
+ /**
2287
+ * Delete all generated test cases for a specific screen in a suite.
2288
+ * Used by replace-mode generation to clear stale cases before re-inserting.
2289
+ */
2290
+ deleteScreenTestCases(tenantId: string, projectId: string, suiteName: string, screenId: string): Promise<{
2291
+ deleted: number;
2292
+ }>;
2293
+ getTenantLLMConfig(tenantId: string): Promise<{
2294
+ provider: string;
2295
+ model: string;
2296
+ apiKey?: string;
2297
+ baseUrl?: string;
2298
+ customModels?: Record<string, string[]>;
2299
+ crawlProvider?: string;
2300
+ crawlApiKey?: string;
2301
+ taskOverrides?: Record<string, {
2302
+ provider: string;
2303
+ model: string;
2304
+ apiKey?: string;
2305
+ baseUrl?: string;
2306
+ }>;
2307
+ } | null>;
2308
+ getTaskLLMConfig(tenantId: string, task: LLMTaskType): Promise<{
2309
+ provider: string;
2310
+ model: string;
2311
+ apiKey?: string;
2312
+ baseUrl?: string;
2313
+ } | null>;
2314
+ saveTenantLLMConfig(tenantId: string, data: {
2315
+ provider: string;
2316
+ model: string;
2317
+ apiKey?: string | null;
2318
+ baseUrl?: string;
2319
+ customModels?: Record<string, string[]>;
2320
+ crawlProvider?: string;
2321
+ crawlApiKey?: string;
2322
+ taskOverrides?: Record<string, {
2323
+ provider: string;
2324
+ model: string;
2325
+ apiKey?: string;
2326
+ baseUrl?: string;
2327
+ }>;
2328
+ }): Promise<{
2329
+ tenantId: string;
2330
+ id: string;
2331
+ updatedAt: Date;
2332
+ category: string;
2333
+ config: Prisma.JsonValue;
2334
+ dataRetentionDays: number;
2335
+ }>;
2336
+ getTenantLLMConfigRedacted(tenantId: string): Promise<{
2337
+ provider: string;
2338
+ model: string;
2339
+ apiKeyConfigured: boolean;
2340
+ baseUrl?: string;
2341
+ customModels?: Record<string, string[]>;
2342
+ crawlProvider?: string;
2343
+ crawlApiKeyConfigured: boolean;
2344
+ taskOverrides?: Record<string, {
2345
+ provider: string;
2346
+ model: string;
2347
+ apiKeyConfigured: boolean;
2348
+ baseUrl?: string;
2349
+ }>;
2350
+ } | null>;
2351
+ getOnboardingStatus(tenantId: string, userId: string): Promise<{
2352
+ dismissed: boolean;
2353
+ dismissedAt: string | null;
2354
+ steps: Array<{
2355
+ id: string;
2356
+ label: string;
2357
+ description: string;
2358
+ done: boolean;
2359
+ optional: boolean;
2360
+ href: string;
2361
+ }>;
2362
+ projectSteps: Array<{
2363
+ projectId: string;
2364
+ projectName: string;
2365
+ type: string;
2366
+ stepId: string;
2367
+ label: string;
2368
+ description: string;
2369
+ done: boolean;
2370
+ href: string;
2371
+ }>;
2372
+ allRequiredDone: boolean;
2373
+ }>;
2374
+ dismissOnboarding(tenantId: string): Promise<void>;
2375
+ getTenantEmailConfig(tenantId: string): Promise<{
2376
+ provider: string;
2377
+ resendApiKey?: string;
2378
+ smtpHost?: string;
2379
+ smtpPort?: number;
2380
+ smtpUser?: string;
2381
+ smtpPass?: string;
2382
+ fromEmail?: string;
2383
+ fromName?: string;
2384
+ } | null>;
2385
+ saveTenantEmailConfig(tenantId: string, data: {
2386
+ provider: string;
2387
+ resendApiKey?: string;
2388
+ smtpHost?: string;
2389
+ smtpPort?: number;
2390
+ smtpUser?: string;
2391
+ smtpPass?: string;
2392
+ fromEmail?: string;
2393
+ fromName?: string;
2394
+ }): Promise<{
2395
+ tenantId: string;
2396
+ id: string;
2397
+ updatedAt: Date;
2398
+ category: string;
2399
+ config: Prisma.JsonValue;
2400
+ dataRetentionDays: number;
2401
+ }>;
2402
+ getTenantNotificationsConfig(tenantId: string): Promise<{
2403
+ globalWebhookUrl?: string;
2404
+ slackWebhookUrl?: string;
2405
+ alertEmails?: string[];
2406
+ } | null>;
2407
+ saveTenantNotificationsConfig(tenantId: string, data: {
2408
+ globalWebhookUrl?: string;
2409
+ slackWebhookUrl?: string;
2410
+ alertEmails?: string[];
2411
+ }): Promise<{
2412
+ tenantId: string;
2413
+ id: string;
2414
+ updatedAt: Date;
2415
+ category: string;
2416
+ config: Prisma.JsonValue;
2417
+ dataRetentionDays: number;
2418
+ }>;
2419
+ getTenantBrandingConfig(tenantId: string): Promise<{
2420
+ primaryColor?: string;
2421
+ logoUrl?: string;
2422
+ } | null>;
2423
+ saveTenantBrandingConfig(tenantId: string, data: {
2424
+ primaryColor?: string;
2425
+ logoUrl?: string;
2426
+ }): Promise<{
2427
+ tenantId: string;
2428
+ id: string;
2429
+ updatedAt: Date;
2430
+ category: string;
2431
+ config: Prisma.JsonValue;
2432
+ dataRetentionDays: number;
2433
+ }>;
2434
+ getTenantEmailConfigRedacted(tenantId: string): Promise<{
2435
+ provider: string;
2436
+ resendConfigured: boolean;
2437
+ smtpHost?: string;
2438
+ smtpPort?: number;
2439
+ smtpUser?: string;
2440
+ fromEmail?: string;
2441
+ fromName?: string;
2442
+ } | null>;
2443
+ saveTenantStorageConfig(tenantId: string, data: {
2444
+ provider: string;
2445
+ bucket: string;
2446
+ region?: string;
2447
+ accountId?: string;
2448
+ endpointUrl?: string;
2449
+ accessKeyId?: string;
2450
+ secretAccessKey?: string;
2451
+ publicUrl?: string;
2452
+ prefix?: string;
2453
+ }): Promise<{
2454
+ tenantId: string;
2455
+ id: string;
2456
+ updatedAt: Date;
2457
+ category: string;
2458
+ config: Prisma.JsonValue;
2459
+ dataRetentionDays: number;
2460
+ }>;
2461
+ getTenantStorageConfigRedacted(tenantId: string): Promise<{
2462
+ provider: string;
2463
+ bucket: string;
2464
+ region?: string;
2465
+ accountId?: string;
2466
+ endpointUrl?: string;
2467
+ accessKeyIdConfigured: boolean;
2468
+ secretKeyConfigured: boolean;
2469
+ publicUrl?: string;
2470
+ prefix?: string;
2471
+ source: "tenant" | "platform";
2472
+ } | null>;
2473
+ /** Persist a confirmed header→field mapping for future auto-apply. */
2474
+ saveImportMapping(tenantId: string, signature: string, mapping: Record<string, unknown>): Promise<void>;
2475
+ /** Load a saved header→field mapping by its signature, or null if none saved. */
2476
+ loadImportMapping(tenantId: string, signature: string): Promise<Record<string, unknown> | null>;
2477
+ getTenantCaptchaConfig(tenantId: string): Promise<{
2478
+ provider: string;
2479
+ apiKey: string;
2480
+ } | null>;
2481
+ saveTenantCaptchaConfig(tenantId: string, data: {
2482
+ provider: string;
2483
+ apiKey?: string;
2484
+ }): Promise<{
2485
+ tenantId: string;
2486
+ id: string;
2487
+ updatedAt: Date;
2488
+ category: string;
2489
+ config: Prisma.JsonValue;
2490
+ dataRetentionDays: number;
2491
+ }>;
2492
+ getTenantCaptchaConfigRedacted(tenantId: string): Promise<{
2493
+ provider: string;
2494
+ configured: boolean;
2495
+ } | null>;
2496
+ getTenantEmailOTPConfig(tenantId: string): Promise<{
2497
+ provider: string;
2498
+ apiKey?: string;
2499
+ inboxId?: string;
2500
+ imapHost?: string;
2501
+ imapPort?: number;
2502
+ imapUser?: string;
2503
+ imapPass?: string;
2504
+ imapTls?: boolean;
2505
+ } | null>;
2506
+ saveTenantEmailOTPConfig(tenantId: string, data: {
2507
+ provider: string;
2508
+ apiKey?: string;
2509
+ inboxId?: string;
2510
+ imapHost?: string;
2511
+ imapPort?: number;
2512
+ imapUser?: string;
2513
+ imapPass?: string;
2514
+ imapTls?: boolean;
2515
+ }): Promise<{
2516
+ tenantId: string;
2517
+ id: string;
2518
+ updatedAt: Date;
2519
+ category: string;
2520
+ config: Prisma.JsonValue;
2521
+ dataRetentionDays: number;
2522
+ }>;
2523
+ getTenantEmailOTPConfigRedacted(tenantId: string): Promise<{
2524
+ provider: string;
2525
+ configured: boolean;
2526
+ inboxId?: string;
2527
+ imapHost?: string;
2528
+ imapPort?: number;
2529
+ imapUser?: string;
2530
+ imapTls?: boolean;
2531
+ } | null>;
2532
+ getTenantSkyvernConfig(tenantId: string): Promise<{
2533
+ apiUrl: string;
2534
+ apiKey: string;
2535
+ llmKey?: string;
2536
+ } | null>;
2537
+ saveTenantSkyvernConfig(tenantId: string, data: {
2538
+ apiUrl?: string;
2539
+ apiKey?: string;
2540
+ llmKey?: string;
2541
+ }): Promise<{
2542
+ tenantId: string;
2543
+ id: string;
2544
+ updatedAt: Date;
2545
+ category: string;
2546
+ config: Prisma.JsonValue;
2547
+ dataRetentionDays: number;
2548
+ }>;
2549
+ getTenantProxyConfig(tenantId: string): Promise<{
2550
+ mode: "none" | "auto" | "custom";
2551
+ proxyUrl?: string;
2552
+ } | null>;
2553
+ saveTenantProxyConfig(tenantId: string, data: {
2554
+ mode: "none" | "auto" | "custom";
2555
+ proxyUrl?: string | null;
2556
+ }): Promise<{
2557
+ tenantId: string;
2558
+ id: string;
2559
+ updatedAt: Date;
2560
+ category: string;
2561
+ config: Prisma.JsonValue;
2562
+ dataRetentionDays: number;
2563
+ }>;
2564
+ getTenantSkyvernConfigRedacted(tenantId: string): Promise<{
2565
+ apiUrl: string;
2566
+ configured: boolean;
2567
+ llmKey?: string;
2568
+ } | null>;
2569
+ getTenantDeviceFarmConfig(tenantId: string): Promise<{
2570
+ appiumServerUrl?: string;
2571
+ browserstackUsername?: string;
2572
+ browserstackAccessKey?: string;
2573
+ awsAccessKeyId?: string;
2574
+ awsSecretAccessKey?: string;
2575
+ awsRegion?: string;
2576
+ awsDeviceFarmProjectArn?: string;
2577
+ } | null>;
2578
+ saveTenantDeviceFarmConfig(tenantId: string, data: {
2579
+ appiumServerUrl?: string;
2580
+ browserstackUsername?: string;
2581
+ browserstackAccessKey?: string;
2582
+ awsAccessKeyId?: string;
2583
+ awsSecretAccessKey?: string;
2584
+ awsRegion?: string;
2585
+ awsDeviceFarmProjectArn?: string;
2586
+ }): Promise<{
2587
+ tenantId: string;
2588
+ id: string;
2589
+ updatedAt: Date;
2590
+ category: string;
2591
+ config: Prisma.JsonValue;
2592
+ dataRetentionDays: number;
2593
+ }>;
2594
+ getTenantDeviceFarmConfigRedacted(tenantId: string): Promise<{
2595
+ appiumServerUrl: string;
2596
+ browserstackUsername: string;
2597
+ browserstackConfigured: boolean;
2598
+ awsAccessKeyId: string;
2599
+ awsConfigured: boolean;
2600
+ awsRegion: string;
2601
+ awsDeviceFarmProjectArn: string;
2602
+ }>;
2603
+ getConnectorConfig(tenantId: string, projectId: string, kind: string): Promise<Record<string, string> | null>;
2604
+ logChange(tenantId: string, projectId: string, changeType: "ADDED" | "REMOVED" | "MODIFIED", summary: string, detail?: any): Promise<{
2605
+ tenantId: string;
2606
+ id: string;
2607
+ projectId: string;
2608
+ createdAt: Date;
2609
+ summary: string;
2610
+ changeType: string;
2611
+ detail: Prisma.JsonValue | null;
2612
+ }>;
2613
+ listChangeLogs(tenantId: string, projectId: string, limit?: number): Promise<{
2614
+ tenantId: string;
2615
+ id: string;
2616
+ projectId: string;
2617
+ createdAt: Date;
2618
+ summary: string;
2619
+ changeType: string;
2620
+ detail: Prisma.JsonValue | null;
2621
+ }[]>;
2622
+ getTenantInfo(tenantId: string): Promise<{
2623
+ name: string;
2624
+ slug: string;
2625
+ } | null>;
2626
+ /** Save or update an OAuth connection. Tokens are encrypted before storage. */
2627
+ upsertOAuthConnection(tenantId: string, projectId: string, provider: string, data: {
2628
+ accessToken?: string | null;
2629
+ refreshToken?: string | null;
2630
+ tokenExpiresAt?: Date | null;
2631
+ refreshTokenExpiresAt?: Date | null;
2632
+ status?: string;
2633
+ connectedAs?: string | null;
2634
+ installationId?: string | null;
2635
+ cloudId?: string | null;
2636
+ scopes?: string | null;
2637
+ pat?: string | null;
2638
+ dcBaseUrl?: string | null;
2639
+ }): Promise<{
2640
+ tenantId: string;
2641
+ id: string;
2642
+ projectId: string;
2643
+ status: string;
2644
+ createdAt: Date;
2645
+ updatedAt: Date;
2646
+ provider: string;
2647
+ accessToken_enc: string | null;
2648
+ refreshToken_enc: string | null;
2649
+ tokenExpiresAt: Date | null;
2650
+ refreshTokenExpiresAt: Date | null;
2651
+ connectedAs: string | null;
2652
+ installationId: string | null;
2653
+ cloudId: string | null;
2654
+ scopes: string | null;
2655
+ pat_enc: string | null;
2656
+ dcBaseUrl: string | null;
2657
+ }>;
2658
+ /** Get an OAuth connection with tokens decrypted. Returns null if not found. */
2659
+ getOAuthConnection(tenantId: string, projectId: string, provider: string): Promise<{
2660
+ accessToken: string | null;
2661
+ refreshToken: string | null;
2662
+ pat: string | null;
2663
+ accessToken_enc: undefined;
2664
+ refreshToken_enc: undefined;
2665
+ pat_enc: undefined;
2666
+ tenantId: string;
2667
+ id: string;
2668
+ projectId: string;
2669
+ status: string;
2670
+ createdAt: Date;
2671
+ updatedAt: Date;
2672
+ provider: string;
2673
+ tokenExpiresAt: Date | null;
2674
+ refreshTokenExpiresAt: Date | null;
2675
+ connectedAs: string | null;
2676
+ installationId: string | null;
2677
+ cloudId: string | null;
2678
+ scopes: string | null;
2679
+ dcBaseUrl: string | null;
2680
+ } | null>;
2681
+ /** List all OAuth connections for a project (tokens redacted). */
2682
+ listOAuthConnections(tenantId: string, projectId: string): Promise<{
2683
+ id: string;
2684
+ status: string;
2685
+ createdAt: Date;
2686
+ updatedAt: Date;
2687
+ provider: string;
2688
+ tokenExpiresAt: Date | null;
2689
+ refreshTokenExpiresAt: Date | null;
2690
+ connectedAs: string | null;
2691
+ installationId: string | null;
2692
+ cloudId: string | null;
2693
+ scopes: string | null;
2694
+ }[]>;
2695
+ /** Set connection status (CONNECTED | NEEDS_REAUTH | DISCONNECTED). */
2696
+ setOAuthConnectionStatus(tenantId: string, projectId: string, provider: string, status: string): Promise<{
2697
+ tenantId: string;
2698
+ id: string;
2699
+ projectId: string;
2700
+ status: string;
2701
+ createdAt: Date;
2702
+ updatedAt: Date;
2703
+ provider: string;
2704
+ accessToken_enc: string | null;
2705
+ refreshToken_enc: string | null;
2706
+ tokenExpiresAt: Date | null;
2707
+ refreshTokenExpiresAt: Date | null;
2708
+ connectedAs: string | null;
2709
+ installationId: string | null;
2710
+ cloudId: string | null;
2711
+ scopes: string | null;
2712
+ pat_enc: string | null;
2713
+ dcBaseUrl: string | null;
2714
+ }>;
2715
+ /** Delete an OAuth connection (user disconnects). */
2716
+ deleteOAuthConnection(tenantId: string, projectId: string, provider: string): Promise<{
2717
+ tenantId: string;
2718
+ id: string;
2719
+ projectId: string;
2720
+ status: string;
2721
+ createdAt: Date;
2722
+ updatedAt: Date;
2723
+ provider: string;
2724
+ accessToken_enc: string | null;
2725
+ refreshToken_enc: string | null;
2726
+ tokenExpiresAt: Date | null;
2727
+ refreshTokenExpiresAt: Date | null;
2728
+ connectedAs: string | null;
2729
+ installationId: string | null;
2730
+ cloudId: string | null;
2731
+ scopes: string | null;
2732
+ pat_enc: string | null;
2733
+ dcBaseUrl: string | null;
2734
+ }>;
2735
+ /**
2736
+ * Auto-create a VisualBaseline entry after crawl if one doesn't exist yet for this (screenId, viewport).
2737
+ * Called after saving a screenshot — creates the baseline on first crawl, skips on recrawls.
2738
+ */
2739
+ autoCreateVisualBaseline(tenantId: string, projectId: string, screenId: string, viewport: string, storagePath: string, gitBranch?: string, gitCommit?: string): Promise<void>;
2740
+ computeElementLayoutDiff(tenantId: string, screenId: string, viewport: string, gitBranch?: string): Promise<Array<{
2741
+ element: string;
2742
+ shiftX: number;
2743
+ shiftY: number;
2744
+ shiftW: number;
2745
+ shiftH: number;
2746
+ severity: string;
2747
+ description: string;
2748
+ }>>;
2749
+ /** Store probe observation markdown as BM25-indexed KnowledgeChunks for a screen. */
2750
+ saveKnowledgeChunk(tenantId: string, projectId: string, sourceType: string, sourceId: string, markdown: string, fallbackHeading: string): Promise<void>;
2751
+ saveCrawlEvidence(tenantId: string, projectId: string, data: {
2752
+ screenId?: string;
2753
+ jobId?: string;
2754
+ type: string;
2755
+ payload: any;
2756
+ }): Promise<{
2757
+ tenantId: string;
2758
+ screenId: string | null;
2759
+ id: string;
2760
+ projectId: string;
2761
+ createdAt: Date;
2762
+ payload: Prisma.JsonValue;
2763
+ jobId: string | null;
2764
+ type: string;
2765
+ }>;
2766
+ saveScreenTransition(tenantId: string, projectId: string, data: {
2767
+ fromScreenId: string;
2768
+ toScreenId?: string | null;
2769
+ action: any;
2770
+ confidence?: number;
2771
+ jobId?: string;
2772
+ }): Promise<{
2773
+ tenantId: string;
2774
+ id: string;
2775
+ projectId: string;
2776
+ createdAt: Date;
2777
+ confidence: number;
2778
+ action: Prisma.JsonValue;
2779
+ jobId: string | null;
2780
+ fromScreenId: string;
2781
+ toScreenId: string | null;
2782
+ }>;
2783
+ saveDiscoveredLinks(tenantId: string, projectId: string, fromScreenId: string, links: Array<{
2784
+ href: string;
2785
+ text?: string;
2786
+ landmark?: string;
2787
+ linkType?: string;
2788
+ source?: string;
2789
+ confidence?: number;
2790
+ toScreenId?: string;
2791
+ jobId?: string;
2792
+ }>): Promise<Prisma.BatchPayload | undefined>;
2793
+ resolveDiscoveredLinkTargets(tenantId: string, projectId: string, jobId: string, hrefToScreenId: Map<string, string>): Promise<void>;
2794
+ saveActionRecipe(tenantId: string, projectId: string, data: {
2795
+ domain: string;
2796
+ purpose: string;
2797
+ fingerprint: string;
2798
+ steps: any;
2799
+ successRate?: number;
2800
+ lastUsedAt?: Date;
2801
+ }): Promise<{
2802
+ tenantId: string;
2803
+ id: string;
2804
+ projectId: string;
2805
+ createdAt: Date;
2806
+ updatedAt: Date;
2807
+ fingerprint: string;
2808
+ steps: Prisma.JsonValue;
2809
+ domain: string;
2810
+ purpose: string;
2811
+ successRate: number;
2812
+ lastUsedAt: Date | null;
2813
+ }>;
2814
+ saveApiEndpointObservations(tenantId: string, projectId: string, screenId: string | undefined, observations: Array<{
2815
+ method: string;
2816
+ urlPattern: string;
2817
+ statusCode?: number;
2818
+ requestKind?: string;
2819
+ responseShape?: unknown;
2820
+ }>): Promise<Prisma.BatchPayload>;
2821
+ /** Look up the best matching ActionRecipe for replay-first self-healing. */
2822
+ getActionRecipe(tenantId: string, projectId: string, domain: string, purpose: string): Promise<{
2823
+ tenantId: string;
2824
+ id: string;
2825
+ projectId: string;
2826
+ createdAt: Date;
2827
+ updatedAt: Date;
2828
+ fingerprint: string;
2829
+ steps: Prisma.JsonValue;
2830
+ domain: string;
2831
+ purpose: string;
2832
+ successRate: number;
2833
+ lastUsedAt: Date | null;
2834
+ } | null>;
2835
+ /** Mark an ActionRecipe as failed (decrement successRate). */
2836
+ recordActionRecipeFailure(tenantId: string, id: string): Promise<{
2837
+ tenantId: string;
2838
+ id: string;
2839
+ projectId: string;
2840
+ createdAt: Date;
2841
+ updatedAt: Date;
2842
+ fingerprint: string;
2843
+ steps: Prisma.JsonValue;
2844
+ domain: string;
2845
+ purpose: string;
2846
+ successRate: number;
2847
+ lastUsedAt: Date | null;
2848
+ }>;
2849
+ getWorkspaceConnector(tenantId: string, kind: string): Promise<{
2850
+ id: any;
2851
+ kind: any;
2852
+ testOk: any;
2853
+ testedAt: any;
2854
+ config: Record<string, string>;
2855
+ } | null>;
2856
+ saveWorkspaceConnector(tenantId: string, kind: string, config: Record<string, string>, encryptKeys: string[]): Promise<{
2857
+ tenantId: string;
2858
+ id: string;
2859
+ createdAt: Date;
2860
+ updatedAt: Date;
2861
+ kind: string;
2862
+ config: Prisma.JsonValue | null;
2863
+ secretRef: string;
2864
+ testOk: boolean;
2865
+ testedAt: Date | null;
2866
+ }>;
2867
+ deleteWorkspaceConnector(tenantId: string, kind: string): Promise<{
2868
+ tenantId: string;
2869
+ id: string;
2870
+ createdAt: Date;
2871
+ updatedAt: Date;
2872
+ kind: string;
2873
+ config: Prisma.JsonValue | null;
2874
+ secretRef: string;
2875
+ testOk: boolean;
2876
+ testedAt: Date | null;
2877
+ } | null>;
2878
+ listWorkspaceConnectors(tenantId: string): Promise<{
2879
+ id: any;
2880
+ kind: any;
2881
+ testOk: any;
2882
+ testedAt: any;
2883
+ config: Record<string, any>;
2884
+ }[]>;
2885
+ markWorkspaceConnectorTested(tenantId: string, kind: string, ok: boolean): Promise<{
2886
+ tenantId: string;
2887
+ id: string;
2888
+ createdAt: Date;
2889
+ updatedAt: Date;
2890
+ kind: string;
2891
+ config: Prisma.JsonValue | null;
2892
+ secretRef: string;
2893
+ testOk: boolean;
2894
+ testedAt: Date | null;
2895
+ } | null>;
2896
+ createSharedRunToken(tenantId: string, runId: string, ttlHours?: number): Promise<{
2897
+ tenantId: string;
2898
+ id: string;
2899
+ createdAt: Date;
2900
+ runId: string;
2901
+ token: string;
2902
+ expiresAt: Date;
2903
+ }>;
2904
+ getSharedRunToken(token: string): Promise<{
2905
+ tenantId: string;
2906
+ id: string;
2907
+ createdAt: Date;
2908
+ runId: string;
2909
+ token: string;
2910
+ expiresAt: Date;
2911
+ } | null>;
2912
+ getWorkspaceSsoProviders(tenantId: string): Promise<Array<{
2913
+ id: string;
2914
+ name: string;
2915
+ provider: string;
2916
+ domain: string;
2917
+ clientId: string;
2918
+ clientSecret?: string;
2919
+ scope?: string;
2920
+ defaultRole?: string;
2921
+ }>>;
2922
+ saveWorkspaceSsoProviders(tenantId: string, providers: Array<{
2923
+ id?: string;
2924
+ name: string;
2925
+ provider: string;
2926
+ domain: string;
2927
+ clientId: string;
2928
+ clientSecret?: string;
2929
+ scope?: string;
2930
+ defaultRole?: string;
2931
+ }>): Promise<void>;
2932
+ getWorkspaceVpnConfig(tenantId: string): Promise<{
2933
+ enabled: boolean;
2934
+ mode: string;
2935
+ provider?: string;
2936
+ gatewayCidrs: string[];
2937
+ jitProvision: boolean;
2938
+ jitDefaultRole: string;
2939
+ fallbackToPassword: boolean;
2940
+ cloudflareTeamDomain?: string;
2941
+ googleIapAudience?: string;
2942
+ awsAlbRegion?: string;
2943
+ azureTenantId?: string;
2944
+ headerEmail?: string;
2945
+ headerUser?: string;
2946
+ headerGroups?: string;
2947
+ providers?: Array<{
2948
+ id: string;
2949
+ provider: string;
2950
+ cloudflareTeamDomain?: string;
2951
+ googleIapAudience?: string;
2952
+ awsAlbRegion?: string;
2953
+ azureTenantId?: string;
2954
+ headerEmail?: string;
2955
+ headerUser?: string;
2956
+ headerGroups?: string;
2957
+ }>;
2958
+ } | null>;
2959
+ saveWorkspaceVpnConfig(tenantId: string, config: {
2960
+ enabled: boolean;
2961
+ mode: string;
2962
+ provider: string;
2963
+ gatewayCidrs: string[];
2964
+ jitProvision: boolean;
2965
+ jitDefaultRole: string;
2966
+ fallbackToPassword: boolean;
2967
+ cloudflareTeamDomain?: string;
2968
+ googleIapAudience?: string;
2969
+ awsAlbRegion?: string;
2970
+ azureTenantId?: string;
2971
+ headerEmail?: string;
2972
+ headerUser?: string;
2973
+ headerGroups?: string;
2974
+ }): Promise<void>;
2975
+ /** Persist a performance snapshot for a screen captured during a crawl job. */
2976
+ savePerformanceSnapshot(tenantId: string, screenId: string, crawlJobId: string, data: {
2977
+ lcp?: number;
2978
+ fcp?: number;
2979
+ cls?: number;
2980
+ ttfb?: number;
2981
+ viewport?: string;
2982
+ lighthousePerf?: number;
2983
+ lighthouseA11y?: number;
2984
+ lighthouseSEO?: number;
2985
+ lighthouseBP?: number;
2986
+ lighthouseReport?: object;
2987
+ }): Promise<unknown>;
2988
+ getVisualDiff(tenantId: string, diffId: string): Promise<unknown>;
2989
+ /**
2990
+ * promoteBaseline: when true and status is APPROVED, each diff's baseline is
2991
+ * also updated to its new screenshot — otherwise "Approve" only marks the
2992
+ * diff reviewed and the next crawl re-diffs against the same (now-stale)
2993
+ * baseline, re-flagging the exact change the reviewer just accepted. Each
2994
+ * diff can have a different baselineId/newScreenshotPath, so this can't be
2995
+ * a single updateMany — it loops per-diff like promoteVisualDiffToBaseline.
2996
+ */
2997
+ bulkUpdateVisualDiffStatus(tenantId: string, diffIds: string[], status: string, reviewedBy: string, promoteBaseline?: boolean): Promise<any>;
2998
+ promoteVisualDiffToBaseline(tenantId: string, diffId: string): Promise<{
2999
+ promoted: boolean;
3000
+ } | null>;
3001
+ importTestCasesFromArtifact(tenantId: string, projectId: string, suiteId: string, cases: Array<{
3002
+ ref?: string;
3003
+ traceTo?: string;
3004
+ caseRefs?: Array<Record<string, unknown>>;
3005
+ title: string;
3006
+ kind: string;
3007
+ priority: string;
3008
+ preconditions?: string;
3009
+ steps: Array<{
3010
+ stepId?: string;
3011
+ action: string;
3012
+ data?: string;
3013
+ expected: string;
3014
+ actualResult?: string;
3015
+ comment?: string;
3016
+ screenshotUrl?: string;
3017
+ }>;
3018
+ expectedResult: string;
3019
+ actualResult?: string;
3020
+ comment?: string;
3021
+ screenshotUrl?: string;
3022
+ gherkin?: string;
3023
+ tags?: string[];
3024
+ section?: string;
3025
+ testDataInline?: Record<string, unknown>;
3026
+ }>, opts?: {
3027
+ duplicateMode?: "skip" | "create" | "replace";
3028
+ artifactId?: string;
3029
+ }): Promise<{
3030
+ created: any[];
3031
+ skipped: number;
3032
+ replaced?: number;
3033
+ }>;
3034
+ /**
3035
+ * Collect unique test-data strings from imported test case steps and upsert them
3036
+ * into a "Imported Test Data" TestDataSet. Each unique data value becomes one row
3037
+ * labelled by the first test-case title that used it.
3038
+ *
3039
+ * Handles common key-value formats:
3040
+ * "username: admin | password: pass" → { username: 'admin', password: 'pass' }
3041
+ * "username=admin, password=pass" → { username: 'admin', password: 'pass' }
3042
+ * Plain text → { testData: '<value>' }
3043
+ *
3044
+ * Safe to call repeatedly — replaces the whole set each time.
3045
+ */
3046
+ syncImportedTestDataToDataSet(tenantId: string, projectId: string, cases: Array<{
3047
+ title: string;
3048
+ steps: Array<{
3049
+ data?: string;
3050
+ }>;
3051
+ /** FZE recording form fields — promoted to a dataset row when no step data strings exist. */
3052
+ testDataInline?: Record<string, unknown>;
3053
+ }>, createdCaseIds?: string[]): Promise<void>;
3054
+ /**
3055
+ * Mirror the project's crawl credentials into a "Crawl Credentials" TestDataSet.
3056
+ * Each credential profile (or the main username/password) becomes a TestDataRow.
3057
+ * Called automatically after test case import and when crawl credentials are saved.
3058
+ * Safe to call repeatedly — upserts, never duplicates.
3059
+ */
3060
+ syncCrawlCredentialsToTestData(tenantId: string, projectId: string): Promise<void>;
3061
+ /**
3062
+ * Upsert requirements extracted from a PRD / user stories.
3063
+ * Conflict key: (tenantId, projectId, reqId) — stable re-ingestion safe.
3064
+ */
3065
+ upsertRequirements(tenantId: string, projectId: string, requirements: RequirementUpsertInput[], options?: {
3066
+ flagSuspectOnChange?: boolean;
3067
+ }): Promise<any[]>;
3068
+ /**
3069
+ * Same as upsertRequirements but also reports which requirements were created vs. changed.
3070
+ * "Changed" means a content field differs (title, acceptance criteria, business rules, actor);
3071
+ * priority/status/metadata-only updates do not invalidate downstream tests.
3072
+ * With flagSuspectOnChange, every test case linked to a changed requirement (primary traceTo,
3073
+ * additional traceToExtra, or via its scenario) is flagged suspect in the same transaction.
3074
+ */
3075
+ upsertRequirementsDetailed(tenantId: string, projectId: string, requirements: RequirementUpsertInput[], options?: {
3076
+ flagSuspectOnChange?: boolean;
3077
+ }): Promise<{
3078
+ saved: any[];
3079
+ created: string[];
3080
+ skipped: string[];
3081
+ changed: {
3082
+ reqId: string;
3083
+ fields: string[];
3084
+ version: number;
3085
+ }[];
3086
+ suspectFlagged: number;
3087
+ }>;
3088
+ /**
3089
+ * Replace a test case's requirement links. traceTo stays the primary link; `additional`
3090
+ * holds secondary reqIds (one test covering several stories).
3091
+ */
3092
+ setTraceLinks(tenantId: string, projectId: string, testCaseId: string, links: {
3093
+ traceTo?: string | null;
3094
+ additional?: string[];
3095
+ }): Promise<any>;
3096
+ /**
3097
+ * Repair test cases with a missing or placeholder traceTo by inheriting the requirement of the
3098
+ * scenario they were generated from. Returns the number of test cases repaired.
3099
+ */
3100
+ backfillTraceTo(tenantId: string, projectId: string): Promise<number>;
3101
+ /**
3102
+ * List all requirements for a project, with optional filters.
3103
+ */
3104
+ listRequirements(tenantId: string, projectId: string, filters?: {
3105
+ priority?: string;
3106
+ type?: string;
3107
+ status?: string;
3108
+ }): Promise<unknown>;
3109
+ /**
3110
+ * Semantic similarity search over project requirements.
3111
+ * Uses the cross-encoder to rank requirements by relevance to an arbitrary query.
3112
+ * Useful for: duplicate detection when creating requirements, TC→req trace suggestions.
3113
+ *
3114
+ * @param query Free text — requirement title, test case title + steps, etc.
3115
+ * @param excludeReqId Optional reqId to exclude from results (e.g. the req being edited).
3116
+ * @param topK Max results to return (default 5).
3117
+ */
3118
+ findSimilarRequirements(tenantId: string, projectId: string, query: string, excludeReqId?: string, topK?: number): Promise<Array<{
3119
+ reqId: string;
3120
+ title: string;
3121
+ score: number;
3122
+ }>>;
3123
+ /**
3124
+ * Suggest requirement IDs a test case should trace to, ranked by semantic relevance.
3125
+ * Uses the cross-encoder on the TC title + first steps as the query.
3126
+ *
3127
+ * @param tcTitle Test case title.
3128
+ * @param tcSteps Test case steps (uses first 5).
3129
+ * @param topK Max suggestions (default 5).
3130
+ */
3131
+ suggestTraceLinks(tenantId: string, projectId: string, tcTitle: string, tcSteps?: string[], topK?: number): Promise<Array<{
3132
+ reqId: string;
3133
+ title: string;
3134
+ score: number;
3135
+ }>>;
3136
+ /**
3137
+ * Get a single requirement by reqId.
3138
+ */
3139
+ getRequirement(tenantId: string, projectId: string, reqId: string): Promise<unknown>;
3140
+ /**
3141
+ * Update a single requirement (manual correction or status update).
3142
+ */
3143
+ updateRequirement(tenantId: string, projectId: string, reqId: string, patch: Record<string, unknown>): Promise<unknown>;
3144
+ /**
3145
+ * Delete a single requirement.
3146
+ */
3147
+ deleteRequirement(tenantId: string, projectId: string, reqId: string): Promise<unknown>;
3148
+ /**
3149
+ * Bulk-update requirement statuses after coverage matching.
3150
+ * Called by requirements-agent after generateCoverageGapReport().
3151
+ */
3152
+ updateRequirementStatuses(tenantId: string, projectId: string, result: {
3153
+ covered: string[];
3154
+ untested: string[];
3155
+ unbuilt: string[];
3156
+ }): Promise<void>;
3157
+ /**
3158
+ * Delete all requirements for a project (used before re-extraction).
3159
+ */
3160
+ clearRequirements(tenantId: string, projectId: string): Promise<unknown>;
3161
+ upsertScenarios(tenantId: string, projectId: string, requirementId: string, scenarios: Array<{
3162
+ title: string;
3163
+ description?: string;
3164
+ gherkin: string;
3165
+ source?: string;
3166
+ }>): Promise<any[]>;
3167
+ listScenarios(tenantId: string, projectId: string, reqId: string): Promise<any[]>;
3168
+ updateScenario(tenantId: string, scenarioId: string, patch: Record<string, any>): Promise<any | null>;
3169
+ deleteScenario(tenantId: string, scenarioId: string): Promise<void>;
3170
+ flagSuspectByRequirement(tenantId: string, projectId: string, reqId: string, reason: string): Promise<void>;
3171
+ listSuspectTestCases(tenantId: string, projectId: string): Promise<any[]>;
3172
+ clearSuspectFlag(tenantId: string, testCaseId: string): Promise<void>;
3173
+ getWorkspaceSettings(tenantId: string, workspaceId: string): Promise<any | null>;
3174
+ getProject(tenantId: string, projectId: string): Promise<any | null>;
3175
+ getJobActivity(tenantId: string, jobId: string): Promise<{
3176
+ job: any;
3177
+ timeline: any[];
3178
+ screens: any[];
3179
+ transitions: any[];
3180
+ apiEndpoints: any[];
3181
+ aiCost: any;
3182
+ orphans: string[];
3183
+ }>;
3184
+ };
3185
+ export {};