@flowriki/agent 0.1.0-beta.41 → 0.1.0-beta.43

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.
@@ -6,7 +6,7 @@ import os from "os";
6
6
  import path from "path";
7
7
  import { createRequire } from "module";
8
8
  import { spawnSync } from "child_process";
9
- var AGENT_VERSION = "0.1.0-beta.41";
9
+ var AGENT_VERSION = "0.1.0-beta.43";
10
10
  async function prepareBrowsers() {
11
11
  if (browsersReady()) {
12
12
  return { ok: true, already: true, message: "chromium is already installed" };
@@ -1008,6 +1008,7 @@ import { z as z42 } from "zod";
1008
1008
  import { z as z5 } from "zod";
1009
1009
  import { z as z6 } from "zod";
1010
1010
  import { z as z7 } from "zod";
1011
+ import { z as z8 } from "zod";
1011
1012
  var EngineSchema = z4.enum(["chromium", "firefox", "webkit"]);
1012
1013
  var CapabilitySchema = z4.object({
1013
1014
  id: z4.string(),
@@ -1036,18 +1037,44 @@ var ExecutionConfigSchema = z4.object({
1036
1037
  })
1037
1038
  ).default({})
1038
1039
  });
1039
- var FlowOccurrenceSchema = z22.object({
1040
+ var BUSINESS_KEYWORDS = ["Given", "When", "Then", "And", "But"];
1041
+ var BusinessStepSchema = z22.object({
1042
+ keyword: z22.enum(BUSINESS_KEYWORDS),
1043
+ text: z22.string().min(1).max(2e3)
1044
+ }).strict();
1045
+ var TestCaseOriginSchema = z22.object({
1046
+ kind: z22.enum(["import", "assistant"]),
1047
+ at: z22.string(),
1048
+ by: z22.string().default(""),
1049
+ /** The file it was imported from, and its row or id there. */
1050
+ file: z22.string().max(300).optional(),
1051
+ row: z22.string().max(200).optional()
1052
+ }).strict();
1053
+ var KEYWORD = /^\s*(given|when|then|and|but)\b[\s:]*(.*)$/i;
1054
+ var keywordOf = (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
1055
+ function parseGherkin(text) {
1056
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).filter((l) => !/^(scenario|scenario outline|feature|background|examples)\s*:/i.test(l) && !l.startsWith("|"));
1057
+ if (!lines.length) return null;
1058
+ const steps = [];
1059
+ for (const line of lines) {
1060
+ const m = KEYWORD.exec(line);
1061
+ if (!m || !m[2].trim()) return null;
1062
+ steps.push({ keyword: keywordOf(m[1]), text: m[2].trim() });
1063
+ }
1064
+ return steps;
1065
+ }
1066
+ var FlowOccurrenceSchema = z32.object({
1040
1067
  /** This copy's own identity. Not the source flow's — a flow added twice is
1041
1068
  * two occurrences, and editing, breakpoints, results and deletion must be
1042
1069
  * able to tell them apart. */
1043
- occurrenceId: z22.string().min(1),
1044
- actorId: z22.string().optional(),
1070
+ occurrenceId: z32.string().min(1),
1071
+ actorId: z32.string().optional(),
1045
1072
  /** Editable, and initially the source flow's name. */
1046
- name: z22.string().min(1),
1073
+ name: z32.string().min(1),
1047
1074
  /** Where this copy starts, when it is told to navigate before running.
1048
1075
  * Absent means continue from wherever the previous row left the browser. */
1049
- startPath: z22.string().optional(),
1050
- baseUrl: z22.string().url().optional(),
1076
+ startPath: z32.string().optional(),
1077
+ baseUrl: z32.string().url().optional(),
1051
1078
  /** GO SOMEWHERE FIRST, or carry on where the last row ended.
1052
1079
  *
1053
1080
  * SUPERSEDED BY `start`, and still read so that every case written before
@@ -1057,7 +1084,7 @@ var FlowOccurrenceSchema = z22.object({
1057
1084
  * Off by default, because a case is a sequence in one browser: re-entering
1058
1085
  * each flow's original starting URL would sign the tester out between rows
1059
1086
  * and is exactly the behaviour composing flows is meant to avoid. */
1060
- navigateFirst: z22.boolean().default(false),
1087
+ navigateFirst: z32.boolean().default(false),
1061
1088
  /** WHICH BROWSER THIS ROW RUNS IN.
1062
1089
  *
1063
1090
  * Two answers, because there are two: carry on in the browser this
@@ -1080,7 +1107,7 @@ var FlowOccurrenceSchema = z22.object({
1080
1107
  * A participant's FIRST row is always fresh in effect: there is nothing to
1081
1108
  * continue from. That is not stored, because it is a fact about the order
1082
1109
  * rather than about the row — moving the row changes the answer. */
1083
- start: z22.enum(["continue", "navigate", "fresh"]).optional(),
1110
+ start: z32.enum(["continue", "navigate", "fresh"]).optional(),
1084
1111
  /** WHETHER THIS COPY DOES ITS OWN SIGNING IN.
1085
1112
  *
1086
1113
  * A flow recorded with "sign in before recording" keeps that sign-in as its
@@ -1099,28 +1126,28 @@ var FlowOccurrenceSchema = z22.object({
1099
1126
  * the two agree on the account and is refused where they do not — so a case
1100
1127
  * written before this keeps its meaning, and a contradiction stays a
1101
1128
  * contradiction until somebody actually resolves it. */
1102
- ownSignIn: z22.enum(["skip", "run"]).optional(),
1103
- steps: z22.array(RecordedStepSchema),
1129
+ ownSignIn: z32.enum(["skip", "run"]).optional(),
1130
+ steps: z32.array(RecordedStepSchema),
1104
1131
  environment: ExecutionEnvironmentSchema.optional(),
1105
1132
  /** Where this copy came from, so the editor can say whether the source has
1106
1133
  * moved on — informational only, never a trigger to rewrite anything. */
1107
- origin: z22.object({
1108
- flowId: z22.string(),
1134
+ origin: z32.object({
1135
+ flowId: z32.string(),
1109
1136
  /** The schema the source was written in, which may be older than today's. */
1110
- schemaVersion: z22.number().int().nonnegative(),
1137
+ schemaVersion: z32.number().int().nonnegative(),
1111
1138
  /** The source's own revision counter at the moment it was copied. */
1112
- revision: z22.number().int().nonnegative(),
1139
+ revision: z32.number().int().nonnegative(),
1113
1140
  /** The commit the copy was taken from, when one was known. */
1114
- commit: z22.string().optional(),
1115
- featureId: z22.string(),
1141
+ commit: z32.string().optional(),
1142
+ featureId: z32.string(),
1116
1143
  /** Names as they were, so an old case still reads correctly after the
1117
1144
  * source is renamed, moved or deleted. */
1118
- featureName: z22.string(),
1119
- flowName: z22.string(),
1145
+ featureName: z32.string(),
1146
+ flowName: z32.string(),
1120
1147
  /** Source step id → the id it was given here. Kept so a later comparison
1121
1148
  * against the source can line the two up; nothing depends on it to run. */
1122
- stepIds: z22.record(z22.string(), z22.string()).default({}),
1123
- copiedAt: z22.string()
1149
+ stepIds: z32.record(z32.string(), z32.string()).default({}),
1150
+ copiedAt: z32.string()
1124
1151
  }),
1125
1152
  /** WHICH OF THIS ROW'S STEPS ARE WHAT RUNS BEFORE ITS FLOW — the flow's
1126
1153
  * sign-in and the prerequisites copied in with it — so the row can be shown
@@ -1131,24 +1158,24 @@ var FlowOccurrenceSchema = z22.object({
1131
1158
  * id, so editing or deleting one of them leaves the rest still grouped.
1132
1159
  * Absent on every row copied before this existed, which simply show flat.
1133
1160
  * Never sent to a machine — see `withoutFolding`. */
1134
- before: z22.object({
1161
+ before: z32.object({
1135
1162
  /** The flow's own sign-in, when the copy brought one. */
1136
- signInStepId: z22.string().optional(),
1137
- flows: z22.array(z22.object({
1138
- flowId: z22.string(),
1139
- name: z22.string(),
1140
- stepIds: z22.array(z22.string())
1163
+ signInStepId: z32.string().optional(),
1164
+ flows: z32.array(z32.object({
1165
+ flowId: z32.string(),
1166
+ name: z32.string(),
1167
+ stepIds: z32.array(z32.string())
1141
1168
  }).strict()).default([])
1142
1169
  }).strict().optional()
1143
1170
  }).strict();
1144
1171
  var CASE_SCHEMA_VERSION = 1;
1145
- var TestCaseSchema = z22.object({
1146
- schemaVersion: z22.literal(CASE_SCHEMA_VERSION),
1147
- id: z22.string().min(1),
1148
- folderId: z22.string().min(1),
1149
- slug: z22.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1150
- name: z22.string().min(1),
1151
- description: z22.string().default(""),
1172
+ var TestCaseSchema = z32.object({
1173
+ schemaVersion: z32.literal(CASE_SCHEMA_VERSION),
1174
+ id: z32.string().min(1),
1175
+ folderId: z32.string().min(1),
1176
+ slug: z32.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1177
+ name: z32.string().min(1),
1178
+ description: z32.string().default(""),
1152
1179
  /** NO LONGER USED, and kept only so that a case written while they were
1153
1180
  * parses.
1154
1181
  *
@@ -1161,19 +1188,25 @@ var TestCaseSchema = z22.object({
1161
1188
  *
1162
1189
  * Every row now starts where its own flow says, which is the same answer
1163
1190
  * whatever the order. */
1164
- startPath: z22.string().default("/"),
1165
- useStartPath: z22.boolean().default(false),
1166
- baseUrl: z22.string().url().optional(),
1191
+ startPath: z32.string().default("/"),
1192
+ useStartPath: z32.boolean().default(false),
1193
+ baseUrl: z32.string().url().optional(),
1167
1194
  environment: ExecutionEnvironmentSchema.optional(),
1168
- actors: z22.array(ActorDefinitionSchema).optional(),
1169
- defaultActorId: z22.string().optional(),
1170
- occurrences: z22.array(FlowOccurrenceSchema).default([]),
1171
- createdAt: z22.string(),
1172
- createdBy: z22.string().default(""),
1173
- updatedAt: z22.string(),
1195
+ actors: z32.array(ActorDefinitionSchema).optional(),
1196
+ defaultActorId: z32.string().optional(),
1197
+ occurrences: z32.array(FlowOccurrenceSchema).default([]),
1198
+ /** WHAT IT CHECKS, IN WORDS: Given / When / Then. Written for a case that is
1199
+ * not automated yet — imported, or written by the assistant — and kept when
1200
+ * it is. Never sent to a machine — see `withoutFolding`. */
1201
+ businessSteps: z32.array(BusinessStepSchema).optional(),
1202
+ /** Where it came from, when not recorded here. */
1203
+ origin: TestCaseOriginSchema.optional(),
1204
+ createdAt: z32.string(),
1205
+ createdBy: z32.string().default(""),
1206
+ updatedAt: z32.string(),
1174
1207
  /** The same optimistic-concurrency counter a flow carries, so an edit made
1175
1208
  * against a stale copy is refused rather than overwriting. */
1176
- revision: z22.number().int().nonnegative().default(0)
1209
+ revision: z32.number().int().nonnegative().default(0)
1177
1210
  }).strict().superRefine((testCase, ctx) => {
1178
1211
  const seen = /* @__PURE__ */ new Set();
1179
1212
  testCase.occurrences.forEach((occurrence, i) => {
@@ -1202,47 +1235,47 @@ var TestCaseSchema = z22.object({
1202
1235
  const { defaultActorId: _gone, ...rest } = testCase;
1203
1236
  return rest;
1204
1237
  });
1205
- var CaseFolderSchema = z22.object({
1206
- schemaVersion: z22.literal(1),
1207
- id: z22.string().min(1),
1208
- slug: z22.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1209
- name: z22.string().min(1),
1210
- description: z22.string().default(""),
1211
- order: z22.number().int().default(0),
1212
- createdAt: z22.string(),
1213
- createdBy: z22.string().default("")
1238
+ var CaseFolderSchema = z32.object({
1239
+ schemaVersion: z32.literal(1),
1240
+ id: z32.string().min(1),
1241
+ slug: z32.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1242
+ name: z32.string().min(1),
1243
+ description: z32.string().default(""),
1244
+ order: z32.number().int().default(0),
1245
+ createdAt: z32.string(),
1246
+ createdBy: z32.string().default("")
1214
1247
  }).strict();
1215
1248
  var SUITE_SCHEMA_VERSION = 1;
1216
- var SuiteEntrySchema = z22.object({
1217
- entryId: z22.string().min(1),
1218
- kind: z22.enum(["flow", "testCase"]),
1249
+ var SuiteEntrySchema = z32.object({
1250
+ entryId: z32.string().min(1),
1251
+ kind: z32.enum(["flow", "testCase"]),
1219
1252
  /** The document's ID — never its name or path, so renaming or moving what it
1220
1253
  * points at cannot break the reference. */
1221
- ref: z22.string().min(1),
1222
- enabled: z22.boolean().default(true),
1223
- order: z22.number().int().nonnegative(),
1254
+ ref: z32.string().min(1),
1255
+ enabled: z32.boolean().default(true),
1256
+ order: z32.number().int().nonnegative(),
1224
1257
  /** What it was called when it was added, so a result stays readable after the
1225
1258
  * source is renamed or deleted. */
1226
- nameSnapshot: z22.string().default("")
1259
+ nameSnapshot: z32.string().default("")
1227
1260
  }).strict();
1228
- var SuiteSchema = z22.object({
1229
- schemaVersion: z22.literal(SUITE_SCHEMA_VERSION),
1230
- id: z22.string().min(1),
1231
- slug: z22.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1232
- name: z22.string().min(1),
1233
- description: z22.string().default(""),
1234
- entries: z22.array(SuiteEntrySchema).default([]),
1235
- defaults: z22.object({
1236
- headless: z22.boolean().optional(),
1261
+ var SuiteSchema = z32.object({
1262
+ schemaVersion: z32.literal(SUITE_SCHEMA_VERSION),
1263
+ id: z32.string().min(1),
1264
+ slug: z32.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1265
+ name: z32.string().min(1),
1266
+ description: z32.string().default(""),
1267
+ entries: z32.array(SuiteEntrySchema).default([]),
1268
+ defaults: z32.object({
1269
+ headless: z32.boolean().optional(),
1237
1270
  /** Carry on through the remaining entries, or stop at the first failure.
1238
1271
  * Continuing is the default: a suite is a list of independent things and
1239
1272
  * one failure rarely invalidates the rest. */
1240
- failurePolicy: z22.enum(["continue", "stop"]).default("continue")
1273
+ failurePolicy: z32.enum(["continue", "stop"]).default("continue")
1241
1274
  }).default({ failurePolicy: "continue" }),
1242
- createdAt: z22.string(),
1243
- createdBy: z22.string().default(""),
1244
- updatedAt: z22.string(),
1245
- revision: z22.number().int().nonnegative().default(0)
1275
+ createdAt: z32.string(),
1276
+ createdBy: z32.string().default(""),
1277
+ updatedAt: z32.string(),
1278
+ revision: z32.number().int().nonnegative().default(0)
1246
1279
  }).strict().superRefine((suite, ctx) => {
1247
1280
  const seen = /* @__PURE__ */ new Set();
1248
1281
  suite.entries.forEach((entry, i) => {
@@ -1433,19 +1466,19 @@ function restoreDebugCase(testCase, owners, steps) {
1433
1466
  }
1434
1467
  return { ...testCase, occurrences };
1435
1468
  }
1436
- var AuthNavigationSchema = z32.discriminatedUnion("kind", [
1469
+ var AuthNavigationSchema = z42.discriminatedUnion("kind", [
1437
1470
  /** Act from wherever the browser already is — a "Log in" link in the header.
1438
1471
  * The only kind that is safe part-way through a journey. */
1439
- z32.object({ kind: z32.literal("continue") }),
1472
+ z42.object({ kind: z42.literal("continue") }),
1440
1473
  /** Open the procedure's own starting page first. Right when signing in is
1441
1474
  * the first thing that happens; it abandons a journey in progress. */
1442
- z32.object({ kind: z32.literal("open"), path: z32.string().min(1) })
1475
+ z42.object({ kind: z42.literal("open"), path: z42.string().min(1) })
1443
1476
  ]);
1444
- var AuthFieldSchema = z32.object({
1477
+ var AuthFieldSchema = z42.object({
1445
1478
  /** The step in the recorded procedure this value is typed into. Where a
1446
1479
  * recording types into the same box more than once, this is the FIRST of
1447
1480
  * them — the one an account's values are keyed by. */
1448
- stepId: z32.string().min(1),
1481
+ stepId: z42.string().min(1),
1449
1482
  /** EVERY step that types into this box, when there is more than one.
1450
1483
  *
1451
1484
  * A login typed twice — a first attempt with a typo, a page that re-rendered
@@ -1457,47 +1490,47 @@ var AuthFieldSchema = z32.object({
1457
1490
  *
1458
1491
  * Empty means the one step in `stepId`, so every profile written before this
1459
1492
  * reads correctly without being migrated. */
1460
- stepIds: z32.array(z32.string().min(1)).default([]),
1493
+ stepIds: z42.array(z42.string().min(1)).default([]),
1461
1494
  /** What it is called where somebody fills it in — the field's own name on
1462
1495
  * the page, so an account form reads "Username" rather than "st_9f2a". */
1463
- label: z32.string().min(1),
1496
+ label: z42.string().min(1),
1464
1497
  /** A secret arrives as a reference and is never written into a document. */
1465
- secret: z32.boolean().default(false),
1498
+ secret: z42.boolean().default(false),
1466
1499
  /** What the recording typed. Empty for a secret, whose value never travels.
1467
1500
  * Kept so the first account can be made from what was recorded, with
1468
1501
  * nothing to fill in. */
1469
- recorded: z32.string().default("")
1502
+ recorded: z42.string().default("")
1470
1503
  }).strict();
1471
1504
  var AUTH_PROFILE_SCHEMA_VERSION = 2;
1472
- var AuthProfileSchema = z32.object({
1473
- schemaVersion: z32.literal(AUTH_PROFILE_SCHEMA_VERSION),
1474
- id: z32.string().min(1),
1475
- slug: z32.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1476
- name: z32.string().min(1),
1477
- description: z32.string().default(""),
1505
+ var AuthProfileSchema = z42.object({
1506
+ schemaVersion: z42.literal(AUTH_PROFILE_SCHEMA_VERSION),
1507
+ id: z42.string().min(1),
1508
+ slug: z42.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1509
+ name: z42.string().min(1),
1510
+ description: z42.string().default(""),
1478
1511
  /** The recorded sign-in. A flow like any other, so it is edited, debugged
1479
1512
  * and replayed by everything that already knows how. */
1480
- flowId: z32.string().min(1),
1513
+ flowId: z42.string().min(1),
1481
1514
  /** Which of the values it types differ per identity. */
1482
- fields: z32.array(AuthFieldSchema).default([]),
1515
+ fields: z42.array(AuthFieldSchema).default([]),
1483
1516
  /** Optional, and its own procedure with its own proof — most profiles will
1484
1517
  * not have one at first. The proof is read off that flow, the same way. */
1485
- signOutFlowId: z32.string().optional(),
1518
+ signOutFlowId: z42.string().optional(),
1486
1519
  /** Descriptive only — "admin", "staff". Never used to decide anything, so
1487
1520
  * two profiles may share a tag without becoming interchangeable. */
1488
- tags: z32.array(z32.string()).default([]),
1489
- createdAt: z32.string(),
1490
- createdBy: z32.string().default(""),
1491
- updatedAt: z32.string(),
1492
- revision: z32.number().int().nonnegative().default(0)
1521
+ tags: z42.array(z42.string()).default([]),
1522
+ createdAt: z42.string(),
1523
+ createdBy: z42.string().default(""),
1524
+ updatedAt: z42.string(),
1525
+ revision: z42.number().int().nonnegative().default(0)
1493
1526
  }).strict();
1494
1527
  var ACCOUNT_BINDING_SCHEMA_VERSION = 3;
1495
- var AccountBindingSchema = z32.object({
1496
- schemaVersion: z32.literal(ACCOUNT_BINDING_SCHEMA_VERSION),
1497
- id: z32.string().min(1),
1498
- slug: z32.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1499
- name: z32.string().min(1),
1500
- profileId: z32.string().min(1),
1528
+ var AccountBindingSchema = z42.object({
1529
+ schemaVersion: z42.literal(ACCOUNT_BINDING_SCHEMA_VERSION),
1530
+ id: z42.string().min(1),
1531
+ slug: z42.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1532
+ name: z42.string().min(1),
1533
+ profileId: z42.string().min(1),
1501
1534
  /** Which environments of the APPLICATION UNDER TEST these credentials work
1502
1535
  * in — never Flowriki's own dev or staging.
1503
1536
  *
@@ -1509,47 +1542,47 @@ var AccountBindingSchema = z32.object({
1509
1542
  * duplicate account — same name, same password, same everything — for each
1510
1543
  * deployment the identity existed in, or a blank "works anywhere" that also
1511
1544
  * claimed environments it had never been created in. Neither was true. */
1512
- environments: z32.array(z32.string().min(1)).default([]),
1545
+ environments: z42.array(z42.string().min(1)).default([]),
1513
1546
  /** Step id → what this identity types there. Ordinary values: a username is
1514
1547
  * not a credential, and keeping it here is what lets somebody read an
1515
1548
  * account and know who it is. */
1516
- values: z32.record(z32.string(), z32.string()).default({}),
1549
+ values: z42.record(z42.string(), z42.string()).default({}),
1517
1550
  /** Step id → secret REFERENCE. The value is sealed in the vault and never
1518
1551
  * enters the repository, whatever form it was typed into. */
1519
- secrets: z32.record(z32.string(), z32.string().regex(/^[a-z][a-z0-9_]*$/i)).default({}),
1552
+ secrets: z42.record(z42.string(), z42.string().regex(/^[a-z][a-z0-9_]*$/i)).default({}),
1520
1553
  /** What this identity should look like once signed in — a displayed name, a
1521
1554
  * tenant. Not secret, and checked so that an already-authenticated browser
1522
1555
  * holding the WRONG account is reported rather than accepted. */
1523
- identity: z32.object({
1524
- label: z32.string().optional(),
1525
- tenant: z32.string().optional()
1556
+ identity: z42.object({
1557
+ label: z42.string().optional(),
1558
+ tenant: z42.string().optional()
1526
1559
  }).default({}),
1527
- createdAt: z32.string(),
1528
- createdBy: z32.string().default(""),
1529
- updatedAt: z32.string(),
1530
- revision: z32.number().int().nonnegative().default(0)
1560
+ createdAt: z42.string(),
1561
+ createdBy: z42.string().default(""),
1562
+ updatedAt: z42.string(),
1563
+ revision: z42.number().int().nonnegative().default(0)
1531
1564
  }).strict();
1532
- var AuthReferenceSchema = z32.object({
1533
- profileId: z32.string().min(1),
1565
+ var AuthReferenceSchema = z42.object({
1566
+ profileId: z42.string().min(1),
1534
1567
  /** Where this use of it begins. See the step's own field. */
1535
- from: z32.enum(["loginPage", "here"]).optional(),
1568
+ from: z42.enum(["loginPage", "here"]).optional(),
1536
1569
  /** Absent means "the only account bound to this profile for the selected
1537
1570
  * environment", which is resolved before a browser opens and refused when
1538
1571
  * it is ambiguous — a silent choice between two identities is the failure
1539
1572
  * this field exists to prevent. */
1540
- accountId: z32.string().optional()
1573
+ accountId: z42.string().optional()
1541
1574
  }).strict();
1542
1575
  var ActorSchema = ActorDefinitionSchema;
1543
- var DeliveredAuthSchema = z32.object({
1544
- profileId: z32.string().min(1),
1545
- accountId: z32.string().min(1),
1546
- name: z32.string(),
1547
- accountName: z32.string(),
1548
- steps: z32.array(RecordedStepSchema),
1576
+ var DeliveredAuthSchema = z42.object({
1577
+ profileId: z42.string().min(1),
1578
+ accountId: z42.string().min(1),
1579
+ name: z42.string(),
1580
+ accountName: z42.string(),
1581
+ steps: z42.array(RecordedStepSchema),
1549
1582
  navigation: AuthNavigationSchema,
1550
- success: z32.array(RecordedAssertionSchema),
1551
- secretRefs: z32.record(z32.string(), z32.string()),
1552
- signOut: z32.object({ steps: z32.array(RecordedStepSchema), success: z32.array(RecordedAssertionSchema) }).optional()
1583
+ success: z42.array(RecordedAssertionSchema),
1584
+ secretRefs: z42.record(z42.string(), z42.string()),
1585
+ signOut: z42.object({ steps: z42.array(RecordedStepSchema), success: z42.array(RecordedAssertionSchema) }).optional()
1553
1586
  }).strict();
1554
1587
  function actorPreparations(actors = []) {
1555
1588
  return actors.flatMap((actor) => actor.startSignedIn ? [{
@@ -1666,8 +1699,8 @@ function expandForRun(flow, lookup) {
1666
1699
  return { ...rest, steps: [...signIn ? [signIn] : [], ...steps, ...flow.steps] };
1667
1700
  }
1668
1701
  var HELPER_SCHEMA_VERSION = 1;
1669
- var SecretRef = z42.string().regex(/^[a-z][a-z0-9_]*$/i);
1670
- var PatternSchema = z42.string().max(500).refine((source) => {
1702
+ var SecretRef = z5.string().regex(/^[a-z][a-z0-9_]*$/i);
1703
+ var PatternSchema = z5.string().max(500).refine((source) => {
1671
1704
  try {
1672
1705
  new RegExp(source);
1673
1706
  return true;
@@ -1675,88 +1708,88 @@ var PatternSchema = z42.string().max(500).refine((source) => {
1675
1708
  return false;
1676
1709
  }
1677
1710
  }, "that is not a valid pattern");
1678
- var MailboxHelperSchema = z42.object({
1679
- kind: z42.literal("mailbox"),
1711
+ var MailboxHelperSchema = z5.object({
1712
+ kind: z5.literal("mailbox"),
1680
1713
  /** IMAP, which every provider offers: Gmail, Outlook, Zoho, Fastmail… */
1681
- host: z42.string().min(1),
1682
- port: z42.number().int().positive().default(993),
1683
- secure: z42.boolean().default(true),
1684
- username: z42.string().min(1),
1714
+ host: z5.string().min(1),
1715
+ port: z5.number().int().positive().default(993),
1716
+ secure: z5.boolean().default(true),
1717
+ username: z5.string().min(1),
1685
1718
  passwordRef: SecretRef,
1686
- folder: z42.string().min(1).default("INBOX"),
1719
+ folder: z5.string().min(1).default("INBOX"),
1687
1720
  /** Which email. All optional; the newest that matches every one given. */
1688
- match: z42.object({
1689
- to: z42.string().default(""),
1690
- from: z42.string().default(""),
1691
- subject: z42.string().default("")
1721
+ match: z5.object({
1722
+ to: z5.string().default(""),
1723
+ from: z5.string().default(""),
1724
+ subject: z5.string().default("")
1692
1725
  }).strict().default({}),
1693
1726
  /** What to take out of it. */
1694
- extract: z42.object({
1695
- take: z42.enum(["code", "link", "subject", "body"]).default("code"),
1727
+ extract: z5.object({
1728
+ take: z5.enum(["code", "link", "subject", "body"]).default("code"),
1696
1729
  /** For a code: the pattern, first group if it has one. Default: 4–8 digits.
1697
1730
  * For a link: only links containing this text. */
1698
- pattern: z42.string().max(500).default("")
1731
+ pattern: z5.string().max(500).default("")
1699
1732
  }).strict().default({}),
1700
1733
  /** How long to wait for the email to arrive. */
1701
- waitSeconds: z42.number().int().min(0).max(300).default(60)
1734
+ waitSeconds: z5.number().int().min(0).max(300).default(60)
1702
1735
  }).strict();
1703
- var TotpHelperSchema = z42.object({
1704
- kind: z42.literal("totp"),
1736
+ var TotpHelperSchema = z5.object({
1737
+ kind: z5.literal("totp"),
1705
1738
  /** The authenticator secret (base32, from the QR code's "manual entry"). */
1706
1739
  secretRef: SecretRef,
1707
- digits: z42.union([z42.literal(6), z42.literal(8)]).default(6),
1708
- period: z42.number().int().min(10).max(120).default(30),
1709
- algorithm: z42.enum(["SHA1", "SHA256", "SHA512"]).default("SHA1")
1740
+ digits: z5.union([z5.literal(6), z5.literal(8)]).default(6),
1741
+ period: z5.number().int().min(10).max(120).default(30),
1742
+ algorithm: z5.enum(["SHA1", "SHA256", "SHA512"]).default("SHA1")
1710
1743
  }).strict();
1711
- var SmsHelperSchema = z42.object({
1712
- kind: z42.literal("sms"),
1713
- provider: z42.literal("twilio").default("twilio"),
1714
- accountSid: z42.string().regex(/^AC[0-9a-f]{32}$/i, "a Twilio account SID starts with AC followed by 32 characters"),
1744
+ var SmsHelperSchema = z5.object({
1745
+ kind: z5.literal("sms"),
1746
+ provider: z5.literal("twilio").default("twilio"),
1747
+ accountSid: z5.string().regex(/^AC[0-9a-f]{32}$/i, "a Twilio account SID starts with AC followed by 32 characters"),
1715
1748
  authTokenRef: SecretRef,
1716
1749
  /** The Twilio number the application texts. */
1717
- to: z42.string().min(3),
1718
- match: z42.object({
1719
- from: z42.string().default(""),
1720
- contains: z42.string().default("")
1750
+ to: z5.string().min(3),
1751
+ match: z5.object({
1752
+ from: z5.string().default(""),
1753
+ contains: z5.string().default("")
1721
1754
  }).strict().default({}),
1722
- extract: z42.object({ pattern: z42.string().max(500).default("") }).strict().default({}),
1723
- waitSeconds: z42.number().int().min(0).max(300).default(60)
1755
+ extract: z5.object({ pattern: z5.string().max(500).default("") }).strict().default({}),
1756
+ waitSeconds: z5.number().int().min(0).max(300).default(60)
1724
1757
  }).strict();
1725
- var HttpHelperSchema = z42.object({
1726
- kind: z42.literal("http"),
1727
- method: z42.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("POST"),
1728
- url: z42.string().min(1),
1758
+ var HttpHelperSchema = z5.object({
1759
+ kind: z5.literal("http"),
1760
+ method: z5.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("POST"),
1761
+ url: z5.string().min(1),
1729
1762
  /** Plain headers, in the repository. */
1730
- headers: z42.record(z42.string(), z42.string()).default({}),
1763
+ headers: z5.record(z5.string(), z5.string()).default({}),
1731
1764
  /** Header name → vault reference, for tokens and keys. */
1732
- secretHeaders: z42.record(z42.string(), SecretRef).default({}),
1733
- body: z42.string().max(2e4).default(""),
1765
+ secretHeaders: z5.record(z5.string(), SecretRef).default({}),
1766
+ body: z5.string().max(2e4).default(""),
1734
1767
  /** Refused unless the response has this status. 0 means any 2xx. */
1735
- expectStatus: z42.number().int().min(0).max(599).default(0),
1736
- extract: z42.object({
1737
- take: z42.enum(["json", "pattern", "status", "nothing"]).default("nothing"),
1768
+ expectStatus: z5.number().int().min(0).max(599).default(0),
1769
+ extract: z5.object({
1770
+ take: z5.enum(["json", "pattern", "status", "nothing"]).default("nothing"),
1738
1771
  /** For json: a dot path — `data.order.id`, `items.0.code`. */
1739
- path: z42.string().max(300).default(""),
1740
- pattern: z42.string().max(500).default("")
1772
+ path: z5.string().max(300).default(""),
1773
+ pattern: z5.string().max(500).default("")
1741
1774
  }).strict().default({})
1742
1775
  }).strict();
1743
- var HelperConfigSchema = z42.discriminatedUnion("kind", [
1776
+ var HelperConfigSchema = z5.discriminatedUnion("kind", [
1744
1777
  MailboxHelperSchema,
1745
1778
  TotpHelperSchema,
1746
1779
  SmsHelperSchema,
1747
1780
  HttpHelperSchema
1748
1781
  ]);
1749
- var HelperSchema = z42.object({
1750
- schemaVersion: z42.literal(HELPER_SCHEMA_VERSION),
1751
- id: z42.string().min(1),
1752
- slug: z42.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1753
- name: z42.string().min(1).max(120),
1754
- description: z42.string().max(1e3).default(""),
1782
+ var HelperSchema = z5.object({
1783
+ schemaVersion: z5.literal(HELPER_SCHEMA_VERSION),
1784
+ id: z5.string().min(1),
1785
+ slug: z5.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1786
+ name: z5.string().min(1).max(120),
1787
+ description: z5.string().max(1e3).default(""),
1755
1788
  config: HelperConfigSchema,
1756
- createdAt: z42.string(),
1757
- createdBy: z42.string().default(""),
1758
- updatedAt: z42.string(),
1759
- revision: z42.number().int().nonnegative().default(0)
1789
+ createdAt: z5.string(),
1790
+ createdBy: z5.string().default(""),
1791
+ updatedAt: z5.string(),
1792
+ revision: z5.number().int().nonnegative().default(0)
1760
1793
  }).strict().superRefine((helper, ctx) => {
1761
1794
  const patterns = [
1762
1795
  helper.config.kind === "mailbox" && helper.config.extract.take === "code" ? helper.config.extract.pattern : "",
@@ -1769,9 +1802,9 @@ var HelperSchema = z42.object({
1769
1802
  }
1770
1803
  }
1771
1804
  });
1772
- var DeliveredHelperSchema = z42.object({
1773
- id: z42.string().min(1),
1774
- name: z42.string(),
1805
+ var DeliveredHelperSchema = z5.object({
1806
+ id: z5.string().min(1),
1807
+ name: z5.string(),
1775
1808
  config: HelperConfigSchema
1776
1809
  }).strict();
1777
1810
  var LIBRARY_CASE_SCHEMA_VERSION = 1;
@@ -1779,83 +1812,83 @@ var TEST_TYPES = ["functional", "negative", "edge", "regression", "smoke", "acce
1779
1812
  var PRIORITIES = ["P0", "P1", "P2", "P3"];
1780
1813
  var CASE_STATUSES = ["draft", "ready", "deprecated"];
1781
1814
  var AUTOMATION_STATES = ["none", "planned", "automated", "not-automatable"];
1782
- var LibraryStepSchema = z5.object({
1783
- id: z5.string().min(1),
1784
- action: z5.string().min(1),
1815
+ var LibraryStepSchema = z6.object({
1816
+ id: z6.string().min(1),
1817
+ action: z6.string().min(1),
1785
1818
  /** What to type or choose, when the action needs it. */
1786
- data: z5.string().optional(),
1819
+ data: z6.string().optional(),
1787
1820
  /** What should happen. Absent for a step that only gets somewhere. */
1788
- expected: z5.string().optional()
1821
+ expected: z6.string().optional()
1789
1822
  }).strict();
1790
- var AutomationLinkSchema = z5.object({
1791
- automationId: z5.string().min(1),
1792
- caseRevision: z5.number().int().nonnegative(),
1823
+ var AutomationLinkSchema = z6.object({
1824
+ automationId: z6.string().min(1),
1825
+ caseRevision: z6.number().int().nonnegative(),
1793
1826
  /** The automation's revision when the link was made or last confirmed —
1794
1827
  * an automation edited since may no longer check what the case says. */
1795
- automationRevision: z5.number().int().nonnegative().optional(),
1796
- at: z5.string(),
1797
- by: z5.string().default("")
1828
+ automationRevision: z6.number().int().nonnegative().optional(),
1829
+ at: z6.string(),
1830
+ by: z6.string().default("")
1798
1831
  }).strict();
1799
- var CaseOriginSchema = z5.discriminatedUnion("kind", [
1800
- z5.object({ kind: z5.literal("manual") }).strict(),
1801
- z5.object({
1802
- kind: z5.literal("import"),
1832
+ var CaseOriginSchema = z6.discriminatedUnion("kind", [
1833
+ z6.object({ kind: z6.literal("manual") }).strict(),
1834
+ z6.object({
1835
+ kind: z6.literal("import"),
1803
1836
  /** "csv", "xlsx", "testrail", "zephyr", "xray", "text"… */
1804
- format: z5.string().min(1),
1805
- file: z5.string().min(1),
1837
+ format: z6.string().min(1),
1838
+ file: z6.string().min(1),
1806
1839
  /** Row or item in that file, to find it again. */
1807
- row: z5.string().optional()
1840
+ row: z6.string().optional()
1808
1841
  }).strict(),
1809
- z5.object({
1810
- kind: z5.literal("generated"),
1811
- sourceId: z5.string().min(1),
1812
- sourceVersion: z5.number().int().positive(),
1813
- requirementIds: z5.array(z5.string().min(1)).default([])
1842
+ z6.object({
1843
+ kind: z6.literal("generated"),
1844
+ sourceId: z6.string().min(1),
1845
+ sourceVersion: z6.number().int().positive(),
1846
+ requirementIds: z6.array(z6.string().min(1)).default([])
1814
1847
  }).strict()
1815
1848
  ]);
1816
- var TestDataSchema = z5.object({
1817
- name: z5.string().min(1),
1818
- value: z5.string().optional(),
1819
- secretRef: z5.string().regex(/^[a-z][a-z0-9_]*$/i).optional()
1849
+ var TestDataSchema = z6.object({
1850
+ name: z6.string().min(1),
1851
+ value: z6.string().optional(),
1852
+ secretRef: z6.string().regex(/^[a-z][a-z0-9_]*$/i).optional()
1820
1853
  }).strict();
1821
- var LibraryCaseSchema = z5.object({
1822
- schemaVersion: z5.literal(LIBRARY_CASE_SCHEMA_VERSION),
1823
- id: z5.string().regex(/^tcs_[A-Za-z0-9]+$/),
1854
+ var LibraryCaseSchema = z6.object({
1855
+ schemaVersion: z6.literal(LIBRARY_CASE_SCHEMA_VERSION),
1856
+ id: z6.string().regex(/^tcs_[A-Za-z0-9]+$/),
1824
1857
  /** "TC-142": what people say out loud. The id is what links point at. */
1825
- key: z5.string().regex(/^TC-[1-9][0-9]*$/),
1858
+ key: z6.string().regex(/^TC-[1-9][0-9]*$/),
1826
1859
  /** The same features flows are filed under (`f_<slug>`), so one feature
1827
1860
  * holds both what to test and what tests it. */
1828
- featureId: z5.string().min(1),
1829
- slug: z5.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1830
- title: z5.string().min(1).max(300),
1831
- description: z5.string().default(""),
1832
- type: z5.enum(TEST_TYPES).default("functional"),
1833
- priority: z5.enum(PRIORITIES).default("P2"),
1834
- status: z5.enum(CASE_STATUSES).default("draft"),
1835
- preconditions: z5.array(z5.string().min(1)).default([]),
1836
- steps: z5.array(LibraryStepSchema).default([]),
1837
- testData: z5.array(TestDataSchema).default([]),
1838
- tags: z5.array(z5.string().min(1)).default([]),
1861
+ featureId: z6.string().min(1),
1862
+ slug: z6.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1863
+ title: z6.string().min(1).max(300),
1864
+ description: z6.string().default(""),
1865
+ type: z6.enum(TEST_TYPES).default("functional"),
1866
+ priority: z6.enum(PRIORITIES).default("P2"),
1867
+ status: z6.enum(CASE_STATUSES).default("draft"),
1868
+ preconditions: z6.array(z6.string().min(1)).default([]),
1869
+ steps: z6.array(LibraryStepSchema).default([]),
1870
+ testData: z6.array(TestDataSchema).default([]),
1871
+ tags: z6.array(z6.string().min(1)).default([]),
1839
1872
  /** Requirement ids (Flowriki storage). The text is not copied here. */
1840
- covers: z5.array(z5.string().min(1)).default([]),
1841
- automation: z5.object({
1842
- state: z5.enum(AUTOMATION_STATES).default("none"),
1873
+ covers: z6.array(z6.string().min(1)).default([]),
1874
+ automation: z6.object({
1875
+ state: z6.enum(AUTOMATION_STATES).default("none"),
1843
1876
  /** Why it cannot be automated, when it cannot. */
1844
- reason: z5.string().optional(),
1877
+ reason: z6.string().optional(),
1845
1878
  /** The plan for automating it: flows to reuse, flows to record, checks to
1846
1879
  * switch on, accounts and helpers it needs, and what cannot be automated.
1847
1880
  * Written (usually by the assistant) and approved before anything is
1848
1881
  * recorded. */
1849
- plan: z5.string().max(2e4).optional(),
1850
- planApproved: z5.boolean().optional(),
1851
- links: z5.array(AutomationLinkSchema).default([])
1882
+ plan: z6.string().max(2e4).optional(),
1883
+ planApproved: z6.boolean().optional(),
1884
+ links: z6.array(AutomationLinkSchema).default([])
1852
1885
  }).strict().default({}),
1853
1886
  origin: CaseOriginSchema.default({ kind: "manual" }),
1854
- createdAt: z5.string(),
1855
- createdBy: z5.string().default(""),
1856
- updatedAt: z5.string(),
1857
- updatedBy: z5.string().default(""),
1858
- revision: z5.number().int().nonnegative().default(0)
1887
+ createdAt: z6.string(),
1888
+ createdBy: z6.string().default(""),
1889
+ updatedAt: z6.string(),
1890
+ updatedBy: z6.string().default(""),
1891
+ revision: z6.number().int().nonnegative().default(0)
1859
1892
  }).strict().superRefine((doc, ctx) => {
1860
1893
  const seen = /* @__PURE__ */ new Set();
1861
1894
  doc.steps.forEach((step, i) => {
@@ -1873,155 +1906,145 @@ var LibraryCaseSchema = z5.object({
1873
1906
  ctx.addIssue({ code: "custom", path: ["automation", "reason"], message: "Say why it cannot be automated" });
1874
1907
  }
1875
1908
  });
1876
- var normaliseText = (text) => text.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
1877
1909
  var SOURCE_KINDS = ["upload", "jira", "figma", "confluence", "linear", "github-issue", "notion", "url"];
1878
- var SourceVersionSchema = z5.object({
1879
- v: z5.number().int().positive(),
1880
- hash: z5.string().min(1),
1881
- extractedText: z5.string(),
1910
+ var SourceVersionSchema = z6.object({
1911
+ v: z6.number().int().positive(),
1912
+ hash: z6.string().min(1),
1913
+ extractedText: z6.string(),
1882
1914
  /** Where an uploaded file WAS kept. Only text is kept since 2026-10-11;
1883
1915
  * read so older records still parse, and cleared at start-up. */
1884
- originalKey: z5.string().optional(),
1885
- mediaType: z5.string().optional(),
1886
- at: z5.string(),
1887
- by: z5.string().default("")
1916
+ originalKey: z6.string().optional(),
1917
+ mediaType: z6.string().optional(),
1918
+ at: z6.string(),
1919
+ by: z6.string().default("")
1888
1920
  }).strict();
1889
- var SourceSchema = z5.object({
1890
- id: z5.string().regex(/^src_[A-Za-z0-9]+$/),
1891
- projectId: z5.string().min(1),
1892
- title: z5.string().min(1).max(300),
1893
- kind: z5.enum(SOURCE_KINDS),
1894
- versions: z5.array(SourceVersionSchema).min(1),
1895
- integrationId: z5.string().optional(),
1896
- externalId: z5.string().optional(),
1897
- externalUrl: z5.string().url().optional(),
1898
- createdAt: z5.string(),
1899
- createdBy: z5.string().default("")
1921
+ var SourceSchema = z6.object({
1922
+ id: z6.string().regex(/^src_[A-Za-z0-9]+$/),
1923
+ projectId: z6.string().min(1),
1924
+ title: z6.string().min(1).max(300),
1925
+ kind: z6.enum(SOURCE_KINDS),
1926
+ versions: z6.array(SourceVersionSchema).min(1),
1927
+ integrationId: z6.string().optional(),
1928
+ externalId: z6.string().optional(),
1929
+ externalUrl: z6.string().url().optional(),
1930
+ createdAt: z6.string(),
1931
+ createdBy: z6.string().default("")
1900
1932
  }).strict();
1901
- var RequirementSchema = z5.object({
1902
- id: z5.string().regex(/^req_[A-Za-z0-9]+$/),
1903
- projectId: z5.string().min(1),
1904
- sourceId: z5.string().min(1),
1905
- sourceVersion: z5.number().int().positive(),
1933
+ var RequirementSchema = z6.object({
1934
+ id: z6.string().regex(/^req_[A-Za-z0-9]+$/),
1935
+ projectId: z6.string().min(1),
1936
+ sourceId: z6.string().min(1),
1937
+ sourceVersion: z6.number().int().positive(),
1906
1938
  /** Where in the source: a heading path, a Jira key, a Figma node. */
1907
- anchor: z5.string().min(1),
1908
- text: z5.string().min(1),
1909
- featureId: z5.string().optional(),
1939
+ anchor: z6.string().min(1),
1940
+ text: z6.string().min(1),
1941
+ featureId: z6.string().optional(),
1910
1942
  /** As of the source's latest version: new in it, changed by it, unchanged
1911
1943
  * ("active"), or gone from it. */
1912
- state: z5.enum(["new", "active", "changed", "removed"]).default("new"),
1913
- history: z5.array(z5.object({
1914
- sourceVersion: z5.number().int().positive(),
1915
- text: z5.string(),
1916
- state: z5.enum(["new", "active", "changed", "removed"])
1944
+ state: z6.enum(["new", "active", "changed", "removed"]).default("new"),
1945
+ history: z6.array(z6.object({
1946
+ sourceVersion: z6.number().int().positive(),
1947
+ text: z6.string(),
1948
+ state: z6.enum(["new", "active", "changed", "removed"])
1917
1949
  }).strict()).default([])
1918
1950
  }).strict();
1919
1951
  var MANUAL_OUTCOMES = ["untested", "passed", "failed", "blocked", "skipped"];
1920
- var EvidenceSchema = z6.object({
1921
- id: z6.string().regex(/^ev_[A-Za-z0-9]+$/),
1952
+ var EvidenceSchema = z7.object({
1953
+ id: z7.string().regex(/^ev_[A-Za-z0-9]+$/),
1922
1954
  /** A file kept in Flowriki's evidence storage, or a link to one elsewhere. */
1923
- kind: z6.enum(["file", "link"]),
1924
- name: z6.string().min(1).max(200),
1925
- mediaType: z6.string().optional(),
1926
- size: z6.number().int().nonnegative().optional(),
1927
- url: z6.string().url().optional(),
1955
+ kind: z7.enum(["file", "link"]),
1956
+ name: z7.string().min(1).max(200),
1957
+ mediaType: z7.string().optional(),
1958
+ size: z7.number().int().nonnegative().optional(),
1959
+ url: z7.string().url().optional(),
1928
1960
  /** The step it shows, when it belongs to one. */
1929
- stepId: z6.string().optional(),
1930
- at: z6.string(),
1931
- by: z6.string().default("")
1961
+ stepId: z7.string().optional(),
1962
+ at: z7.string(),
1963
+ by: z7.string().default("")
1932
1964
  }).strict();
1933
- var StepOutcomeSchema = z6.object({
1934
- stepId: z6.string().min(1),
1935
- outcome: z6.enum(MANUAL_OUTCOMES),
1965
+ var StepOutcomeSchema = z7.object({
1966
+ stepId: z7.string().min(1),
1967
+ outcome: z7.enum(MANUAL_OUTCOMES),
1936
1968
  /** What actually happened, when it was not what was expected. */
1937
- actual: z6.string().max(4e3).optional()
1969
+ actual: z7.string().max(4e3).optional()
1938
1970
  }).strict();
1939
- var ManualResultSchema = z6.object({
1940
- outcome: z6.enum(MANUAL_OUTCOMES),
1941
- steps: z6.array(StepOutcomeSchema).default([]),
1942
- note: z6.string().max(8e3).default(""),
1943
- evidence: z6.array(EvidenceSchema).default([]),
1944
- bugIds: z6.array(z6.string().min(1)).default([]),
1945
- tester: z6.string().default(""),
1946
- at: z6.string()
1971
+ var ManualResultSchema = z7.object({
1972
+ outcome: z7.enum(MANUAL_OUTCOMES),
1973
+ steps: z7.array(StepOutcomeSchema).default([]),
1974
+ note: z7.string().max(8e3).default(""),
1975
+ evidence: z7.array(EvidenceSchema).default([]),
1976
+ bugIds: z7.array(z7.string().min(1)).default([]),
1977
+ tester: z7.string().default(""),
1978
+ at: z7.string()
1947
1979
  }).strict();
1948
- var CycleEntrySchema = z6.object({
1949
- testCaseId: z6.string().min(1),
1950
- key: z6.string().min(1),
1951
- title: z6.string().min(1),
1952
- featureId: z6.string().min(1),
1953
- priority: z6.enum(PRIORITIES),
1980
+ var CycleEntrySchema = z7.object({
1981
+ testCaseId: z7.string().min(1),
1982
+ key: z7.string().min(1),
1983
+ title: z7.string().min(1),
1984
+ featureId: z7.string().min(1),
1985
+ priority: z7.enum(PRIORITIES),
1954
1986
  /** The case revision snapshot, so "the case changed since" is answerable. */
1955
- caseRevision: z6.number().int().nonnegative(),
1956
- preconditions: z6.array(z6.string()).default([]),
1957
- steps: z6.array(z6.object({ id: z6.string(), action: z6.string(), data: z6.string().optional(), expected: z6.string().optional() }).strict()).default([]),
1958
- assignee: z6.string().optional(),
1987
+ caseRevision: z7.number().int().nonnegative(),
1988
+ preconditions: z7.array(z7.string()).default([]),
1989
+ steps: z7.array(z7.object({ id: z7.string(), action: z7.string(), data: z7.string().optional(), expected: z7.string().optional() }).strict()).default([]),
1990
+ assignee: z7.string().optional(),
1959
1991
  result: ManualResultSchema.nullable().default(null),
1960
1992
  /** Earlier results for this case in this cycle, newest last — a retest
1961
1993
  * keeps what the first attempt found. */
1962
- history: z6.array(ManualResultSchema).default([])
1994
+ history: z7.array(ManualResultSchema).default([])
1963
1995
  }).strict();
1964
- var TestCycleSchema = z6.object({
1965
- id: z6.string().regex(/^cyc_[A-Za-z0-9]+$/),
1966
- projectId: z6.string().min(1),
1967
- name: z6.string().min(1).max(200),
1968
- description: z6.string().max(4e3).default(""),
1969
- status: z6.enum(["open", "closed"]).default("open"),
1996
+ var TestCycleSchema = z7.object({
1997
+ id: z7.string().regex(/^cyc_[A-Za-z0-9]+$/),
1998
+ projectId: z7.string().min(1),
1999
+ name: z7.string().min(1).max(200),
2000
+ description: z7.string().max(4e3).default(""),
2001
+ status: z7.enum(["open", "closed"]).default("open"),
1970
2002
  /** Which branch the cases were read from; null for the published tests. */
1971
- changeId: z6.string().nullable().default(null),
1972
- entries: z6.array(CycleEntrySchema).default([]),
1973
- createdAt: z6.string(),
1974
- createdBy: z6.string().default(""),
1975
- updatedAt: z6.string(),
1976
- closedAt: z6.string().nullable().default(null)
2003
+ changeId: z7.string().nullable().default(null),
2004
+ entries: z7.array(CycleEntrySchema).default([]),
2005
+ createdAt: z7.string(),
2006
+ createdBy: z7.string().default(""),
2007
+ updatedAt: z7.string(),
2008
+ closedAt: z7.string().nullable().default(null)
1977
2009
  }).strict();
1978
2010
  var BUG_STATUSES = ["open", "in-progress", "fixed", "verified", "closed", "wont-fix"];
1979
2011
  var SEVERITIES = ["critical", "major", "minor", "trivial"];
1980
- var BugSchema = z6.object({
1981
- id: z6.string().regex(/^bug_[A-Za-z0-9]+$/),
1982
- key: z6.string().regex(/^BUG-[1-9][0-9]*$/),
1983
- projectId: z6.string().min(1),
1984
- title: z6.string().min(1).max(300),
1985
- description: z6.string().max(2e4).default(""),
1986
- severity: z6.enum(SEVERITIES).default("major"),
1987
- status: z6.enum(BUG_STATUSES).default("open"),
1988
- tracker: z6.enum(["flowriki", "jira", "linear", "github"]).default("flowriki"),
1989
- externalKey: z6.string().optional(),
1990
- url: z6.string().url().optional(),
2012
+ var BugSchema = z7.object({
2013
+ id: z7.string().regex(/^bug_[A-Za-z0-9]+$/),
2014
+ key: z7.string().regex(/^BUG-[1-9][0-9]*$/),
2015
+ projectId: z7.string().min(1),
2016
+ title: z7.string().min(1).max(300),
2017
+ description: z7.string().max(2e4).default(""),
2018
+ severity: z7.enum(SEVERITIES).default("major"),
2019
+ status: z7.enum(BUG_STATUSES).default("open"),
2020
+ tracker: z7.enum(["flowriki", "jira", "linear", "github"]).default("flowriki"),
2021
+ externalKey: z7.string().optional(),
2022
+ url: z7.string().url().optional(),
1991
2023
  /** Where it was found: a case, the step in it, the cycle that ran it. */
1992
- testCaseIds: z6.array(z6.string()).default([]),
1993
- cycleIds: z6.array(z6.string()).default([]),
1994
- runIds: z6.array(z6.string()).default([]),
1995
- automationIds: z6.array(z6.string()).default([]),
1996
- foundAt: z6.object({ cycleId: z6.string(), testCaseId: z6.string(), stepId: z6.string().optional() }).strict().optional(),
1997
- createdAt: z6.string(),
1998
- createdBy: z6.string().default(""),
1999
- updatedAt: z6.string()
2024
+ testCaseIds: z7.array(z7.string()).default([]),
2025
+ cycleIds: z7.array(z7.string()).default([]),
2026
+ runIds: z7.array(z7.string()).default([]),
2027
+ automationIds: z7.array(z7.string()).default([]),
2028
+ foundAt: z7.object({ cycleId: z7.string(), testCaseId: z7.string(), stepId: z7.string().optional() }).strict().optional(),
2029
+ createdAt: z7.string(),
2030
+ createdBy: z7.string().default(""),
2031
+ updatedAt: z7.string()
2000
2032
  }).strict();
2001
2033
  var STOP = new Set("a an the and or of to in on for with by is are be as at it this that from user users can should must shall will when then given".split(" "));
2002
- function similarity(a, b2) {
2003
- const words = (s2) => new Set(normaliseText(s2).split(" ").filter((w) => w.length > 1 && !STOP.has(w)));
2004
- const x = words(a);
2005
- const y = words(b2);
2006
- if (x.size === 0 || y.size === 0) return 0;
2007
- let shared = 0;
2008
- for (const w of x) if (y.has(w)) shared += 1;
2009
- return shared / (x.size + y.size - shared);
2010
- }
2011
- var ConversationSchema = z7.object({
2012
- id: z7.string().regex(/^conv_[A-Za-z0-9]+$/),
2013
- projectId: z7.string().min(1),
2034
+ var ConversationSchema = z8.object({
2035
+ id: z8.string().regex(/^conv_[A-Za-z0-9]+$/),
2036
+ projectId: z8.string().min(1),
2014
2037
  /** Whose: conversations are personal. */
2015
- userId: z7.string().min(1),
2016
- title: z7.string().min(1).max(200),
2017
- mode: z7.enum(["ask", "do"]).default("ask"),
2038
+ userId: z8.string().min(1),
2039
+ title: z8.string().min(1).max(200),
2040
+ mode: z8.enum(["ask", "do"]).default("ask"),
2018
2041
  /** The assistant's own session, to continue where it left off. */
2019
- sessionId: z7.string().optional(),
2042
+ sessionId: z8.string().optional(),
2020
2043
  /** The conversation as the dashboard draws it. Kept as given: the shape
2021
2044
  * belongs to the screen, and the service only stores and returns it. */
2022
- lines: z7.array(z7.unknown()).max(2e3).default([]),
2023
- createdAt: z7.string(),
2024
- updatedAt: z7.string()
2045
+ lines: z8.array(z8.unknown()).max(2e3).default([]),
2046
+ createdAt: z8.string(),
2047
+ updatedAt: z8.string()
2025
2048
  }).strict();
2026
2049
 
2027
2050
  // ../engine-playwright/dist/index.js
@@ -6947,8 +6970,8 @@ async function fieldNames(page) {
6947
6970
  }
6948
6971
 
6949
6972
  // ../results/dist/chunk-LYCTVHPR.js
6950
- import { z as z8 } from "zod";
6951
- var RunStatusSchema = z8.enum([
6973
+ import { z as z9 } from "zod";
6974
+ var RunStatusSchema = z9.enum([
6952
6975
  "queued",
6953
6976
  "running",
6954
6977
  "passed",
@@ -6956,7 +6979,7 @@ var RunStatusSchema = z8.enum([
6956
6979
  "cancelled",
6957
6980
  "errored"
6958
6981
  ]);
6959
- var TaskStatusSchema = z8.enum([
6982
+ var TaskStatusSchema = z9.enum([
6960
6983
  "queued",
6961
6984
  "running",
6962
6985
  "passed",
@@ -6965,7 +6988,7 @@ var TaskStatusSchema = z8.enum([
6965
6988
  "cancelled",
6966
6989
  "errored"
6967
6990
  ]);
6968
- var FailureKindSchema = z8.enum([
6991
+ var FailureKindSchema = z9.enum([
6969
6992
  "not_found",
6970
6993
  "ambiguous",
6971
6994
  "timeout",
@@ -6974,30 +6997,30 @@ var FailureKindSchema = z8.enum([
6974
6997
  "crash",
6975
6998
  "unknown"
6976
6999
  ]);
6977
- var RunCheckSchema = z8.object({
6978
- id: z8.string(),
6979
- assertionId: z8.string().nullable(),
6980
- kind: z8.string(),
6981
- expected: z8.string().nullable(),
6982
- actual: z8.string().nullable(),
6983
- ok: z8.boolean(),
7000
+ var RunCheckSchema = z9.object({
7001
+ id: z9.string(),
7002
+ assertionId: z9.string().nullable(),
7003
+ kind: z9.string(),
7004
+ expected: z9.string().nullable(),
7005
+ actual: z9.string().nullable(),
7006
+ ok: z9.boolean(),
6984
7007
  /** A failed soft check is reported but does not fail its step, so a run can
6985
7008
  * be green and still carry findings. Stored per check rather than derived,
6986
7009
  * because the flow it came from can be edited afterwards and history has to
6987
7010
  * keep saying what actually happened. */
6988
- soft: z8.boolean().default(false),
6989
- detail: z8.string().nullable()
7011
+ soft: z9.boolean().default(false),
7012
+ detail: z9.string().nullable()
6990
7013
  });
6991
- var RunStepSchema = z8.object({
6992
- id: z8.string(),
6993
- ordinal: z8.number().int().nonnegative(),
7014
+ var RunStepSchema = z9.object({
7015
+ id: z9.string(),
7016
+ ordinal: z9.number().int().nonnegative(),
6994
7017
  /** The STABLE document step id — this is what joins runs across time and
6995
7018
  * makes flake-rate a view over existing columns rather than a migration. */
6996
- stepId: z8.string(),
6997
- action: z8.string(),
6998
- targetRole: z8.string().nullable(),
6999
- targetName: z8.string().nullable(),
7000
- targetScope: z8.string().nullable(),
7019
+ stepId: z9.string(),
7020
+ action: z9.string(),
7021
+ targetRole: z9.string().nullable(),
7022
+ targetName: z9.string().nullable(),
7023
+ targetScope: z9.string().nullable(),
7001
7024
  /** What the step typed, chose or pressed.
7002
7025
  *
7003
7026
  * A report that says `fill "Password"` and nothing else cannot tell you
@@ -7006,100 +7029,100 @@ var RunStepSchema = z8.object({
7006
7029
  *
7007
7030
  * NEVER set for a secret — see `secret` below. A run report is the thing
7008
7031
  * people paste into a ticket. */
7009
- value: z8.string().nullable().default(null),
7032
+ value: z9.string().nullable().default(null),
7010
7033
  /** The step typed a secret, so `value` is deliberately absent rather than
7011
7034
  * merely unknown. Stored, because "no value" and "a value withheld" render
7012
7035
  * differently and history has to keep saying which it was. */
7013
- secret: z8.boolean().default(false),
7014
- status: z8.enum(["passed", "failed", "skipped"]),
7036
+ secret: z9.boolean().default(false),
7037
+ status: z9.enum(["passed", "failed", "skipped"]),
7015
7038
  failureKind: FailureKindSchema.nullable(),
7016
- detail: z8.string().nullable(),
7039
+ detail: z9.string().nullable(),
7017
7040
  /** WHICH WAY FOUND IT, when the first way did not.
7018
7041
  *
7019
7042
  * A step carries several routes to its element and takes the first that
7020
7043
  * matches. Passing on a later one is not a failure, but it does mean the
7021
7044
  * page has moved under the step — and a suite that repairs itself quietly
7022
7045
  * drifts away from the application it tests. Null is the ordinary case. */
7023
- foundBy: z8.string().nullable().default(null),
7024
- durationMs: z8.number().int().nonnegative().nullable(),
7046
+ foundBy: z9.string().nullable().default(null),
7047
+ durationMs: z9.number().int().nonnegative().nullable(),
7025
7048
  /** Where the browser was when this step ran. Recorded on failure, and
7026
7049
  * frequently the whole answer — the element is missing because a step
7027
7050
  * before it navigated somewhere unexpected, not because it was renamed. */
7028
- pageUrl: z8.string().nullable().default(null),
7029
- pageTitle: z8.string().nullable().default(null),
7030
- checks: z8.array(RunCheckSchema).default([])
7051
+ pageUrl: z9.string().nullable().default(null),
7052
+ pageTitle: z9.string().nullable().default(null),
7053
+ checks: z9.array(RunCheckSchema).default([])
7031
7054
  });
7032
- var RunTaskSchema = z8.object({
7033
- id: z8.string(),
7034
- ordinal: z8.number().int().nonnegative(),
7035
- caseName: z8.string(),
7036
- rowId: z8.string().nullable(),
7055
+ var RunTaskSchema = z9.object({
7056
+ id: z9.string(),
7057
+ ordinal: z9.number().int().nonnegative(),
7058
+ caseName: z9.string(),
7059
+ rowId: z9.string().nullable(),
7037
7060
  capability: CapabilitySchema,
7038
- provider: z8.string(),
7039
- attempt: z8.number().int().positive().default(1),
7040
- maxAttempts: z8.number().int().positive().default(1),
7061
+ provider: z9.string(),
7062
+ attempt: z9.number().int().positive().default(1),
7063
+ maxAttempts: z9.number().int().positive().default(1),
7041
7064
  status: TaskStatusSchema,
7042
- workerId: z8.string().nullable(),
7065
+ workerId: z9.string().nullable(),
7043
7066
  /** Cloud provider's own session, so a failure is one click from their video. */
7044
- sessionId: z8.string().nullable(),
7045
- sessionUrl: z8.string().nullable(),
7046
- detail: z8.string().nullable(),
7047
- stepsRun: z8.number().int().nonnegative().default(0),
7048
- stepsTotal: z8.number().int().nonnegative().default(0),
7049
- durationMs: z8.number().int().nonnegative().nullable(),
7050
- steps: z8.array(RunStepSchema).default([])
7067
+ sessionId: z9.string().nullable(),
7068
+ sessionUrl: z9.string().nullable(),
7069
+ detail: z9.string().nullable(),
7070
+ stepsRun: z9.number().int().nonnegative().default(0),
7071
+ stepsTotal: z9.number().int().nonnegative().default(0),
7072
+ durationMs: z9.number().int().nonnegative().nullable(),
7073
+ steps: z9.array(RunStepSchema).default([])
7051
7074
  });
7052
- var ArtifactKindSchema = z8.enum(["screenshot", "video", "trace", "console", "har"]);
7053
- var ArtifactSchema = z8.object({
7054
- id: z8.string(),
7055
- runId: z8.string(),
7056
- runStepId: z8.string().nullable(),
7075
+ var ArtifactKindSchema = z9.enum(["screenshot", "video", "trace", "console", "har"]);
7076
+ var ArtifactSchema = z9.object({
7077
+ id: z9.string(),
7078
+ runId: z9.string(),
7079
+ runStepId: z9.string().nullable(),
7057
7080
  kind: ArtifactKindSchema,
7058
- relPath: z8.string(),
7059
- bytes: z8.number().int().nonnegative().nullable(),
7060
- masked: z8.boolean().default(false),
7061
- createdAt: z8.string()
7081
+ relPath: z9.string(),
7082
+ bytes: z9.number().int().nonnegative().nullable(),
7083
+ masked: z9.boolean().default(false),
7084
+ createdAt: z9.string()
7062
7085
  });
7063
- var RunSummarySchema = z8.object({
7064
- id: z8.string(),
7065
- projectId: z8.string(),
7086
+ var RunSummarySchema = z9.object({
7087
+ id: z9.string(),
7088
+ projectId: z9.string(),
7066
7089
  /** What the run was FOR.
7067
7090
  *
7068
7091
  * `flake` is a deliberate repetition of one flow to measure how often it
7069
7092
  * passes. It is kept out of pass-rate statistics: a 98/100 flakiness check is
7070
7093
  * a useful finding, not a failing suite, and counting it as one would make
7071
7094
  * measuring reliability look like losing it. */
7072
- kind: z8.enum(["smoke", "test", "adhoc", "flake"]),
7073
- trigger: z8.enum(["ui", "cli", "ci"]),
7095
+ kind: z9.enum(["smoke", "test", "adhoc", "flake"]),
7096
+ trigger: z9.enum(["ui", "cli", "ci"]),
7074
7097
  status: RunStatusSchema,
7075
- flowId: z8.string().nullable(),
7076
- flowName: z8.string().nullable(),
7077
- gitSha: z8.string().nullable(),
7078
- gitBranch: z8.string().nullable(),
7079
- gitDirty: z8.boolean().default(false),
7080
- env: z8.string().nullable(),
7081
- concurrency: z8.number().int().positive().nullable(),
7082
- tasksTotal: z8.number().int().nonnegative().default(0),
7083
- tasksPassed: z8.number().int().nonnegative().default(0),
7084
- tasksFailed: z8.number().int().nonnegative().default(0),
7098
+ flowId: z9.string().nullable(),
7099
+ flowName: z9.string().nullable(),
7100
+ gitSha: z9.string().nullable(),
7101
+ gitBranch: z9.string().nullable(),
7102
+ gitDirty: z9.boolean().default(false),
7103
+ env: z9.string().nullable(),
7104
+ concurrency: z9.number().int().positive().nullable(),
7105
+ tasksTotal: z9.number().int().nonnegative().default(0),
7106
+ tasksPassed: z9.number().int().nonnegative().default(0),
7107
+ tasksFailed: z9.number().int().nonnegative().default(0),
7085
7108
  /** Rolled up from the run's tasks rather than stored on the run, so they
7086
7109
  * cannot drift from the step rows they describe. "9 of 12" in a list is
7087
7110
  * what tells a reader where a run died; "1 of 1 tasks" tells them nothing
7088
7111
  * until there is more than one browser in play. */
7089
- stepsRun: z8.number().int().nonnegative().default(0),
7090
- stepsTotal: z8.number().int().nonnegative().default(0),
7091
- queuedAt: z8.string(),
7092
- startedAt: z8.string().nullable(),
7093
- finishedAt: z8.string().nullable(),
7094
- durationMs: z8.number().int().nonnegative().nullable()
7112
+ stepsRun: z9.number().int().nonnegative().default(0),
7113
+ stepsTotal: z9.number().int().nonnegative().default(0),
7114
+ queuedAt: z9.string(),
7115
+ startedAt: z9.string().nullable(),
7116
+ finishedAt: z9.string().nullable(),
7117
+ durationMs: z9.number().int().nonnegative().nullable()
7095
7118
  });
7096
7119
  var RunDetailSchema = RunSummarySchema.extend({
7097
- errorMessage: z8.string().nullable(),
7098
- artifactsDir: z8.string().nullable(),
7099
- tasks: z8.array(RunTaskSchema).default([]),
7120
+ errorMessage: z9.string().nullable(),
7121
+ artifactsDir: z9.string().nullable(),
7122
+ tasks: z9.array(RunTaskSchema).default([]),
7100
7123
  /** Screenshots and the like, addressed by `runStepId`. Kept flat rather than
7101
7124
  * nested inside each step: an artifact can belong to the run as a whole. */
7102
- artifacts: z8.array(ArtifactSchema).default([]),
7125
+ artifacts: z9.array(ArtifactSchema).default([]),
7103
7126
  /** The document as it was executed — a flow, or a test case with the copies
7104
7127
  * it held that day.
7105
7128
  *
@@ -7108,7 +7131,7 @@ var RunDetailSchema = RunSummarySchema.extend({
7108
7131
  * edited, and without surfacing it the promise was only half kept: the bytes
7109
7132
  * were there and nothing could reach them. Null for runs recorded before a
7110
7133
  * snapshot was taken. */
7111
- flowSnapshot: z8.unknown().nullable().default(null)
7134
+ flowSnapshot: z9.unknown().nullable().default(null)
7112
7135
  });
7113
7136
 
7114
7137
  // ../results/dist/chunk-IW3PJWP6.js
@@ -11380,12 +11403,7 @@ var ASK_MODE_TOOLS = [
11380
11403
  "project_brief",
11381
11404
  "knowledge_search",
11382
11405
  "knowledge_read",
11383
- "sources",
11384
- "requirements",
11385
- "find_similar_cases",
11386
- "library_cases",
11387
- "test_cycles",
11388
- "bugs",
11406
+ "read_test_cases",
11389
11407
  "list_my_branches"
11390
11408
  ];
11391
11409
  function buildAskArgs(opts) {
@@ -11393,7 +11411,7 @@ function buildAskArgs(opts) {
11393
11411
  const folders = opts.folders ?? [];
11394
11412
  const guidance = [
11395
11413
  `You are the Flowriki assistant for the project "${opts.projectName}" (id ${opts.projectId}). Use the Flowriki tools, and pass projectId "${opts.projectId}" wherever a tool takes one. Start with project_brief: it is this project's memory.`,
11396
- ask ? "ASK MODE: answer the question; change nothing. Cite what you rely on as [[testCase:TC-12]], [[source:src_x@v2]], [[requirement:req_x]], [[knowledge:features/checkout.md]], [[automation:tc_x]], [[cycle:cyc_x]], [[bug:BUG-3]] or [[file:path/in/repo.ts]]." : "DO MODE: do what is asked, on Claude's working branch, for a person to review. Ask in the chat and wait before publishing, deleting, or sending anything to another system. End by writing a short summary of what you did with write_knowledge (memory/sessions/<date>-<topic>.md, append) so the next session knows.",
11414
+ ask ? "ASK MODE: answer the question; change nothing. Cite what you rely on as [[testCase:<its id, tc_\u2026>]], [[knowledge:features/checkout.md]] or [[file:path/in/repo.ts]]." : "DO MODE: do what is asked, on Claude's working branch, for a person to review. Ask in the chat and wait before publishing, deleting, or sending anything to another system. End by writing a short summary of what you did with write_knowledge (memory/sessions/<date>-<topic>.md, append) so the next session knows.",
11397
11415
  folders.length ? `The project's code is in ${folders.join(" and ")}. Read it with Read, Grep and Glob when it helps; you cannot change it.` : ""
11398
11416
  ].filter(Boolean).join("\n");
11399
11417
  const flowriki = ask ? ASK_MODE_TOOLS.map((t) => `mcp__${MCP_SERVER_NAME}__${t}`) : [`mcp__${MCP_SERVER_NAME}`];
@@ -12056,7 +12074,7 @@ export {
12056
12074
  connect,
12057
12075
  RecordedStepSchema,
12058
12076
  mirrorCheckTargets,
12059
- similarity,
12077
+ parseGherkin,
12060
12078
  runtimeValue,
12061
12079
  authenticationResolver,
12062
12080
  checkAssertion,