donobu 5.30.0 → 5.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/esm/lib/page/extendPage.js +2 -0
  2. package/dist/esm/lib/test/testExtension.js +2 -0
  3. package/dist/esm/main.d.ts +2 -0
  4. package/dist/esm/main.js +2 -0
  5. package/dist/esm/managers/AdminApiController.js +4 -1
  6. package/dist/esm/managers/DonobuFlowsManager.js +2 -0
  7. package/dist/esm/managers/SuitesManager.js +2 -0
  8. package/dist/esm/managers/TestsManager.js +2 -0
  9. package/dist/esm/models/FlowMetadata.d.ts +17 -0
  10. package/dist/esm/models/FlowMetadata.js +3 -0
  11. package/dist/esm/models/Provenance.d.ts +43 -0
  12. package/dist/esm/models/Provenance.js +60 -0
  13. package/dist/esm/models/SuiteMetadata.d.ts +17 -0
  14. package/dist/esm/models/SuiteMetadata.js +3 -0
  15. package/dist/esm/models/TestMetadata.d.ts +85 -0
  16. package/dist/esm/models/TestMetadata.js +3 -0
  17. package/dist/esm/reporter/buildReport.js +1 -0
  18. package/dist/esm/reporter/render.js +3 -0
  19. package/dist/esm/reporter/renderMarkdown.js +6 -0
  20. package/dist/esm/utils/buildProvenance.d.ts +3 -0
  21. package/dist/esm/utils/buildProvenance.js +101 -0
  22. package/dist/lib/page/extendPage.js +2 -0
  23. package/dist/lib/test/testExtension.js +2 -0
  24. package/dist/main.d.ts +2 -0
  25. package/dist/main.js +2 -0
  26. package/dist/managers/AdminApiController.js +4 -1
  27. package/dist/managers/DonobuFlowsManager.js +2 -0
  28. package/dist/managers/SuitesManager.js +2 -0
  29. package/dist/managers/TestsManager.js +2 -0
  30. package/dist/models/FlowMetadata.d.ts +17 -0
  31. package/dist/models/FlowMetadata.js +3 -0
  32. package/dist/models/Provenance.d.ts +43 -0
  33. package/dist/models/Provenance.js +60 -0
  34. package/dist/models/SuiteMetadata.d.ts +17 -0
  35. package/dist/models/SuiteMetadata.js +3 -0
  36. package/dist/models/TestMetadata.d.ts +85 -0
  37. package/dist/models/TestMetadata.js +3 -0
  38. package/dist/reporter/buildReport.js +1 -0
  39. package/dist/reporter/render.js +3 -0
  40. package/dist/reporter/renderMarkdown.js +6 -0
  41. package/dist/utils/buildProvenance.d.ts +3 -0
  42. package/dist/utils/buildProvenance.js +101 -0
  43. package/package.json +1 -1
@@ -23,6 +23,7 @@ const GoToWebpageTool_1 = require("../../tools/GoToWebpageTool");
23
23
  const RunAccessibilityTestTool_1 = require("../../tools/RunAccessibilityTestTool");
24
24
  const ansi_1 = require("../../utils/ansi");
25
25
  const BrowserUtils_1 = require("../../utils/BrowserUtils");
26
+ const buildProvenance_1 = require("../../utils/buildProvenance");
26
27
  const Logger_1 = require("../../utils/Logger");
27
28
  const MiscUtils_1 = require("../../utils/MiscUtils");
28
29
  const PlaywrightUtils_1 = require("../../utils/PlaywrightUtils");
@@ -120,6 +121,7 @@ async function extendPage(page, options) {
120
121
  completedAt: null,
121
122
  state: 'RUNNING_ACTION',
122
123
  nextState: null,
124
+ provenance: (0, buildProvenance_1.buildProvenance)('CODE'),
123
125
  },
124
126
  interactionVisualizer: interactionVisualizer,
125
127
  donobuStack: donobuStack,
@@ -33,6 +33,7 @@ const DonobuFlowsManager_1 = require("../../managers/DonobuFlowsManager");
33
33
  const fileUploadWorkerRegistry_1 = require("../../persistence/files/fileUploadWorkerRegistry");
34
34
  const ansi_1 = require("../../utils/ansi");
35
35
  const BrowserUtils_1 = require("../../utils/BrowserUtils");
36
+ const buildProvenance_1 = require("../../utils/buildProvenance");
36
37
  const FlowLogBuffer_1 = require("../../utils/FlowLogBuffer");
37
38
  const Logger_1 = require("../../utils/Logger");
38
39
  const MiscUtils_1 = require("../../utils/MiscUtils");
@@ -945,6 +946,7 @@ function flowMetadataToTestMetadata(testId, flowMeta) {
945
946
  maxToolCalls: flowMeta.maxToolCalls,
946
947
  suiteId: null,
947
948
  nextRunMode: 'DETERMINISTIC',
949
+ provenance: (0, buildProvenance_1.buildProvenance)('CODE'),
948
950
  };
949
951
  }
950
952
  //# sourceMappingURL=testExtension.js.map
@@ -44,6 +44,7 @@ export type * from './models/GptMessage';
44
44
  export * from './models/InteractableElement';
45
45
  export type { PaginatedResult } from './models/PaginatedResult';
46
46
  export * from './models/ProposedToolCall';
47
+ export * from './models/Provenance';
47
48
  export * from './models/RunConfig';
48
49
  export * from './models/SuiteMetadata';
49
50
  export * from './models/TargetConfig';
@@ -63,6 +64,7 @@ export type { TargetRuntime } from './targets/TargetRuntime';
63
64
  export { type TargetRuntimeParams, type TargetRuntimePlugin, TargetRuntimePluginRegistry, } from './targets/TargetRuntimePlugin';
64
65
  export * from './tools/ReplayableInteraction';
65
66
  export * from './tools/Tool';
67
+ export * from './utils/buildProvenance';
66
68
  export * from './utils/createTool';
67
69
  export * from './utils/FlowLogBuffer';
68
70
  export * from './utils/JsonSchemaUtils';
package/dist/esm/main.js CHANGED
@@ -82,6 +82,7 @@ __exportStar(require("./models/CreateDonobuFlow"), exports);
82
82
  __exportStar(require("./models/FlowMetadata"), exports);
83
83
  __exportStar(require("./models/InteractableElement"), exports);
84
84
  __exportStar(require("./models/ProposedToolCall"), exports);
85
+ __exportStar(require("./models/Provenance"), exports);
85
86
  __exportStar(require("./models/RunConfig"), exports);
86
87
  __exportStar(require("./models/SuiteMetadata"), exports);
87
88
  __exportStar(require("./models/TargetConfig"), exports);
@@ -97,6 +98,7 @@ var TargetRuntimePlugin_1 = require("./targets/TargetRuntimePlugin");
97
98
  Object.defineProperty(exports, "TargetRuntimePluginRegistry", { enumerable: true, get: function () { return TargetRuntimePlugin_1.TargetRuntimePluginRegistry; } });
98
99
  __exportStar(require("./tools/ReplayableInteraction"), exports);
99
100
  __exportStar(require("./tools/Tool"), exports);
101
+ __exportStar(require("./utils/buildProvenance"), exports);
100
102
  __exportStar(require("./utils/createTool"), exports);
101
103
  __exportStar(require("./utils/FlowLogBuffer"), exports);
102
104
  __exportStar(require("./utils/JsonSchemaUtils"), exports);
@@ -185,7 +185,10 @@ class AdminApiController {
185
185
  const start = performance.now();
186
186
  res.on('finish', () => {
187
187
  const duration = Math.round(performance.now() - start);
188
- const logLevel = req.path === '/api/ping' ? 'debug' : 'info';
188
+ // Turn down the log noise for the endpoints polled by the frontend.
189
+ const logLevel = req.path === '/api/ping' || req.path === '/api/uploads/status'
190
+ ? 'debug'
191
+ : 'info';
189
192
  Logger_1.accessLogger[logLevel](`${req.method} | ${req.path} | ${res.statusCode} | ${duration}ms`);
190
193
  });
191
194
  next();
@@ -52,6 +52,7 @@ const UnknownToolException_1 = require("../exceptions/UnknownToolException");
52
52
  const GptConfig_1 = require("../models/GptConfig");
53
53
  const resolveTargetRuntime_1 = require("../targets/resolveTargetRuntime");
54
54
  const CustomToolRunnerTool_1 = require("../tools/CustomToolRunnerTool");
55
+ const buildProvenance_1 = require("../utils/buildProvenance");
55
56
  const displayName_1 = require("../utils/displayName");
56
57
  const FlowLogBuffer_1 = require("../utils/FlowLogBuffer");
57
58
  const Logger_1 = require("../utils/Logger");
@@ -176,6 +177,7 @@ class DonobuFlowsManager {
176
177
  testId: flowParams.testId ?? null,
177
178
  // Target-specific fields (browser, targetWebsite, isControlPanelEnabled, etc.)
178
179
  ...targetRuntime.getMetadataFields(),
180
+ provenance: (0, buildProvenance_1.buildProvenance)('DONOBU_STUDIO'),
179
181
  };
180
182
  const flowsPersistence = await this.flowsPersistenceRegistry.get();
181
183
  const envData = await this.envDataManager.getByNames(flowMetadata.envVars ?? []);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SuitesManager = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  const SuiteNotFoundException_1 = require("../exceptions/SuiteNotFoundException");
6
+ const buildProvenance_1 = require("../utils/buildProvenance");
6
7
  const FederatedPagination_1 = require("./FederatedPagination");
7
8
  class SuitesManager {
8
9
  constructor(suitesPersistenceRegistry, testsPersistenceRegistry) {
@@ -30,6 +31,7 @@ class SuitesManager {
30
31
  resultJsonSchema: params.resultJsonSchema ?? null,
31
32
  maxToolCalls: params.maxToolCalls ?? null,
32
33
  videoDisabled: params.videoDisabled,
34
+ provenance: (0, buildProvenance_1.buildProvenance)('DONOBU_STUDIO'),
33
35
  };
34
36
  // Create in the primary persistence layer only.
35
37
  const primary = await this.suitesPersistenceRegistry.get();
@@ -4,6 +4,7 @@ exports.TestsManager = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  const CannotDeleteRunningFlowException_1 = require("../exceptions/CannotDeleteRunningFlowException");
6
6
  const TestNotFoundException_1 = require("../exceptions/TestNotFoundException");
7
+ const buildProvenance_1 = require("../utils/buildProvenance");
7
8
  const displayName_1 = require("../utils/displayName");
8
9
  const FederatedPagination_1 = require("./FederatedPagination");
9
10
  class TestsManager {
@@ -35,6 +36,7 @@ class TestsManager {
35
36
  resultJsonSchema: params.resultJsonSchema ?? null,
36
37
  maxToolCalls: params.maxToolCalls ?? null,
37
38
  videoDisabled: params.videoDisabled,
39
+ provenance: (0, buildProvenance_1.buildProvenance)('DONOBU_STUDIO'),
38
40
  };
39
41
  // Create in the primary persistence layer only.
40
42
  const primary = await this.testsPersistenceRegistry.get();
@@ -181,6 +181,23 @@ export declare const FlowMetadataSchema: z.ZodObject<{
181
181
  FAILED: "FAILED";
182
182
  SUCCESS: "SUCCESS";
183
183
  }>>;
184
+ provenance: z.ZodOptional<z.ZodObject<{
185
+ source: z.ZodEnum<{
186
+ DONOBU_STUDIO: "DONOBU_STUDIO";
187
+ CODE: "CODE";
188
+ }>;
189
+ user: z.ZodOptional<z.ZodString>;
190
+ os: z.ZodOptional<z.ZodString>;
191
+ hostname: z.ZodOptional<z.ZodString>;
192
+ ci: z.ZodOptional<z.ZodObject<{
193
+ provider: z.ZodOptional<z.ZodString>;
194
+ }, z.core.$loose>>;
195
+ git: z.ZodOptional<z.ZodObject<{
196
+ sha: z.ZodOptional<z.ZodString>;
197
+ branch: z.ZodOptional<z.ZodString>;
198
+ dirty: z.ZodOptional<z.ZodBoolean>;
199
+ }, z.core.$loose>>;
200
+ }, z.core.$loose>>;
184
201
  }, z.core.$loose>;
185
202
  export type FlowMetadata = z.infer<typeof FlowMetadataSchema>;
186
203
  /**
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isComplete = exports.FlowsQuerySchema = exports.FlowMetadataSchema = exports.StateSchema = void 0;
4
4
  const v4_1 = require("zod/v4");
5
+ const Provenance_1 = require("./Provenance");
5
6
  const RunConfig_1 = require("./RunConfig");
6
7
  const RunMode_1 = require("./RunMode");
7
8
  const SortOrder_1 = require("./SortOrder");
@@ -87,6 +88,8 @@ depends on how the flow completes:
87
88
  .describe('The Unix epoch millisecond timestamp of when the flow completed.'),
88
89
  state: exports.StateSchema.describe('The current state of the flow.'),
89
90
  nextState: exports.StateSchema.nullable().describe('The planned next state of the flow.'),
91
+ provenance: Provenance_1.ProvenanceSchema.optional().describe('Records how this flow came into existence (Studio vs. code-first). ' +
92
+ 'Absent on rows written before provenance tracking was introduced.'),
90
93
  });
91
94
  const FlowSortBySchema = v4_1.z.enum(['created_at', 'name', 'run_mode', 'state']);
92
95
  /**
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod/v4';
2
+ export declare const ProvenanceSourceSchema: z.ZodEnum<{
3
+ DONOBU_STUDIO: "DONOBU_STUDIO";
4
+ CODE: "CODE";
5
+ }>;
6
+ export type ProvenanceSource = z.infer<typeof ProvenanceSourceSchema>;
7
+ export declare const GitProvenanceSchema: z.ZodObject<{
8
+ sha: z.ZodOptional<z.ZodString>;
9
+ branch: z.ZodOptional<z.ZodString>;
10
+ dirty: z.ZodOptional<z.ZodBoolean>;
11
+ }, z.core.$loose>;
12
+ export type GitProvenance = z.infer<typeof GitProvenanceSchema>;
13
+ export declare const CiProvenanceSchema: z.ZodObject<{
14
+ provider: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$loose>;
16
+ export type CiProvenance = z.infer<typeof CiProvenanceSchema>;
17
+ /**
18
+ * Provenance — records *how* a Suite/Test/Flow came into existence so downstream
19
+ * code can render origin badges and gate operations that aren't safe across run
20
+ * modes (e.g. running a CODE-created test from Studio).
21
+ *
22
+ * `.loose()` so newer SDKs can write extra provenance fields without breaking
23
+ * older readers that round-trip the metadata blob.
24
+ */
25
+ export declare const ProvenanceSchema: z.ZodObject<{
26
+ source: z.ZodEnum<{
27
+ DONOBU_STUDIO: "DONOBU_STUDIO";
28
+ CODE: "CODE";
29
+ }>;
30
+ user: z.ZodOptional<z.ZodString>;
31
+ os: z.ZodOptional<z.ZodString>;
32
+ hostname: z.ZodOptional<z.ZodString>;
33
+ ci: z.ZodOptional<z.ZodObject<{
34
+ provider: z.ZodOptional<z.ZodString>;
35
+ }, z.core.$loose>>;
36
+ git: z.ZodOptional<z.ZodObject<{
37
+ sha: z.ZodOptional<z.ZodString>;
38
+ branch: z.ZodOptional<z.ZodString>;
39
+ dirty: z.ZodOptional<z.ZodBoolean>;
40
+ }, z.core.$loose>>;
41
+ }, z.core.$loose>;
42
+ export type Provenance = z.infer<typeof ProvenanceSchema>;
43
+ //# sourceMappingURL=Provenance.d.ts.map
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProvenanceSchema = exports.CiProvenanceSchema = exports.GitProvenanceSchema = exports.ProvenanceSourceSchema = void 0;
4
+ const v4_1 = require("zod/v4");
5
+ exports.ProvenanceSourceSchema = v4_1.z.enum(['DONOBU_STUDIO', 'CODE']);
6
+ exports.GitProvenanceSchema = v4_1.z
7
+ .object({
8
+ sha: v4_1.z.string().optional().describe('The git commit SHA at HEAD.'),
9
+ branch: v4_1.z
10
+ .string()
11
+ .optional()
12
+ .describe('The git branch at HEAD; absent in detached-HEAD state.'),
13
+ dirty: v4_1.z
14
+ .boolean()
15
+ .optional()
16
+ .describe('True if the working tree had uncommitted changes.'),
17
+ })
18
+ .loose();
19
+ exports.CiProvenanceSchema = v4_1.z
20
+ .object({
21
+ provider: v4_1.z
22
+ .string()
23
+ .optional()
24
+ .describe('The CI provider name when identified (e.g. "github", "gitlab", "circle"); ' +
25
+ 'absent when only a generic CI=true marker was present.'),
26
+ })
27
+ .loose();
28
+ /**
29
+ * Provenance — records *how* a Suite/Test/Flow came into existence so downstream
30
+ * code can render origin badges and gate operations that aren't safe across run
31
+ * modes (e.g. running a CODE-created test from Studio).
32
+ *
33
+ * `.loose()` so newer SDKs can write extra provenance fields without breaking
34
+ * older readers that round-trip the metadata blob.
35
+ */
36
+ exports.ProvenanceSchema = v4_1.z
37
+ .object({
38
+ source: exports.ProvenanceSourceSchema.describe('The run mode that created this entity. ' +
39
+ 'DONOBU_STUDIO = created via the Studio desktop app (HTTP API → managers); ' +
40
+ "CODE = created via a code-first SDK fixture (e.g. `donobu`'s `test` " +
41
+ "or `donobu-mobile`'s `mobileTest`)."),
42
+ user: v4_1.z
43
+ .string()
44
+ .optional()
45
+ .describe('The OS username under which the entity was created.'),
46
+ os: v4_1.z
47
+ .string()
48
+ .optional()
49
+ .describe('The OS platform (e.g. "darwin", "linux", "win32").'),
50
+ hostname: v4_1.z
51
+ .string()
52
+ .optional()
53
+ .describe('The hostname of the machine that created this entity.'),
54
+ ci: exports.CiProvenanceSchema.optional().describe('CI context when creation happened in a CI environment. Absent when ' +
55
+ 'no CI markers were detected.'),
56
+ git: exports.GitProvenanceSchema.optional().describe('Git working-tree context at creation time. Absent when the CWD is ' +
57
+ 'not a git repo or `git` is unavailable.'),
58
+ })
59
+ .loose();
60
+ //# sourceMappingURL=Provenance.js.map
@@ -140,6 +140,23 @@ export declare const SuiteMetadataSchema: z.ZodObject<{
140
140
  metadataVersion: z.ZodOptional<z.ZodNumber>;
141
141
  name: z.ZodString;
142
142
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
143
+ provenance: z.ZodOptional<z.ZodObject<{
144
+ source: z.ZodEnum<{
145
+ DONOBU_STUDIO: "DONOBU_STUDIO";
146
+ CODE: "CODE";
147
+ }>;
148
+ user: z.ZodOptional<z.ZodString>;
149
+ os: z.ZodOptional<z.ZodString>;
150
+ hostname: z.ZodOptional<z.ZodString>;
151
+ ci: z.ZodOptional<z.ZodObject<{
152
+ provider: z.ZodOptional<z.ZodString>;
153
+ }, z.core.$loose>>;
154
+ git: z.ZodOptional<z.ZodObject<{
155
+ sha: z.ZodOptional<z.ZodString>;
156
+ branch: z.ZodOptional<z.ZodString>;
157
+ dirty: z.ZodOptional<z.ZodBoolean>;
158
+ }, z.core.$loose>>;
159
+ }, z.core.$loose>>;
143
160
  }, z.core.$loose>;
144
161
  export type SuiteMetadata = z.infer<typeof SuiteMetadataSchema>;
145
162
  /**
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SuitesQuerySchema = exports.SuiteMetadataSchema = void 0;
4
4
  const v4_1 = require("zod/v4");
5
+ const Provenance_1 = require("./Provenance");
5
6
  const RunConfig_1 = require("./RunConfig");
6
7
  const SortOrder_1 = require("./SortOrder");
7
8
  /**
@@ -24,6 +25,8 @@ exports.SuiteMetadataSchema = RunConfig_1.RunConfigSchema.partial().extend({
24
25
  .nullable()
25
26
  .optional()
26
27
  .describe('An optional description of the suite.'),
28
+ provenance: Provenance_1.ProvenanceSchema.optional().describe('Records how this suite came into existence (Studio vs. code-first). ' +
29
+ 'Absent on rows written before provenance tracking was introduced.'),
27
30
  });
28
31
  const SuiteSortBySchema = v4_1.z.enum(['created_at', 'name']);
29
32
  /**
@@ -143,6 +143,23 @@ export declare const TestMetadataSchema: z.ZodObject<{
143
143
  INSTRUCT: "INSTRUCT";
144
144
  DETERMINISTIC: "DETERMINISTIC";
145
145
  }>;
146
+ provenance: z.ZodOptional<z.ZodObject<{
147
+ source: z.ZodEnum<{
148
+ DONOBU_STUDIO: "DONOBU_STUDIO";
149
+ CODE: "CODE";
150
+ }>;
151
+ user: z.ZodOptional<z.ZodString>;
152
+ os: z.ZodOptional<z.ZodString>;
153
+ hostname: z.ZodOptional<z.ZodString>;
154
+ ci: z.ZodOptional<z.ZodObject<{
155
+ provider: z.ZodOptional<z.ZodString>;
156
+ }, z.core.$loose>>;
157
+ git: z.ZodOptional<z.ZodObject<{
158
+ sha: z.ZodOptional<z.ZodString>;
159
+ branch: z.ZodOptional<z.ZodString>;
160
+ dirty: z.ZodOptional<z.ZodBoolean>;
161
+ }, z.core.$loose>>;
162
+ }, z.core.$loose>>;
146
163
  }, z.core.$loose>;
147
164
  export type TestMetadata = z.infer<typeof TestMetadataSchema>;
148
165
  /**
@@ -307,6 +324,23 @@ export declare const TestListItemSchema: z.ZodObject<{
307
324
  INSTRUCT: "INSTRUCT";
308
325
  DETERMINISTIC: "DETERMINISTIC";
309
326
  }>;
327
+ provenance: z.ZodOptional<z.ZodObject<{
328
+ source: z.ZodEnum<{
329
+ DONOBU_STUDIO: "DONOBU_STUDIO";
330
+ CODE: "CODE";
331
+ }>;
332
+ user: z.ZodOptional<z.ZodString>;
333
+ os: z.ZodOptional<z.ZodString>;
334
+ hostname: z.ZodOptional<z.ZodString>;
335
+ ci: z.ZodOptional<z.ZodObject<{
336
+ provider: z.ZodOptional<z.ZodString>;
337
+ }, z.core.$loose>>;
338
+ git: z.ZodOptional<z.ZodObject<{
339
+ sha: z.ZodOptional<z.ZodString>;
340
+ branch: z.ZodOptional<z.ZodString>;
341
+ dirty: z.ZodOptional<z.ZodBoolean>;
342
+ }, z.core.$loose>>;
343
+ }, z.core.$loose>>;
310
344
  flowCount: z.ZodNumber;
311
345
  latestFlow: z.ZodNullable<z.ZodObject<{
312
346
  target: z.ZodString;
@@ -478,6 +512,23 @@ export declare const TestListItemSchema: z.ZodObject<{
478
512
  FAILED: "FAILED";
479
513
  SUCCESS: "SUCCESS";
480
514
  }>>;
515
+ provenance: z.ZodOptional<z.ZodObject<{
516
+ source: z.ZodEnum<{
517
+ DONOBU_STUDIO: "DONOBU_STUDIO";
518
+ CODE: "CODE";
519
+ }>;
520
+ user: z.ZodOptional<z.ZodString>;
521
+ os: z.ZodOptional<z.ZodString>;
522
+ hostname: z.ZodOptional<z.ZodString>;
523
+ ci: z.ZodOptional<z.ZodObject<{
524
+ provider: z.ZodOptional<z.ZodString>;
525
+ }, z.core.$loose>>;
526
+ git: z.ZodOptional<z.ZodObject<{
527
+ sha: z.ZodOptional<z.ZodString>;
528
+ branch: z.ZodOptional<z.ZodString>;
529
+ dirty: z.ZodOptional<z.ZodBoolean>;
530
+ }, z.core.$loose>>;
531
+ }, z.core.$loose>>;
481
532
  }, z.core.$loose>>;
482
533
  }, z.core.$loose>;
483
534
  export type TestListItem = z.infer<typeof TestListItemSchema>;
@@ -621,6 +672,23 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
621
672
  INSTRUCT: "INSTRUCT";
622
673
  DETERMINISTIC: "DETERMINISTIC";
623
674
  }>;
675
+ provenance: z.ZodOptional<z.ZodObject<{
676
+ source: z.ZodEnum<{
677
+ DONOBU_STUDIO: "DONOBU_STUDIO";
678
+ CODE: "CODE";
679
+ }>;
680
+ user: z.ZodOptional<z.ZodString>;
681
+ os: z.ZodOptional<z.ZodString>;
682
+ hostname: z.ZodOptional<z.ZodString>;
683
+ ci: z.ZodOptional<z.ZodObject<{
684
+ provider: z.ZodOptional<z.ZodString>;
685
+ }, z.core.$loose>>;
686
+ git: z.ZodOptional<z.ZodObject<{
687
+ sha: z.ZodOptional<z.ZodString>;
688
+ branch: z.ZodOptional<z.ZodString>;
689
+ dirty: z.ZodOptional<z.ZodBoolean>;
690
+ }, z.core.$loose>>;
691
+ }, z.core.$loose>>;
624
692
  flowCount: z.ZodNumber;
625
693
  latestFlow: z.ZodNullable<z.ZodObject<{
626
694
  target: z.ZodString;
@@ -792,6 +860,23 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
792
860
  FAILED: "FAILED";
793
861
  SUCCESS: "SUCCESS";
794
862
  }>>;
863
+ provenance: z.ZodOptional<z.ZodObject<{
864
+ source: z.ZodEnum<{
865
+ DONOBU_STUDIO: "DONOBU_STUDIO";
866
+ CODE: "CODE";
867
+ }>;
868
+ user: z.ZodOptional<z.ZodString>;
869
+ os: z.ZodOptional<z.ZodString>;
870
+ hostname: z.ZodOptional<z.ZodString>;
871
+ ci: z.ZodOptional<z.ZodObject<{
872
+ provider: z.ZodOptional<z.ZodString>;
873
+ }, z.core.$loose>>;
874
+ git: z.ZodOptional<z.ZodObject<{
875
+ sha: z.ZodOptional<z.ZodString>;
876
+ branch: z.ZodOptional<z.ZodString>;
877
+ dirty: z.ZodOptional<z.ZodBoolean>;
878
+ }, z.core.$loose>>;
879
+ }, z.core.$loose>>;
795
880
  }, z.core.$loose>>;
796
881
  }, z.core.$loose>>;
797
882
  nextPageToken: z.ZodOptional<z.ZodString>;
@@ -4,6 +4,7 @@ exports.TestListItemPaginatedResultSchema = exports.TestListItemSchema = exports
4
4
  const v4_1 = require("zod/v4");
5
5
  const FlowMetadata_1 = require("./FlowMetadata");
6
6
  const PaginatedResult_1 = require("./PaginatedResult");
7
+ const Provenance_1 = require("./Provenance");
7
8
  const RunConfig_1 = require("./RunConfig");
8
9
  const RunMode_1 = require("./RunMode");
9
10
  const SortOrder_1 = require("./SortOrder");
@@ -28,6 +29,8 @@ exports.TestMetadataSchema = RunConfig_1.RunConfigSchema.extend({
28
29
  .nullable()
29
30
  .describe('The ID of the suite this test belongs to, or null if the test has not been added to a test suite (or was orphaned when the suite was deleted).'),
30
31
  nextRunMode: RunMode_1.RunModeSchema.describe('The run mode to use when creating the next flow from this test.'),
32
+ provenance: Provenance_1.ProvenanceSchema.optional().describe('Records how this test came into existence (Studio vs. code-first). ' +
33
+ 'Absent on rows written before provenance tracking was introduced.'),
31
34
  });
32
35
  const TestSortBySchema = v4_1.z.enum([
33
36
  'created_at',
@@ -43,6 +43,7 @@ function buildDonobuReport(resultsByTest) {
43
43
  const results = resultsByTest.get(test) ?? [];
44
44
  return {
45
45
  annotations: test.annotations,
46
+ tags: test.tags,
46
47
  projectName: getProjectName(test),
47
48
  // Signal "skipped" tests to the renderers the same way the JSON
48
49
  // reporter does.
@@ -356,6 +356,7 @@ function extractTests(jsonData) {
356
356
  isSelfHealed,
357
357
  objective: objectiveAnnotation?.description ?? null,
358
358
  annotations,
359
+ tags: Array.isArray(test.tags) ? test.tags : [],
359
360
  results,
360
361
  projectName: test.projectName ?? '',
361
362
  plan: null,
@@ -1367,6 +1368,7 @@ function renderHtml(report, triage, outputDir) {
1367
1368
  <span class="test-name"><span class="test-file">${esc(displayFileName)}</span> (${esc(test.specTitle)})</span>
1368
1369
  </div>
1369
1370
  ${test.plan ? `<span class="inline-reason" style="color:${reasonCfg(test.plan.plan.failureReason).color}" title="${esc(test.plan.plan.failureReason)}">${esc(reasonCfg(test.plan.plan.failureReason).label)}</span>` : ''}
1371
+ ${test.tags.map((t) => `<span class="test-tag">${esc(t)}</span>`).join('')}
1370
1372
  ${totalStepCount > 0 ? `<span class="test-step-count" title="${totalStepCount} steps">${totalStepCount} steps</span>` : ''}
1371
1373
  <span class="test-duration">${fmtDuration(totalTestDuration)}</span>
1372
1374
  </div>
@@ -1473,6 +1475,7 @@ body::before{content:'';position:fixed;top:-750px;left:50%;transform:translateX(
1473
1475
  .copy-flow-id .check-icon{color:#22c55e}
1474
1476
  .inline-reason{font-size:11px;font-weight:500;flex-shrink:0;padding:1px 8px;border-radius:4px;background:var(--overlay-light)}
1475
1477
  .test-step-count{font-size:11px;color:var(--text-dim);flex-shrink:0;font-family:var(--mono);padding:2px 8px;background:rgba(255,255,255,.04);border-radius:4px}
1478
+ .test-tag{font-size:11px;color:var(--accent);flex-shrink:0;font-family:var(--mono);padding:2px 8px;background:rgba(255,127,58,.08);border:1px solid rgba(255,127,58,.2);border-radius:4px}
1476
1479
  .test-duration{font-size:12px;color:var(--text-dim);flex-shrink:0;font-family:var(--mono)}
1477
1480
  .flow-id-detail{display:flex;align-items:center;gap:10px;margin-bottom:4px;padding:8px 12px;background:var(--surface-raised);border:1px solid var(--border-subtle);border-radius:var(--radius)}
1478
1481
  .flow-id-detail .detail-label{margin-bottom:0;font-size:10px;letter-spacing:.8px}
@@ -105,6 +105,9 @@ function renderMarkdown(report) {
105
105
  (!result && test.status === undefined)) {
106
106
  markdown += `**Status**: ⏭️ Skipped \n`;
107
107
  markdown += `**Duration**: N/A \n`;
108
+ if (Array.isArray(test.tags) && test.tags.length > 0) {
109
+ markdown += `**Tags**: ${test.tags.join(' ')} \n`;
110
+ }
108
111
  const objectiveAnnotation = test.annotations?.find((a) => a.type === 'objective');
109
112
  if (objectiveAnnotation) {
110
113
  const objective = (objectiveAnnotation.description || 'No objective provided').replace(/```/g, '\\`\\`\\`');
@@ -142,6 +145,9 @@ function renderMarkdown(report) {
142
145
  const duration = formatDuration(result.duration || 0);
143
146
  markdown += `**Status**: ${status} \n`;
144
147
  markdown += `**Duration**: ${duration} \n`;
148
+ if (Array.isArray(test.tags) && test.tags.length > 0) {
149
+ markdown += `**Tags**: ${test.tags.join(' ')} \n`;
150
+ }
145
151
  if (healed) {
146
152
  markdown += `> ❤️‍🩹 This test was automatically healed by re-running with Donobu treatment plan directives.\n\n`;
147
153
  }
@@ -0,0 +1,3 @@
1
+ import type { Provenance, ProvenanceSource } from '../models/Provenance';
2
+ export declare function buildProvenance(source: ProvenanceSource): Provenance;
3
+ //# sourceMappingURL=buildProvenance.d.ts.map
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildProvenance = buildProvenance;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ function detectCi() {
10
+ const env = process.env;
11
+ if (env.GITHUB_ACTIONS) {
12
+ return { provider: 'github' };
13
+ }
14
+ if (env.GITLAB_CI) {
15
+ return { provider: 'gitlab' };
16
+ }
17
+ if (env.CIRCLECI) {
18
+ return { provider: 'circle' };
19
+ }
20
+ if (env.BUILDKITE) {
21
+ return { provider: 'buildkite' };
22
+ }
23
+ if (env.JENKINS_URL) {
24
+ return { provider: 'jenkins' };
25
+ }
26
+ if (env.TF_BUILD) {
27
+ return { provider: 'azure-pipelines' };
28
+ }
29
+ if (env.BITBUCKET_BUILD_NUMBER) {
30
+ return { provider: 'bitbucket' };
31
+ }
32
+ if (env.TRAVIS) {
33
+ return { provider: 'travis' };
34
+ }
35
+ if (env.CI === 'true' || env.CI === '1') {
36
+ return {};
37
+ }
38
+ return undefined;
39
+ }
40
+ function detectGit() {
41
+ const opts = {
42
+ timeout: 500,
43
+ stdio: ['ignore', 'pipe', 'ignore'],
44
+ encoding: 'utf8',
45
+ windowsHide: true,
46
+ };
47
+ let sha;
48
+ try {
49
+ sha = (0, node_child_process_1.execFileSync)('git', ['rev-parse', 'HEAD'], opts).trim() || undefined;
50
+ }
51
+ catch {
52
+ // Not a git repo, git missing, or timed out — nothing useful to report.
53
+ return undefined;
54
+ }
55
+ let branch;
56
+ try {
57
+ const raw = (0, node_child_process_1.execFileSync)('git', ['rev-parse', '--abbrev-ref', 'HEAD'], opts).trim();
58
+ branch = raw && raw !== 'HEAD' ? raw : undefined;
59
+ }
60
+ catch {
61
+ /* leave undefined */
62
+ }
63
+ let dirty;
64
+ try {
65
+ const status = (0, node_child_process_1.execFileSync)('git', ['status', '--porcelain'], opts);
66
+ dirty = status.length > 0;
67
+ }
68
+ catch {
69
+ /* leave undefined */
70
+ }
71
+ return { sha, branch, dirty };
72
+ }
73
+ function buildProvenance(source) {
74
+ let user;
75
+ let hostname;
76
+ try {
77
+ user = node_os_1.default.userInfo().username;
78
+ }
79
+ catch {
80
+ // os.userInfo() can throw in containerised envs with no passwd entry.
81
+ }
82
+ try {
83
+ hostname = node_os_1.default.hostname() || undefined;
84
+ }
85
+ catch {
86
+ // Defensive: hostname() is documented to throw only on rare libuv errors.
87
+ }
88
+ // CI and git context only make sense for CODE-source entities — they
89
+ // describe the user's project environment, not Studio's own runtime. Skip
90
+ // both for DONOBU_STUDIO to avoid the `git` shell-outs on every create.
91
+ const isCode = source === 'CODE';
92
+ return {
93
+ source,
94
+ user,
95
+ os: process.platform,
96
+ hostname,
97
+ ci: isCode ? detectCi() : undefined,
98
+ git: isCode ? detectGit() : undefined,
99
+ };
100
+ }
101
+ //# sourceMappingURL=buildProvenance.js.map
@@ -23,6 +23,7 @@ const GoToWebpageTool_1 = require("../../tools/GoToWebpageTool");
23
23
  const RunAccessibilityTestTool_1 = require("../../tools/RunAccessibilityTestTool");
24
24
  const ansi_1 = require("../../utils/ansi");
25
25
  const BrowserUtils_1 = require("../../utils/BrowserUtils");
26
+ const buildProvenance_1 = require("../../utils/buildProvenance");
26
27
  const Logger_1 = require("../../utils/Logger");
27
28
  const MiscUtils_1 = require("../../utils/MiscUtils");
28
29
  const PlaywrightUtils_1 = require("../../utils/PlaywrightUtils");
@@ -120,6 +121,7 @@ async function extendPage(page, options) {
120
121
  completedAt: null,
121
122
  state: 'RUNNING_ACTION',
122
123
  nextState: null,
124
+ provenance: (0, buildProvenance_1.buildProvenance)('CODE'),
123
125
  },
124
126
  interactionVisualizer: interactionVisualizer,
125
127
  donobuStack: donobuStack,
@@ -33,6 +33,7 @@ const DonobuFlowsManager_1 = require("../../managers/DonobuFlowsManager");
33
33
  const fileUploadWorkerRegistry_1 = require("../../persistence/files/fileUploadWorkerRegistry");
34
34
  const ansi_1 = require("../../utils/ansi");
35
35
  const BrowserUtils_1 = require("../../utils/BrowserUtils");
36
+ const buildProvenance_1 = require("../../utils/buildProvenance");
36
37
  const FlowLogBuffer_1 = require("../../utils/FlowLogBuffer");
37
38
  const Logger_1 = require("../../utils/Logger");
38
39
  const MiscUtils_1 = require("../../utils/MiscUtils");
@@ -945,6 +946,7 @@ function flowMetadataToTestMetadata(testId, flowMeta) {
945
946
  maxToolCalls: flowMeta.maxToolCalls,
946
947
  suiteId: null,
947
948
  nextRunMode: 'DETERMINISTIC',
949
+ provenance: (0, buildProvenance_1.buildProvenance)('CODE'),
948
950
  };
949
951
  }
950
952
  //# sourceMappingURL=testExtension.js.map