@jevitate/cli 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -31320,6 +31320,8 @@ var USAGE_ERROR_PATTERNS = [
31320
31320
  var USAGE_ERROR_CODES = /* @__PURE__ */ new Set([
31321
31321
  "E_CHECK_SUITE",
31322
31322
  // the suite (or its preflight) was refused
31323
+ "E_CHAIN_REQUEST",
31324
+ // #580: an approved-requests line is malformed or carries an unknown key — nothing ran
31323
31325
  "E_CAMPAIGN_PREFLIGHT",
31324
31326
  // #485: a campaign's preconditions failed (start URL, session, allow list) — no mission ran
31325
31327
  "E_AI_SETUP_REQUIRED",
@@ -35996,11 +35998,31 @@ async function waitForOutcome(actor, expect, wait, step) {
35996
35998
  }
35997
35999
 
35998
36000
  // ../interpreter/dist/run-step.js
36001
+ var SelectAlternativeUnavailableError = class extends Error {
36002
+ constructor() {
36003
+ super("no-alternative: the select has no enabled option other than the one this proof avoids");
36004
+ this.name = "SelectAlternativeUnavailableError";
36005
+ }
36006
+ };
35999
36007
  async function strictTarget(actor, d, opts) {
36000
36008
  const page = actor.ability(BrowseTheWebToken).session.page;
36001
36009
  const locator = await resolveTarget(page, d, opts);
36002
36010
  return Target.named(descriptorToTarget(d).description).locatedBy(() => locator);
36003
36011
  }
36012
+ async function selectAlternativeValue(locator, avoid) {
36013
+ const value = await locator.evaluate((el, avoided) => {
36014
+ const options = el.options ?? [];
36015
+ for (let i = 0; i < options.length; i += 1) {
36016
+ const option = options[i];
36017
+ if (!option.disabled && option.value !== "" && option.value !== avoided)
36018
+ return option.value;
36019
+ }
36020
+ return null;
36021
+ }, avoid);
36022
+ if (value === null)
36023
+ throw new SelectAlternativeUnavailableError();
36024
+ return value;
36025
+ }
36004
36026
  function resolveValue(value, vars) {
36005
36027
  if ("var" in value) {
36006
36028
  const v = vars.get(value.var);
@@ -36101,9 +36123,12 @@ async function runStep(actor, rec, vars, index = 0, targetOpts = {}) {
36101
36123
  return { kind: "done" };
36102
36124
  }
36103
36125
  case "select": {
36104
- const value = resolveValue(step.value, vars);
36126
+ const alternative = targetOpts.selectAlternative?.(index);
36127
+ const chosen = alternative === void 0 ? resolveValue(step.value, vars) : void 0;
36105
36128
  const page = actor.ability(BrowseTheWebToken).session.page;
36106
- await (await resolveTarget(page, step.target, targetOpts)).selectOption(value);
36129
+ const locator = await resolveTarget(page, step.target, targetOpts);
36130
+ const value = chosen ?? await selectAlternativeValue(locator, alternative.avoid);
36131
+ await locator.selectOption(value);
36107
36132
  await postcondition(actor, expected(step.expect), step.waitFor, "select", index, targetOpts);
36108
36133
  return { kind: "done" };
36109
36134
  }
@@ -36150,8 +36175,14 @@ async function runStep(actor, rec, vars, index = 0, targetOpts = {}) {
36150
36175
  vars.set(`${step.as}.__index`, String(i));
36151
36176
  for (const childStep of step.steps) {
36152
36177
  await runRowScopedChildStep(actor, childStep, rowLocator, vars, expected);
36178
+ if (childStep.kind === "extract") {
36179
+ const value = vars.get(childStep.as);
36180
+ if (value !== void 0)
36181
+ vars.set(`${step.as}.${i}.${childStep.as}`, value);
36182
+ }
36153
36183
  }
36154
36184
  }
36185
+ vars.set(`${step.as}.count`, String(count));
36155
36186
  return { kind: "done" };
36156
36187
  }
36157
36188
  case "handback": {
@@ -36266,6 +36297,7 @@ var RecordingInterpreter = class {
36266
36297
  ...this.options.targetTimeoutMs === void 0 ? {} : { timeoutMs: this.options.targetTimeoutMs },
36267
36298
  ...this.options.observer === void 0 ? {} : { observer: this.options.observer },
36268
36299
  ...this.options.skipStep === void 0 ? {} : { skipStep: this.options.skipStep },
36300
+ ...this.options.selectAlternative === void 0 ? {} : { selectAlternative: this.options.selectAlternative },
36269
36301
  ...this.options.fixtureBaseDir === void 0 ? {} : { fixtureBaseDir: this.options.fixtureBaseDir },
36270
36302
  ...this.options.refLookup === void 0 ? {} : { refLookup: this.options.refLookup }
36271
36303
  };
@@ -53506,7 +53538,7 @@ function assertionSiteKey(site) {
53506
53538
  return `step-request:${site.step}:${site.checkIndex}`;
53507
53539
  return `end-state:${site.index}`;
53508
53540
  }
53509
- var MUTATION_RE = /^(skip|block-write|stale-value|swap-param|truncate-param):(.+)$/;
53541
+ var MUTATION_RE = /^(skip|block-write|stale-value|swap-param|truncate-param|guard-param):(.+)$/;
53510
53542
  var SITE_RE = /^(?:step:[1-9][0-9]*|step-request:[1-9][0-9]*:(?:0|[1-9][0-9]*)|end-state:(?:0|[1-9][0-9]*))$/;
53511
53543
  function flat(journey) {
53512
53544
  return journey.recording.pages.flatMap((p) => p.steps);
@@ -53518,15 +53550,36 @@ function firstActionStep(journey, name) {
53518
53550
  const i = flat(journey).findIndex((r) => actionRefs(r.step).includes(name));
53519
53551
  return i < 0 ? null : i + 1;
53520
53552
  }
53553
+ function allActionReadsAreSelect(journey, name) {
53554
+ const reads2 = flat(journey).filter((r) => actionRefs(r.step).includes(name));
53555
+ return reads2.length > 0 && reads2.every((r) => r.step.kind === "select");
53556
+ }
53557
+ function reviewedValue(journey, name, supplied) {
53558
+ const declared = (journey.metadata.parameters ?? []).find((p) => p.name === name);
53559
+ return declared?.mutationValues?.find((v) => v !== supplied) ?? null;
53560
+ }
53561
+ function selectsVar(step, name) {
53562
+ return step.kind === "select" && "var" in step.value && step.value.var === name;
53563
+ }
53521
53564
  function paramMutation(kind, step, param) {
53522
53565
  return { kind, step, id: `${kind}:${param}`, param };
53523
53566
  }
53567
+ function guardSites(journey, sites, name) {
53568
+ const action = firstActionStep(journey, name);
53569
+ return sites.filter((s3) => s3.where === "step" && siteRefs(s3).includes(name) && (action === null || s3.step < action));
53570
+ }
53524
53571
  function paramMutationRef(journey, kind, name, value) {
53525
53572
  const declared = [...journey.metadata.params, ...(journey.metadata.parameters ?? []).map((p) => p.name)];
53526
53573
  if (!declared.includes(name))
53527
53574
  throw new Error(`${JSON.stringify(value)}: ${JSON.stringify(name)} is not a declared parameter`);
53528
53575
  if (secretParamNames(journey).includes(name))
53529
53576
  throw new Error(`${JSON.stringify(value)}: ${name} is a secret parameter \u2014 a secret is never mutated`);
53577
+ if (kind === "guard-param") {
53578
+ const first = guardSites(journey, journeyAssertions(journey), name)[0];
53579
+ if (first === void 0)
53580
+ throw new Error(`${JSON.stringify(value)}: no step assertion checks ${name} before an action reads it`);
53581
+ return paramMutation(kind, first.step, name);
53582
+ }
53530
53583
  const step = firstActionStep(journey, name);
53531
53584
  if (step === null)
53532
53585
  throw new Error(`${JSON.stringify(value)}: no step's action reads ${name} (a fill or a navigate URL)`);
@@ -53535,9 +53588,9 @@ function paramMutationRef(journey, kind, name, value) {
53535
53588
  function resolveMutationRef(journey, value) {
53536
53589
  const m = MUTATION_RE.exec(value.trim());
53537
53590
  if (m === null)
53538
- throw new Error(`${JSON.stringify(value)} is not a mutation \u2014 expected skip:<step>, block-write:<step> or stale-value:<step> (a step number or an anchor name), or swap-param:<param> / truncate-param:<param>`);
53591
+ throw new Error(`${JSON.stringify(value)} is not a mutation \u2014 expected skip:<step>, block-write:<step> or stale-value:<step> (a step number or an anchor name), or swap-param:<param> / truncate-param:<param> / guard-param:<param>`);
53539
53592
  const ref = m[2];
53540
- if (m[1] === "swap-param" || m[1] === "truncate-param")
53593
+ if (m[1] === "swap-param" || m[1] === "truncate-param" || m[1] === "guard-param")
53541
53594
  return paramMutationRef(journey, m[1], ref, value);
53542
53595
  let step;
53543
53596
  try {
@@ -53653,17 +53706,30 @@ function planJourneyMutations(journey, opts = {}) {
53653
53706
  const params = opts.params ?? {};
53654
53707
  const names = [...new Set(sites.flatMap(siteRefs))];
53655
53708
  for (const name of names) {
53709
+ const supplied = mutableParamValue(journey, name, params);
53710
+ const guards = guardSites(journey, sites, name);
53711
+ const guardValue = supplied === null ? null : reviewedValue(journey, name, supplied) ?? swappedValue(journey, name, params);
53712
+ if (guards.length > 0 && guardValue !== null) {
53713
+ const g = { ...paramMutation("guard-param", guards[0].step, name), source: reviewedValue(journey, name, supplied) === null ? "generated" : "reviewed" };
53714
+ mutations.push(g);
53715
+ for (const s3 of guards)
53716
+ pair(s3, g);
53717
+ }
53656
53718
  const at3 = firstActionStep(journey, name);
53657
53719
  if (at3 === null)
53658
53720
  continue;
53659
- const naming = sites.filter((s3) => siteRefs(s3).includes(name));
53660
- for (const [kind, value] of [
53721
+ const naming = sites.filter((s3) => siteRefs(s3).includes(name) && !guards.includes(s3));
53722
+ if (naming.length === 0)
53723
+ continue;
53724
+ const candidates = [
53661
53725
  ["swap-param", swappedValue(journey, name, params)],
53662
- ["truncate-param", truncatedValue(journey, name, params)]
53663
- ]) {
53726
+ ...allActionReadsAreSelect(journey, name) ? [] : [["truncate-param", truncatedValue(journey, name, params)]]
53727
+ ];
53728
+ const source = supplied !== null && reviewedValue(journey, name, supplied) !== null ? "reviewed" : allActionReadsAreSelect(journey, name) ? "option" : "generated";
53729
+ for (const [kind, value] of candidates) {
53664
53730
  if (value === null)
53665
53731
  continue;
53666
- const m = paramMutation(kind, at3, name);
53732
+ const m = kind === "swap-param" ? { ...paramMutation(kind, at3, name), source } : paramMutation(kind, at3, name);
53667
53733
  mutations.push(m);
53668
53734
  for (const s3 of naming)
53669
53735
  pair(s3, m);
@@ -53686,7 +53752,7 @@ function mutationWait(wait) {
53686
53752
  var MUTATION_WAIT_MARGIN_MS = 5e3;
53687
53753
  function mutationReplay(journey, m, opts = {}) {
53688
53754
  const index = m.step - 1;
53689
- if (m.kind === "swap-param" || m.kind === "truncate-param")
53755
+ if (m.kind === "swap-param" || m.kind === "truncate-param" || m.kind === "guard-param")
53690
53756
  return paramReplay(journey, m, opts.params ?? {});
53691
53757
  if (m.kind === "block-write") {
53692
53758
  const requests = journeyWriteSteps(journey, opts).find((w) => w.step === m.step)?.requests ?? [];
@@ -53719,6 +53785,25 @@ function mutationReplay(journey, m, opts = {}) {
53719
53785
  }
53720
53786
  function paramReplay(journey, m, params) {
53721
53787
  const name = m.param ?? "";
53788
+ const index = m.step - 1;
53789
+ const target = flat(journey)[index]?.step;
53790
+ if (m.kind === "guard-param") {
53791
+ const supplied2 = mutableParamValue(journey, name, params);
53792
+ const wrong = supplied2 === null ? null : reviewedValue(journey, name, supplied2) ?? swappedValue(journey, name, params);
53793
+ if (wrong === null)
53794
+ throw new Error(`${m.id}: parameter ${name} has no value this proof may alter (not supplied, blank, too short, or secret)`);
53795
+ return { journey, params: { ...params, [name]: wrong } };
53796
+ }
53797
+ const reviewedFrom = m.kind === "swap-param" ? mutableParamValue(journey, name, params) : null;
53798
+ const reviewed = reviewedFrom === null ? null : reviewedValue(journey, name, reviewedFrom);
53799
+ if (reviewedFrom !== null && reviewed !== null)
53800
+ return { journey: pinAssertionRefs(journey, name, reviewedFrom), params: { ...params, [name]: reviewed } };
53801
+ if (m.kind === "swap-param" && target !== void 0 && selectsVar(target, name)) {
53802
+ const supplied2 = mutableParamValue(journey, name, params);
53803
+ if (supplied2 === null)
53804
+ throw new Error(`${m.id}: parameter ${name} has no value this proof may alter (not supplied, blank, too short, or secret)`);
53805
+ return { journey: structuredClone(journey), swapSelectAtStep: { index, avoid: supplied2 } };
53806
+ }
53722
53807
  const supplied = mutableParamValue(journey, name, params);
53723
53808
  const altered = m.kind === "swap-param" ? swappedValue(journey, name, params) : truncatedValue(journey, name, params);
53724
53809
  if (supplied === null || altered === null)
@@ -53734,7 +53819,7 @@ function relaxedMutationReplay(journey, m, opts = {}) {
53734
53819
  const index = m.step - 1;
53735
53820
  if (m.kind === "skip")
53736
53821
  return { journey, skipIndex: index };
53737
- if (m.kind === "swap-param" || m.kind === "truncate-param") {
53822
+ if (m.kind === "swap-param" || m.kind === "truncate-param" || m.kind === "guard-param") {
53738
53823
  const how = paramReplay(journey, m, opts.params ?? {});
53739
53824
  const target2 = flat(how.journey)[index];
53740
53825
  if (target2 !== void 0 && "expect" in target2.step) {
@@ -53987,7 +54072,9 @@ var JourneySuccessCriterionSchema = external_exports.object({
53987
54072
  var JourneyParameterSchema = external_exports.object({
53988
54073
  name: external_exports.string().min(1).max(200),
53989
54074
  description: external_exports.string().max(2e3).optional(),
53990
- secret: external_exports.boolean().optional()
54075
+ secret: external_exports.boolean().optional(),
54076
+ // #579: reviewed, non-secret alternative values — at most 20 distinct 1..2000-char strings.
54077
+ mutationValues: external_exports.array(TEXT).min(1).max(20).refine((vs) => new Set(vs).size === vs.length, { message: "mutationValues: duplicate value" }).optional()
53991
54078
  }).strict();
53992
54079
  var CATALOG_REF_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
53993
54080
  var CatalogRefSchema = external_exports.string().regex(CATALOG_REF_RE, "a catalog id: 1-64 of [A-Za-z0-9._-], starting alphanumeric");
@@ -54072,6 +54159,17 @@ var JourneySchema = external_exports.object({
54072
54159
  }));
54073
54160
  for (const issue3 of assertionRefIssues(j))
54074
54161
  ctx.addIssue({ code: "custom", path: [...issue3.path], message: issue3.message });
54162
+ (j.metadata.parameters ?? []).forEach((p, i) => {
54163
+ if (p.mutationValues === void 0)
54164
+ return;
54165
+ if (p.secret === true || SECRET_PARAM_NAME_RE2.test(p.name)) {
54166
+ ctx.addIssue({
54167
+ code: "custom",
54168
+ path: ["metadata", "parameters", i, "mutationValues"],
54169
+ message: `E_MUTATION_VALUES_SECRET: parameter "${p.name}" is a secret (secret: true or a credential-like name) \u2014 mutationValues are reviewed, non-secret values and may never carry a credential`
54170
+ });
54171
+ }
54172
+ });
54075
54173
  });
54076
54174
  function ownAssertions(step) {
54077
54175
  if (step.kind === "assert")
@@ -54293,6 +54391,40 @@ function deriveParamSchema(rec) {
54293
54391
  }
54294
54392
  return { required: required2 };
54295
54393
  }
54394
+ function journeyParamSchema(journey) {
54395
+ const { required: required2 } = deriveParamSchema(journey.recording);
54396
+ const declared = /* @__PURE__ */ new Set([...journey.metadata.params, ...(journey.metadata.parameters ?? []).map((p) => p.name)]);
54397
+ const extracted = /* @__PURE__ */ new Set();
54398
+ const add = (a) => {
54399
+ for (const name of assertionRefs(a)) {
54400
+ if (declared.has(name) && !extracted.has(name) && !required2.includes(name))
54401
+ required2.push(name);
54402
+ }
54403
+ };
54404
+ for (const page of journey.recording.pages) {
54405
+ for (const { step } of page.steps) {
54406
+ for (const name of actionRefs(step))
54407
+ if (declared.has(name) && !extracted.has(name) && !required2.includes(name))
54408
+ required2.push(name);
54409
+ if (step.kind === "extract")
54410
+ extracted.add(step.as);
54411
+ for (const { assertion } of ownAssertions(step))
54412
+ add(assertion);
54413
+ if (step.kind === "forEach") {
54414
+ for (const child of step.steps) {
54415
+ if (child.kind === "extract")
54416
+ extracted.add(child.as);
54417
+ for (const { assertion } of ownAssertions(child))
54418
+ add(assertion);
54419
+ }
54420
+ }
54421
+ }
54422
+ }
54423
+ for (const c of journey.metadata.endState ?? [])
54424
+ if (c.kind === "page" || c.kind === "reloadThen")
54425
+ add(c.assertion);
54426
+ return { required: required2 };
54427
+ }
54296
54428
  function validateParams(schema, params) {
54297
54429
  const provided = Object.keys(params);
54298
54430
  const missing = schema.required.filter((v) => !(v in params));
@@ -54303,7 +54435,7 @@ function validateParams(schema, params) {
54303
54435
  }
54304
54436
  function journeyRunParams(journey) {
54305
54437
  const out = [...journey.metadata.params];
54306
- for (const p of deriveParamSchema(journey.recording).required)
54438
+ for (const p of journeyParamSchema(journey).required)
54307
54439
  if (!out.includes(p))
54308
54440
  out.push(p);
54309
54441
  return out;
@@ -61856,9 +61988,9 @@ function hostStarvedFailure(failure3, health) {
61856
61988
  }
61857
61989
 
61858
61990
  // src/build-info.generated.ts
61859
- var GENERATED_COMMIT = "2b37f8e";
61860
- var GENERATED_TREE = "c3b34ef";
61861
- var GENERATED_BUILT_AT = "2026-10-18T00:47:21.290Z";
61991
+ var GENERATED_COMMIT = "89237bd";
61992
+ var GENERATED_TREE = "b6b4c8d";
61993
+ var GENERATED_BUILT_AT = "2026-10-18T16:47:05.375Z";
61862
61994
 
61863
61995
  // src/version.ts
61864
61996
  import { readFileSync as readFileSync5 } from "node:fs";
@@ -61880,13 +62012,13 @@ function readCliVersion() {
61880
62012
 
61881
62013
  // src/engine.ts
61882
62014
  function bundledCommit() {
61883
- return true ? "2b37f8e" : void 0;
62015
+ return true ? "89237bd" : void 0;
61884
62016
  }
61885
62017
  function bundledBuiltAt() {
61886
- return true ? "2026-10-18T00:47:21.290Z" : void 0;
62018
+ return true ? "2026-10-18T16:47:05.375Z" : void 0;
61887
62019
  }
61888
62020
  function bundledTree() {
61889
- return true ? "c3b34ef" : void 0;
62021
+ return true ? "b6b4c8d" : void 0;
61890
62022
  }
61891
62023
  function currentBuildTree() {
61892
62024
  return bundledTree() ?? GENERATED_TREE;
@@ -80224,13 +80356,13 @@ import { readFile as readFile9, writeFile as writeFile8, mkdir as mkdir8, rm as
80224
80356
  import { homedir as realHomedir } from "node:os";
80225
80357
  import { join as join19, dirname as dirname6 } from "node:path";
80226
80358
  function detectRuntimes(deps = {}) {
80227
- const existsSync46 = deps.existsSync ?? realExistsSync;
80359
+ const existsSync47 = deps.existsSync ?? realExistsSync;
80228
80360
  const homedir6 = deps.homedir ?? realHomedir;
80229
80361
  const cwd = deps.cwd ?? process.cwd;
80230
80362
  const detected = [];
80231
- if (existsSync46(join19(homedir6(), ".claude"))) detected.push("claude-code");
80232
- if (existsSync46(join19(homedir6(), ".codex"))) detected.push("codex");
80233
- if (existsSync46(join19(cwd(), ".cursor"))) detected.push("cursor");
80363
+ if (existsSync47(join19(homedir6(), ".claude"))) detected.push("claude-code");
80364
+ if (existsSync47(join19(homedir6(), ".codex"))) detected.push("codex");
80365
+ if (existsSync47(join19(cwd(), ".cursor"))) detected.push("cursor");
80234
80366
  detected.push("generic");
80235
80367
  return detected;
80236
80368
  }
@@ -82119,7 +82251,7 @@ var JourneyRunner = class {
82119
82251
  }
82120
82252
  async #run(req, collect9) {
82121
82253
  assertCompletePolicy(req?.policy);
82122
- validateParams(deriveParamSchema(req.journey.recording), req.params);
82254
+ validateParams(journeyParamSchema(req.journey), req.params);
82123
82255
  if (req.policy.secret.secretMode === "vault-autofill") {
82124
82256
  await this.preflightSecretRefs(req.journey.metadata.secretRefs ?? []);
82125
82257
  }
@@ -90742,7 +90874,7 @@ async function resolveJourneyPrefix(opts) {
90742
90874
  }
90743
90875
  const prefix = journeyPrefix(full, resolved.step);
90744
90876
  const used = prefixParams(full, prefix, opts.params);
90745
- validateParams(deriveParamSchema(prefix.recording), used);
90877
+ validateParams(journeyParamSchema(prefix), used);
90746
90878
  const branch = journeyBranchPoint(full, resolved);
90747
90879
  const site = full.recording.site;
90748
90880
  let startUrl;
@@ -94050,10 +94182,10 @@ function redactSecretValues(result, secrets) {
94050
94182
  return scrub3(result);
94051
94183
  }
94052
94184
  function prefixParams(full, prefix, params) {
94053
- const known = deriveParamSchema(full.recording).required;
94185
+ const known = journeyParamSchema(full).required;
94054
94186
  const unknown2 = Object.keys(params).filter((k) => !known.includes(k));
94055
94187
  if (unknown2.length > 0) throw new ParamValidationError(`param mismatch \u2014 missing: [], unknown: [${unknown2.join(", ")}]`);
94056
- const needed = deriveParamSchema(prefix.recording).required;
94188
+ const needed = journeyParamSchema(prefix).required;
94057
94189
  return Object.fromEntries(Object.entries(params).filter(([k]) => needed.includes(k)));
94058
94190
  }
94059
94191
  function journeyFixtureBaseDir(deps = {}) {
@@ -94211,7 +94343,7 @@ async function runJourneyProgrammatically(opts, mutation2 = {}) {
94211
94343
  );
94212
94344
  }
94213
94345
  const inputParams = journey === full ? opts.params : prefixParams(full, journey, opts.params);
94214
- validateParams(deriveParamSchema(journey.recording), inputParams);
94346
+ validateParams(journeyParamSchema(journey), inputParams);
94215
94347
  const replayed = mutation2.mutateJourney === void 0 || journey !== full ? journey : mutation2.mutateJourney(journey);
94216
94348
  const policy = opts.policy ?? safeRunPolicy();
94217
94349
  const gate2 = await gateJourney(opts.siteGate, journey.recording, { ...opts.account === void 0 ? {} : { account: opts.account }, enforceLimits: true });
@@ -94282,8 +94414,15 @@ async function runJourneyProgrammatically(opts, mutation2 = {}) {
94282
94414
  shots === void 0 ? void 0 : screenshotObserver(shots, (a) => a.ability(BrowseTheWebToken).session.page, whatOf)
94283
94415
  );
94284
94416
  const skipStep = mutation2.skipStep;
94417
+ const selectAlternative = mutation2.selectAlternativeAtStep;
94285
94418
  const fixtureBaseDir = opts.fixtureBaseDir ?? journeyFixtureBaseDir();
94286
- const interpreter = opts.interpreter ?? (opts.observer === void 0 && shots === void 0 && replayDeltas === void 0 && outcomeChecks.observer() === void 0 && blocker === void 0 && healGuard === void 0 && skipStep === void 0 ? new RecordingInterpreter({ fixtureBaseDir, refLookup: refs }) : new RecordingInterpreter({ observer, fixtureBaseDir, refLookup: refs, ...skipStep === void 0 ? {} : { skipStep: (i) => skipStep(i) } }));
94419
+ const interpreter = opts.interpreter ?? (opts.observer === void 0 && shots === void 0 && replayDeltas === void 0 && outcomeChecks.observer() === void 0 && blocker === void 0 && healGuard === void 0 && skipStep === void 0 && selectAlternative === void 0 ? new RecordingInterpreter({ fixtureBaseDir, refLookup: refs }) : new RecordingInterpreter({
94420
+ observer,
94421
+ fixtureBaseDir,
94422
+ refLookup: refs,
94423
+ ...skipStep === void 0 ? {} : { skipStep: (i) => skipStep(i) },
94424
+ ...selectAlternative === void 0 ? {} : { selectAlternative: (i) => i === selectAlternative.index ? { avoid: selectAlternative.avoid } : void 0 }
94425
+ }));
94287
94426
  const heal = opts.heal === void 0 || healGuard === void 0 ? void 0 : { scope: opts.heal.scope, riskOf: journeyStepRisk(), writeGuard: healGuard.guard, allowedOrigins, observe: healObserver(healDir, mask) };
94288
94427
  const runner = new JourneyRunner(actor, interpreter, void 0, void 0, opts.selfHealer, heal);
94289
94428
  let result;
@@ -107473,6 +107612,249 @@ function registerJourneyRun(journey, program2, deps) {
107473
107612
  }));
107474
107613
  }
107475
107614
 
107615
+ // src/journey-cli/chain.ts
107616
+ import { existsSync as existsSync27, readFileSync as readFileSync33 } from "node:fs";
107617
+
107618
+ // src/journey-chain.ts
107619
+ var JourneyChainError = class extends Error {
107620
+ code;
107621
+ constructor(code, message2) {
107622
+ super(message2);
107623
+ this.name = "JourneyChainError";
107624
+ this.code = code;
107625
+ }
107626
+ };
107627
+ var E_CHAIN_FIELD = "E_CHAIN_FIELD";
107628
+ var REQUEST_KEYS = ["commentId", "account", "replyText"];
107629
+ function parseChainRequests(text2) {
107630
+ const requests = [];
107631
+ const lines = text2.split(/\r?\n/);
107632
+ for (let n3 = 0; n3 < lines.length; n3++) {
107633
+ const line2 = lines[n3]?.trim() ?? "";
107634
+ if (line2 === "") continue;
107635
+ let parsed;
107636
+ try {
107637
+ parsed = JSON.parse(line2);
107638
+ } catch {
107639
+ throw new JourneyChainError("E_CHAIN_REQUEST", `request line ${n3 + 1} is not valid JSON`);
107640
+ }
107641
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
107642
+ throw new JourneyChainError("E_CHAIN_REQUEST", `request line ${n3 + 1} must be a JSON object`);
107643
+ }
107644
+ const bag = parsed;
107645
+ const keys = Object.keys(bag);
107646
+ const missing = REQUEST_KEYS.filter((k) => !keys.includes(k));
107647
+ const extra = keys.filter((k) => !REQUEST_KEYS.includes(k));
107648
+ if (missing.length > 0 || extra.length > 0) {
107649
+ throw new JourneyChainError("E_CHAIN_REQUEST", `request line ${n3 + 1} \u2014 missing: [${missing.join(", ")}], unknown: [${extra.join(", ")}]`);
107650
+ }
107651
+ for (const k of REQUEST_KEYS) {
107652
+ if (typeof bag[k] !== "string") {
107653
+ throw new JourneyChainError("E_CHAIN_REQUEST", `request line ${n3 + 1} \u2014 ${k} must be a string`);
107654
+ }
107655
+ }
107656
+ requests.push({ commentId: bag.commentId, account: bag.account, replyText: bag.replyText });
107657
+ }
107658
+ return requests;
107659
+ }
107660
+ var recordKey2 = (commentId, account) => `${commentId}\0${account}`;
107661
+ var asString = (v) => typeof v === "string" && v !== "" ? v : void 0;
107662
+ function readRecords(output3) {
107663
+ const bag = output3 !== null && typeof output3 === "object" ? output3 : {};
107664
+ const raw = bag["records.count"];
107665
+ const count = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
107666
+ if (!Number.isInteger(count) || count <= 0) {
107667
+ throw new JourneyChainError("E_CHAIN_EMPTY", `the discover Journey returned no records (records.count = ${raw === void 0 ? "missing" : JSON.stringify(raw)})`);
107668
+ }
107669
+ const field3 = (i, name) => asString(bag[`records.${i}.${name}`]);
107670
+ const records = Array.from({ length: count }, (_, i) => ({
107671
+ commentId: field3(i, "commentId"),
107672
+ account: field3(i, "account"),
107673
+ threadId: field3(i, "threadId"),
107674
+ sourceText: field3(i, "sourceText"),
107675
+ parentId: field3(i, "parentId")
107676
+ }));
107677
+ return { count, records };
107678
+ }
107679
+ function chainParams(record2, request, declared) {
107680
+ const sources = {
107681
+ commentId: record2.commentId,
107682
+ replyIdentity: record2.account,
107683
+ threadId: record2.threadId,
107684
+ expected_commentid: record2.commentId,
107685
+ expected_account: record2.account,
107686
+ expected_sourcetext: record2.sourceText,
107687
+ expected_parentid: record2.parentId
107688
+ };
107689
+ const params = { replyText: request.replyText };
107690
+ for (const [name, value] of Object.entries(sources)) {
107691
+ if (!declared.has(name)) continue;
107692
+ if (value === void 0) return { ok: false, detail: `E_CHAIN_FIELD: the record has no value for ${name}` };
107693
+ params[name] = value;
107694
+ }
107695
+ return { ok: true, params };
107696
+ }
107697
+ var TEXT_PARAMS = ["replyText", "expected_sourcetext"];
107698
+ function redactReason(reason, params) {
107699
+ let out = reason;
107700
+ for (const name of TEXT_PARAMS) {
107701
+ const value = params[name];
107702
+ if (value === void 0 || value === "") continue;
107703
+ for (const form of /* @__PURE__ */ new Set([value, JSON.stringify(value).slice(1, -1)])) out = out.split(form).join(`<param ${name}>`);
107704
+ }
107705
+ return out;
107706
+ }
107707
+ function chainSucceeded(result) {
107708
+ return result.unmatched.length === 0 && result.runs.every((run) => run.outcome === "ok");
107709
+ }
107710
+ function sharedRunOptions(opts) {
107711
+ return {
107712
+ dir: opts.dir,
107713
+ ...opts.environment === void 0 ? {} : { environment: opts.environment },
107714
+ ...opts.storageState === void 0 ? {} : { storageState: opts.storageState },
107715
+ ...opts.browser === void 0 ? {} : { browser: opts.browser },
107716
+ ...opts.emulation === void 0 ? {} : { emulation: opts.emulation },
107717
+ ...opts.browserPortFactory === void 0 ? {} : { browserPortFactory: opts.browserPortFactory },
107718
+ ...opts.allow === void 0 ? {} : { allow: opts.allow },
107719
+ ...opts.siteGate === void 0 ? {} : { siteGate: opts.siteGate }
107720
+ };
107721
+ }
107722
+ async function runJourneyChain(opts, deps = {}) {
107723
+ const requests = parseChainRequests(opts.requests);
107724
+ const runner = deps.runJourney ?? runJourneyProgrammatically;
107725
+ const shared3 = sharedRunOptions(opts);
107726
+ const registry3 = new JourneyRegistry(new FsJourneyStore(opts.dir));
107727
+ const toJourney = await registry3.get(opts.to);
107728
+ if (toJourney === null) throw new UnknownJourneyError(`unknown journey '${opts.to}'`);
107729
+ if (opts.requirePromoted === true) {
107730
+ const fromJourney = await registry3.get(opts.from);
107731
+ if (fromJourney === null) throw new UnknownJourneyError(`unknown journey '${opts.from}'`);
107732
+ for (const j of [fromJourney, toJourney]) {
107733
+ if (!j.metadata.promoted) throw new JourneyChainError("E_CHAIN_NOT_PROMOTED", `journey '${j.metadata.id}' is not promoted \u2014 a chain runs promoted Journeys (journey chain --allow-draft runs drafts on the CLI)`);
107734
+ }
107735
+ }
107736
+ const fromResult = await runner({ ...shared3, id: opts.from, params: {} });
107737
+ if (fromResult.outcome !== "ok") {
107738
+ const reason = "reason" in fromResult ? fromResult.reason : void 0;
107739
+ throw new JourneyChainError(
107740
+ "E_CHAIN_FROM_FAILED",
107741
+ `discover Journey '${opts.from}' did not run ok (${fromResult.outcome})${reason === void 0 ? "" : `: ${reason}`}`
107742
+ );
107743
+ }
107744
+ const { count, records } = readRecords(fromResult.output);
107745
+ const declared = new Set(journeyParamSchema(toJourney).required);
107746
+ const byKey = /* @__PURE__ */ new Map();
107747
+ for (const record2 of records) {
107748
+ if (record2.commentId !== void 0 && record2.account !== void 0) byKey.set(recordKey2(record2.commentId, record2.account), record2);
107749
+ }
107750
+ const runs = [];
107751
+ const unmatched = [];
107752
+ for (const request of requests) {
107753
+ const record2 = byKey.get(recordKey2(request.commentId, request.account));
107754
+ if (record2 === void 0) {
107755
+ unmatched.push({ commentId: request.commentId, account: request.account });
107756
+ continue;
107757
+ }
107758
+ const mapped = chainParams(record2, request, declared);
107759
+ if (!mapped.ok) {
107760
+ runs.push({ commentId: request.commentId, account: request.account, outcome: E_CHAIN_FIELD, reason: mapped.detail });
107761
+ continue;
107762
+ }
107763
+ try {
107764
+ const result = await runner({ ...shared3, id: opts.to, params: mapped.params });
107765
+ runs.push({
107766
+ commentId: request.commentId,
107767
+ account: request.account,
107768
+ outcome: result.outcome,
107769
+ ..."reason" in result ? { reason: redactReason(result.reason, mapped.params) } : {}
107770
+ });
107771
+ } catch (err) {
107772
+ runs.push({ commentId: request.commentId, account: request.account, outcome: "error", reason: redactReason(err instanceof Error ? err.message : String(err), mapped.params) });
107773
+ }
107774
+ }
107775
+ return { from: { journey: opts.from, outcome: "ok", records: count }, runs, unmatched };
107776
+ }
107777
+
107778
+ // src/journey-cli/chain.ts
107779
+ function registerJourneyChain(journey, program2, deps) {
107780
+ withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withEmulationFlags(journey.command("chain"))))).requiredOption("--from <id>", "the discover Journey, run once to produce the records").requiredOption("--to <id>", "the prepare-only reply Journey, run once per matched request").requiredOption("--requests <file>", "JSON Lines file of approved requests ({commentId, account, replyText})").option("--dir <path>", "journeys directory (default: the repo's .jevitate/journeys; outside a repo ~/.jevitate/journeys)").option("--storage-state <file>", "Playwright storageState JSON to start each run authenticated; must exist").option("--allow-draft", "also run Journeys that are not promoted (authoring); without it both must be promoted").option("--json", "emit a JSON envelope").action(async function() {
107781
+ const { from, to, requests: requestsFile, dir, storageState: storageStateFlag, json: json2, allowDraft } = this.opts();
107782
+ const { env: envName, baseUrl } = this.opts();
107783
+ let environment;
107784
+ let allow;
107785
+ try {
107786
+ allow = replayAllowFromFlags(this.opts().allow);
107787
+ environment = environmentFromFlags(
107788
+ { ...envName === void 0 ? {} : { env: envName }, ...baseUrl === void 0 ? {} : { baseUrl } },
107789
+ environmentSeams(deps)
107790
+ );
107791
+ } catch (err) {
107792
+ if (!isEnvironmentError(err)) throw err;
107793
+ emitJson(program2, fail(err.code, err.message));
107794
+ return;
107795
+ }
107796
+ const storageState = storageStateFlag ?? environment?.storageState;
107797
+ if (storageState !== void 0 && !existsSync27(storageState)) {
107798
+ emitJson(program2, fail("E_CHAIN_ARGS", `storage state not found: ${storageState}`));
107799
+ return;
107800
+ }
107801
+ let requests;
107802
+ try {
107803
+ requests = readFileSync33(requestsFile, "utf8");
107804
+ } catch (err) {
107805
+ emitJson(program2, fail("E_CHAIN_ARGS", `could not read requests file ${requestsFile}: ${err instanceof Error ? err.message : String(err)}`));
107806
+ return;
107807
+ }
107808
+ let browser;
107809
+ try {
107810
+ browser = browserRunFromFlags(this.opts(), deps.explore?.env ?? process.env);
107811
+ } catch (err) {
107812
+ emitJson(program2, fail("E_CHAIN_ARGS", err instanceof Error ? err.message : String(err)));
107813
+ return;
107814
+ }
107815
+ let emulation;
107816
+ try {
107817
+ emulation = emulationFromFlags(this.opts());
107818
+ } catch (err) {
107819
+ emitJson(program2, fail("E_CHAIN_ARGS", err instanceof Error ? err.message : String(err)));
107820
+ return;
107821
+ }
107822
+ try {
107823
+ const result = await withSiteGate(
107824
+ resolveDbPath(deps),
107825
+ (siteGate) => runJourneyChain({
107826
+ from,
107827
+ to,
107828
+ requests,
107829
+ dir: resolveJourneysDir(deps, dir),
107830
+ requirePromoted: allowDraft !== true,
107831
+ ...siteGate === void 0 ? {} : { siteGate },
107832
+ browserPortFactory: deps.explore?.browserPortFactory,
107833
+ ...browser === void 0 ? {} : { browser },
107834
+ ...emulation === void 0 ? {} : { emulation },
107835
+ ...storageState !== void 0 ? { storageState } : {},
107836
+ ...environment === void 0 ? {} : { environment },
107837
+ ...allow.length === 0 ? {} : { allow }
107838
+ })
107839
+ );
107840
+ const envelope = ok(result);
107841
+ if (json2) emitJson(program2, envelope);
107842
+ else writeRawResult(program2, result);
107843
+ process.exitCode = chainSucceeded(result) ? 0 : 1;
107844
+ } catch (err) {
107845
+ if (err instanceof JourneyChainError) {
107846
+ emitJson(program2, fail(err.code, err.message));
107847
+ } else if (err instanceof UnknownJourneyError) {
107848
+ emitJson(program2, fail("E_UNKNOWN_JOURNEY", String(err.message)));
107849
+ } else if (isEnvironmentError(err)) {
107850
+ emitJson(program2, fail(err.code, err.message));
107851
+ } else {
107852
+ emitJson(program2, fail("E_CHAIN", String(err instanceof Error ? err.message : err)));
107853
+ }
107854
+ }
107855
+ });
107856
+ }
107857
+
107476
107858
  // src/journey-cli/promote.ts
107477
107859
  import { readFile as readFile31 } from "node:fs/promises";
107478
107860
 
@@ -108021,7 +108403,7 @@ function registerJourneyReview(journey, program2, deps) {
108021
108403
  }
108022
108404
 
108023
108405
  // src/journey-cli/migrate.ts
108024
- import { existsSync as existsSync27 } from "node:fs";
108406
+ import { existsSync as existsSync28 } from "node:fs";
108025
108407
 
108026
108408
  // src/journey-migrate-api.ts
108027
108409
  import { readdir as readdir11, readFile as readFile32, writeFile as writeFile37 } from "node:fs/promises";
@@ -108277,7 +108659,7 @@ function registerJourneyMigrate(journey, program2, deps) {
108277
108659
  return;
108278
108660
  }
108279
108661
  const storageState = storageStateFlag ?? environment?.storageState;
108280
- if (storageState !== void 0 && !existsSync27(storageState)) {
108662
+ if (storageState !== void 0 && !existsSync28(storageState)) {
108281
108663
  emitJson(program2, fail("E_JOURNEY_MIGRATE_ARGS", `storage state not found: ${storageState}`));
108282
108664
  return;
108283
108665
  }
@@ -108610,7 +108992,7 @@ function registerJourneyLint(journey, program2, deps) {
108610
108992
  }
108611
108993
 
108612
108994
  // src/journey-cli/verify.ts
108613
- import { existsSync as existsSync28 } from "node:fs";
108995
+ import { existsSync as existsSync29 } from "node:fs";
108614
108996
 
108615
108997
  // src/journey-verify.ts
108616
108998
  function verifyExitCode(verdict) {
@@ -108660,10 +109042,14 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
108660
109042
  structuredFailures: true,
108661
109043
  ...how.journey === journey ? {} : { mutateJourney: (j) => describe6(j, m, replayOpts2).journey },
108662
109044
  ...how.skipIndex === void 0 ? {} : { skipStep: (i) => i === how.skipIndex },
109045
+ ...how.swapSelectAtStep === void 0 ? {} : { selectAlternativeAtStep: how.swapSelectAtStep },
108663
109046
  ...how.blockIndex === void 0 ? {} : { blockWritesAtStep: how.blockIndex }
108664
109047
  });
108665
109048
  const blocked = r.blockedWrites ?? [];
108666
109049
  const blockedFields = how.blockIndex === void 0 ? {} : { blockedWrites: blocked.map((b2) => `${b2.method} ${b2.path}`) };
109050
+ if (r.outcome === "quarantined" && /no-alternative/.test(r.reason)) {
109051
+ return { outcome: "not-applied", failedSites: [], ...blockedFields, reason: r.reason };
109052
+ }
108667
109053
  if (how.blockIndex !== void 0 && !blockedOwnWrite(how.blockRequests ?? [], blocked)) {
108668
109054
  return {
108669
109055
  outcome: "not-applied",
@@ -108685,7 +109071,15 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
108685
109071
  for (const m of plan2.mutations) {
108686
109072
  const first = await replay(m, mutationReplay);
108687
109073
  const relaxed2 = needsRelaxedReplay(plan2, m, first) ? await replay(m, relaxedMutationReplay) : void 0;
108688
- mutations.push({ kind: m.kind, step: m.step, id: m.id, ...m.param === void 0 ? {} : { param: m.param }, ...first, ...relaxed2 === void 0 ? {} : { relaxed: relaxed2 } });
109074
+ mutations.push({
109075
+ kind: m.kind,
109076
+ step: m.step,
109077
+ id: m.id,
109078
+ ...m.param === void 0 ? {} : { param: m.param },
109079
+ ...m.source === void 0 ? {} : { source: m.source },
109080
+ ...first,
109081
+ ...relaxed2 === void 0 ? {} : { relaxed: relaxed2 }
109082
+ });
108689
109083
  }
108690
109084
  }
108691
109085
  const results = new Map(
@@ -108753,7 +109147,7 @@ function registerJourneyVerify(journey, program2, deps) {
108753
109147
  ...ownFixtureFlags.after === void 0 && environment?.hooks?.after !== void 0 ? { after: environment.hooks.after } : {}
108754
109148
  };
108755
109149
  const storageState = storageStateFlag ?? environment?.storageState;
108756
- if (storageState !== void 0 && !existsSync28(storageState)) {
109150
+ if (storageState !== void 0 && !existsSync29(storageState)) {
108757
109151
  emitJson(program2, fail("E_JOURNEY_VERIFY_ARGS", `storage state not found: ${storageState}`));
108758
109152
  return;
108759
109153
  }
@@ -109180,7 +109574,7 @@ function registerJourneyLinkCommand(journey, program2, deps) {
109180
109574
  }
109181
109575
 
109182
109576
  // src/journey-annotate-cli.ts
109183
- import { existsSync as existsSync29 } from "node:fs";
109577
+ import { existsSync as existsSync30 } from "node:fs";
109184
109578
  function registerJourneyAnnotateCommand(journey, program2, deps) {
109185
109579
  withScreenshotsFlag(withEnvironmentFlags(withBrowserLaunchFlags(withEmulationFlags(withFixtureFlags(journey.command("annotate <id>")))))).description(
109186
109580
  "draft each step's objective/expected result (and the goal/success criteria when missing) by replaying the Journey; writes a reviewable draft, never the Journey \u2014 `--approve` applies a reviewed draft (human gate)"
@@ -109229,7 +109623,7 @@ function registerJourneyAnnotateCommand(journey, program2, deps) {
109229
109623
  ...ownFixtureFlags.after === void 0 && environment?.hooks?.after !== void 0 ? { after: environment.hooks.after } : {}
109230
109624
  };
109231
109625
  const storageState = storageStateFlag ?? environment?.storageState;
109232
- if (storageState !== void 0 && !existsSync29(storageState)) {
109626
+ if (storageState !== void 0 && !existsSync30(storageState)) {
109233
109627
  emitJson(program2, fail("E_JOURNEY_ANNOTATE_ARGS", `storage state not found: ${storageState}`));
109234
109628
  return;
109235
109629
  }
@@ -109317,7 +109711,7 @@ review or edit it, then apply it: jevitate journey annotate ${id} --approve
109317
109711
  }
109318
109712
 
109319
109713
  // src/journey-demo-cli.ts
109320
- import { existsSync as existsSync30 } from "node:fs";
109714
+ import { existsSync as existsSync31 } from "node:fs";
109321
109715
  import { join as join64, resolve as resolvePath15 } from "node:path";
109322
109716
  function registerJourneyDemoCommand(journey, program2, deps) {
109323
109717
  withScreenshotsFlag(withEnvironmentFlags(withDemoFlags(withBrowserLaunchFlags(withEmulationFlags(withFixtureFlags(journey.command("demo <id>"))))))).description(
@@ -109341,7 +109735,7 @@ function registerJourneyDemoCommand(journey, program2, deps) {
109341
109735
  ...ownFixtureFlags.after === void 0 && environment?.hooks?.after !== void 0 ? { after: environment.hooks.after } : {}
109342
109736
  };
109343
109737
  const storageState = storageStateFlag ?? environment?.storageState;
109344
- if (storageState !== void 0 && !existsSync30(storageState)) {
109738
+ if (storageState !== void 0 && !existsSync31(storageState)) {
109345
109739
  emitJson(program2, fail("E_JOURNEY_DEMO_ARGS", `storage state not found: ${storageState}`));
109346
109740
  return;
109347
109741
  }
@@ -109437,6 +109831,7 @@ function registerJourneyCommands(program2, deps) {
109437
109831
  registerJourneyList(journey, program2, deps);
109438
109832
  registerJourneyFind(journey, program2, deps);
109439
109833
  registerJourneyRun(journey, program2, deps);
109834
+ registerJourneyChain(journey, program2, deps);
109440
109835
  registerJourneyPromote(journey, program2, deps);
109441
109836
  registerJourneyReview(journey, program2, deps);
109442
109837
  registerJourneyMigrate(journey, program2, deps);
@@ -109461,7 +109856,7 @@ function registerBuiltinAnalyzers() {
109461
109856
  }
109462
109857
 
109463
109858
  // src/source-cli.ts
109464
- import { existsSync as existsSync31 } from "node:fs";
109859
+ import { existsSync as existsSync32 } from "node:fs";
109465
109860
 
109466
109861
  // src/source-run-api.ts
109467
109862
  var realResolvedJourneyRunner = async (file2, params, policy, storageState, emulation, browser, siteGate) => {
@@ -109470,7 +109865,7 @@ var realResolvedJourneyRunner = async (file2, params, policy, storageState, emul
109470
109865
  `journey '${file2.metadata.id}' requires auth (metadata.requiresAuth) \u2014 run with --storage-state <file>`
109471
109866
  );
109472
109867
  }
109473
- validateParams(deriveParamSchema(file2.recording), params);
109868
+ validateParams(journeyParamSchema(file2), params);
109474
109869
  const allowedOrigins = [.../* @__PURE__ */ new Set([file2.recording.site, ...file2.declaredOrigins])];
109475
109870
  const gate2 = await gateJourney(siteGate, file2.recording, { enforceLimits: true });
109476
109871
  const port = new PlaywrightBrowserPort();
@@ -109679,7 +110074,7 @@ function registerSourceCommands(program2, deps) {
109679
110074
  "Playwright storageState JSON to start the session authenticated (#118: required when the journey declares metadata.requiresAuth); must exist"
109680
110075
  ).option("--json", "emit a JSON envelope").option(TAG_FLAG, TAG_HELP, collectTag, []).action(taggedAction(program2, "source run", async function(name, journeyId) {
109681
110076
  const { param, storageState, json: json2, ...emulationFlags } = this.opts();
109682
- if (storageState !== void 0 && !existsSync31(storageState)) {
110077
+ if (storageState !== void 0 && !existsSync32(storageState)) {
109683
110078
  emitJson(program2, fail("E_SOURCE_RUN_ARGS", `storage state not found: ${storageState}`));
109684
110079
  return;
109685
110080
  }
@@ -109742,7 +110137,7 @@ function registerSourceCommands(program2, deps) {
109742
110137
  }
109743
110138
 
109744
110139
  // src/load-cli.ts
109745
- import { existsSync as existsSync32 } from "node:fs";
110140
+ import { existsSync as existsSync33 } from "node:fs";
109746
110141
 
109747
110142
  // ../load/dist/percentiles.js
109748
110143
  function percentile(sorted2, p) {
@@ -109870,7 +110265,7 @@ async function runJourneyLoadTest(opts) {
109870
110265
  `journey '${opts.id}' requires auth (metadata.requiresAuth) \u2014 run with --storage-state <file>`
109871
110266
  );
109872
110267
  }
109873
- validateParams(deriveParamSchema(journey.recording), opts.params);
110268
+ validateParams(journeyParamSchema(journey), opts.params);
109874
110269
  const policy = opts.policy ?? safeRunPolicy();
109875
110270
  const browserPortFactory = opts.browserPortFactory ?? (() => new PlaywrightBrowserPort());
109876
110271
  const fixtureBaseDir = journeyFixtureBaseDir();
@@ -109950,7 +110345,7 @@ function registerLoadCommands(program2, deps) {
109950
110345
  return;
109951
110346
  }
109952
110347
  const storageState = storageStateFlag ?? environment?.storageState;
109953
- if (storageState !== void 0 && !existsSync32(storageState)) {
110348
+ if (storageState !== void 0 && !existsSync33(storageState)) {
109954
110349
  emitJson(program2, fail("E_LOAD_RUN_ARGS", `storage state not found: ${storageState}`));
109955
110350
  return;
109956
110351
  }
@@ -110000,10 +110395,10 @@ function registerLoadCommands(program2, deps) {
110000
110395
  }
110001
110396
 
110002
110397
  // src/explore-cli/run.ts
110003
- import { existsSync as existsSync34 } from "node:fs";
110398
+ import { existsSync as existsSync35 } from "node:fs";
110004
110399
 
110005
110400
  // src/type-fixture-file.ts
110006
- import { readFileSync as readFileSync33, statSync as statSync10 } from "node:fs";
110401
+ import { readFileSync as readFileSync34, statSync as statSync10 } from "node:fs";
110007
110402
  import { basename as basename15, resolve as resolve18 } from "node:path";
110008
110403
  var TYPE_FIXTURE_MAX_BYTES = 256 * 1024;
110009
110404
  function loadTypeFixtures(specs, cwd = process.cwd()) {
@@ -110022,7 +110417,7 @@ function loadTypeFixtures(specs, cwd = process.cwd()) {
110022
110417
  if (size > TYPE_FIXTURE_MAX_BYTES) {
110023
110418
  throw new TypeFixtureSpecError(`--type-fixture ${descriptor}: ${path2} is ${size} bytes (at most ${TYPE_FIXTURE_MAX_BYTES})`);
110024
110419
  }
110025
- const bytes = readFileSync33(abs);
110420
+ const bytes = readFileSync34(abs);
110026
110421
  let text2;
110027
110422
  try {
110028
110423
  text2 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
@@ -110343,7 +110738,7 @@ async function runExploreMultiRun(args) {
110343
110738
  import { resolve as resolvePath16 } from "node:path";
110344
110739
 
110345
110740
  // src/campaign-api.ts
110346
- import { existsSync as existsSync33, readFileSync as readFileSync34 } from "node:fs";
110741
+ import { existsSync as existsSync34, readFileSync as readFileSync35 } from "node:fs";
110347
110742
  import { mkdir as mkdir31, writeFile as writeFile39 } from "node:fs/promises";
110348
110743
  import { homedir as homedir5 } from "node:os";
110349
110744
  import { dirname as dirname28, isAbsolute as isAbsolute11, join as join66, resolve as resolve19 } from "node:path";
@@ -110460,7 +110855,7 @@ var at2 = (path2) => path2.length === 0 ? "spec" : path2.map((p) => typeof p ===
110460
110855
  async function validateCampaign(specPath, opts) {
110461
110856
  let raw;
110462
110857
  try {
110463
- raw = JSON.parse(readFileSync34(specPath, "utf8"));
110858
+ raw = JSON.parse(readFileSync35(specPath, "utf8"));
110464
110859
  } catch (e) {
110465
110860
  throw new CampaignSpecError([`cannot read ${specPath}: ${e instanceof SyntaxError ? "not valid JSON" : e instanceof Error ? e.message : String(e)}`]);
110466
110861
  }
@@ -110484,8 +110879,8 @@ async function validateCampaignSpec(raw, specPath, opts) {
110484
110879
  problems.push(`env: ${err.message}`);
110485
110880
  }
110486
110881
  const defaultSession = spec.storageState === void 0 ? environment?.storageState : abs(spec.storageState);
110487
- if (spec.storageState !== void 0 && !existsSync33(abs(spec.storageState))) problems.push(`storageState: not found: ${abs(spec.storageState)}`);
110488
- if (spec.fixtures !== void 0 && !existsSync33(abs(spec.fixtures))) problems.push(`fixtures: not found: ${abs(spec.fixtures)}`);
110882
+ if (spec.storageState !== void 0 && !existsSync34(abs(spec.storageState))) problems.push(`storageState: not found: ${abs(spec.storageState)}`);
110883
+ if (spec.fixtures !== void 0 && !existsSync34(abs(spec.fixtures))) problems.push(`fixtures: not found: ${abs(spec.fixtures)}`);
110489
110884
  if ((spec.before !== void 0 || spec.after !== void 0) && opts.allowShellHooks !== true) {
110490
110885
  problems.push("before/after: operator shell hooks run only with --allow-shell-hooks");
110491
110886
  }
@@ -110510,7 +110905,7 @@ async function validateCampaignSpec(raw, specPath, opts) {
110510
110905
  }
110511
110906
  if (new Set(job.strategies).size !== job.strategies.length) problems.push(`${where}.strategies: duplicate strategy`);
110512
110907
  const session2 = job.storageState === void 0 ? defaultSession : abs(job.storageState);
110513
- if (job.storageState !== void 0 && !existsSync33(abs(job.storageState))) problems.push(`${where}.storageState: not found: ${abs(job.storageState)}`);
110908
+ if (job.storageState !== void 0 && !existsSync34(abs(job.storageState))) problems.push(`${where}.storageState: not found: ${abs(job.storageState)}`);
110514
110909
  let stored = null;
110515
110910
  try {
110516
110911
  stored = await registry3.get(job.journey);
@@ -110555,7 +110950,7 @@ async function validateCampaignSpec(raw, specPath, opts) {
110555
110950
  if (deepest > 0) {
110556
110951
  try {
110557
110952
  const prefix = journeyPrefix(full, deepest);
110558
- validateParams(deriveParamSchema(prefix.recording), prefixParams(full, prefix, job.params ?? {}));
110953
+ validateParams(journeyParamSchema(prefix), prefixParams(full, prefix, job.params ?? {}));
110559
110954
  } catch (err) {
110560
110955
  problems.push(`${where}.params: ${err instanceof Error ? err.message : String(err)}`);
110561
110956
  }
@@ -112278,7 +112673,7 @@ function registerExploreRun(program2, deps, buildProgram2) {
112278
112673
  emitExplore(fail(err.code, err.message));
112279
112674
  return;
112280
112675
  }
112281
- if (o.storageState !== void 0 && !existsSync34(o.storageState) && (authCheck.mode === "off" || projectPersona?.login === void 0)) {
112676
+ if (o.storageState !== void 0 && !existsSync35(o.storageState) && (authCheck.mode === "off" || projectPersona?.login === void 0)) {
112282
112677
  emitExplore(fail("E_EXPLORE_ARGS", `storage state not found: ${o.storageState}`));
112283
112678
  return;
112284
112679
  }
@@ -112570,11 +112965,11 @@ function pick3(k, v) {
112570
112965
  import { resolve as resolve22 } from "node:path";
112571
112966
 
112572
112967
  // src/sweep-api.ts
112573
- import { existsSync as existsSync36, mkdirSync as mkdirSync11, readFileSync as readFileSync35, realpathSync as realpathSync5, statSync as statSync11, writeFileSync as writeFileSync13 } from "node:fs";
112968
+ import { existsSync as existsSync37, mkdirSync as mkdirSync11, readFileSync as readFileSync36, realpathSync as realpathSync5, statSync as statSync11, writeFileSync as writeFileSync13 } from "node:fs";
112574
112969
  import { basename as basename18, dirname as dirname30, extname as extname2, join as join68, resolve as resolve21 } from "node:path";
112575
112970
 
112576
112971
  // src/mcp-paths.ts
112577
- import { existsSync as existsSync35, realpathSync as realpathSync4 } from "node:fs";
112972
+ import { existsSync as existsSync36, realpathSync as realpathSync4 } from "node:fs";
112578
112973
  import { basename as basename17, dirname as dirname29, isAbsolute as isAbsolute12, join as join67, relative as relative9, resolve as resolve20, sep as sep9 } from "node:path";
112579
112974
  var McpPathError = class extends Error {
112580
112975
  error = "invalid_args";
@@ -112592,7 +112987,7 @@ function realish(p) {
112592
112987
  let head2 = p;
112593
112988
  const tail = [];
112594
112989
  for (; ; ) {
112595
- if (existsSync35(head2)) {
112990
+ if (existsSync36(head2)) {
112596
112991
  try {
112597
112992
  return join67(realpathSync4(head2), ...tail);
112598
112993
  } catch {
@@ -112968,6 +113363,29 @@ var CLI_TOOL_SPECS = [
112968
113363
  omitted: { "--dir": OMIT.storeDir, "--allow": OMIT.mcpOrigins, ...HOOK_FLAGS, ...BROWSER_FLAGS, ...JSON_FLAG }
112969
113364
  }
112970
113365
  },
113366
+ {
113367
+ name: "chain_journeys",
113368
+ description: "`jevitate journey chain --from <discover> --to <reply> --requests <file>` (#580): run a discover Journey once, then a prepare-only reply Journey once per approved request in the JSON Lines file ({commentId, account, replyText}) matched to a discovered record by commentId AND account. Both Journeys must be promoted. Unmatched requests never run; an empty discovery stops the chain (no target is guessed). The result names ids and outcomes only, never record or reply text. Exit 0 only when every request matched and its run was ok.",
113369
+ command: {
113370
+ path: "journey chain",
113371
+ params: {
113372
+ from: s2("--from", { required: true }),
113373
+ to: s2("--to", { required: true }),
113374
+ requests: path("--requests", { required: true }),
113375
+ storageState: session("--storage-state"),
113376
+ ...EXTENSION,
113377
+ ...EMULATION,
113378
+ ...ENVIRONMENT
113379
+ },
113380
+ omitted: {
113381
+ "--allow-draft": "#580: over MCP a chain runs promoted Journeys only, as run_journey does; running drafts is the operator's CLI call",
113382
+ "--dir": OMIT.storeDir,
113383
+ "--allow": OMIT.mcpOrigins,
113384
+ ...BROWSER_FLAGS,
113385
+ ...JSON_FLAG
113386
+ }
113387
+ }
113388
+ },
112971
113389
  {
112972
113390
  name: "publish_journey",
112973
113391
  description: "`jevitate journey publish <id> --to <source>`: publish a promoted Journey into a registered source on a new publish/<id> branch (a PR when gh is available). Secret references only; declared origins must cover its steps.",
@@ -113994,7 +114412,7 @@ function personaOf(value, base, roots, where, problems) {
113994
114412
  }
113995
114413
  try {
113996
114414
  const confined = confineMcpPath(abs, `persona of ${where}`, roots, { session: true });
113997
- if (!existsSync36(confined)) {
114415
+ if (!existsSync37(confined)) {
113998
114416
  problems.push(`${where}: persona storage state not found: ${confined}`);
113999
114417
  return void 0;
114000
114418
  }
@@ -114063,8 +114481,8 @@ function validateRunShaping(options, strategy, where, problems) {
114063
114481
  }
114064
114482
  function loadSweepTargets(path2, opts = {}) {
114065
114483
  const abs = resolve21(path2);
114066
- if (!existsSync36(abs)) throw new SweepSpecError(`targets file not found: ${abs}`);
114067
- const text2 = readFileSync35(abs, "utf8");
114484
+ if (!existsSync37(abs)) throw new SweepSpecError(`targets file not found: ${abs}`);
114485
+ const text2 = readFileSync36(abs, "utf8");
114068
114486
  const isTsv = /\.(tsv|txt)$/i.test(abs);
114069
114487
  const parsed = isTsv ? parseTsv(text2, path2) : parseJson(text2, path2);
114070
114488
  const problems = [...parsed.problems];
@@ -114311,9 +114729,9 @@ function targetArgv(plan2, target, runDir) {
114311
114729
  ];
114312
114730
  }
114313
114731
  function finishedEnvelope(envelopePath) {
114314
- if (!existsSync36(envelopePath)) return void 0;
114732
+ if (!existsSync37(envelopePath)) return void 0;
114315
114733
  try {
114316
- const env = JSON.parse(readFileSync35(envelopePath, "utf8"));
114734
+ const env = JSON.parse(readFileSync36(envelopePath, "utf8"));
114317
114735
  if (!isRecord(env) || env.ok !== true || !isRecord(env.data)) return void 0;
114318
114736
  if (env.data.interrupted !== void 0) return void 0;
114319
114737
  return env;
@@ -114687,7 +115105,7 @@ function registerVerifyFixCommands(program2, deps) {
114687
115105
  }
114688
115106
 
114689
115107
  // src/author-journey-cli.ts
114690
- import { existsSync as existsSync37 } from "node:fs";
115108
+ import { existsSync as existsSync38 } from "node:fs";
114691
115109
  var AUTHOR_GOAL_RUN_FLAGS = [
114692
115110
  "successWhen",
114693
115111
  "allowVacuousChecks",
@@ -114783,7 +115201,7 @@ function registerAuthorJourneyCommands(program2, deps) {
114783
115201
  ].join("\n")
114784
115202
  ).action(async function() {
114785
115203
  const o = this.opts();
114786
- if (o.storageState !== void 0 && !existsSync37(o.storageState)) {
115204
+ if (o.storageState !== void 0 && !existsSync38(o.storageState)) {
114787
115205
  emitJson(program2, fail("E_EXPLORE_ARGS", `storage state not found: ${o.storageState}`));
114788
115206
  return;
114789
115207
  }
@@ -114905,11 +115323,11 @@ function registerAuthorJourneyCommands(program2, deps) {
114905
115323
  }
114906
115324
 
114907
115325
  // src/demo-aspect-cli.ts
114908
- import { existsSync as existsSync39 } from "node:fs";
115326
+ import { existsSync as existsSync40 } from "node:fs";
114909
115327
  import { join as join71, resolve as resolvePath18 } from "node:path";
114910
115328
 
114911
115329
  // src/demo-aspect-api.ts
114912
- import { existsSync as existsSync38 } from "node:fs";
115330
+ import { existsSync as existsSync39 } from "node:fs";
114913
115331
  import { mkdir as mkdir33, mkdtemp as mkdtemp5, readFile as readFile33, rm as rm13, writeFile as writeFile41 } from "node:fs/promises";
114914
115332
  import { tmpdir as tmpdir5 } from "node:os";
114915
115333
  import { dirname as dirname31, join as join70 } from "node:path";
@@ -115246,7 +115664,7 @@ async function demoAspect(opts) {
115246
115664
  const store = new FsJourneyStore(opts.journeysDir);
115247
115665
  if (await store.get(id) !== null) throw new DemoExistsError(`journey '${id}' already exists \u2014 pick another --id (nothing was changed)`);
115248
115666
  const recordPath = demoDraftPath(opts.journeysDir, id);
115249
- if (existsSync38(recordPath)) {
115667
+ if (existsSync39(recordPath)) {
115250
115668
  throw new DemoExistsError(`a demo draft '${id}' is waiting for approval (${recordPath}) \u2014 approve it with \`jevitate demo approve ${id}\`, or pick another --id`);
115251
115669
  }
115252
115670
  const maxReplays = opts.maxReplays ?? DEMO_MINIMIZE_MAX_REPLAYS;
@@ -115493,7 +115911,7 @@ function replayOptionsFrom(cmd, deps, o, environment) {
115493
115911
  fixtureIdentity: o.fixtureIdentity
115494
115912
  };
115495
115913
  const storageState = o.storageState ?? environment.storageState;
115496
- if (storageState !== void 0 && !existsSync39(storageState)) throw new DemoAspectArgsError(`storage state not found: ${storageState}`);
115914
+ if (storageState !== void 0 && !existsSync40(storageState)) throw new DemoAspectArgsError(`storage state not found: ${storageState}`);
115497
115915
  let browser;
115498
115916
  let emulation;
115499
115917
  try {
@@ -115846,12 +116264,12 @@ function registerRecordCommands(program2, deps) {
115846
116264
  }
115847
116265
 
115848
116266
  // src/regression-cli.ts
115849
- import { existsSync as existsSync41 } from "node:fs";
116267
+ import { existsSync as existsSync42 } from "node:fs";
115850
116268
  import { readFile as readFile35, writeFile as writeFile43 } from "node:fs/promises";
115851
116269
  import { join as join73 } from "node:path";
115852
116270
 
115853
116271
  // src/regression-api.ts
115854
- import { existsSync as existsSync40 } from "node:fs";
116272
+ import { existsSync as existsSync41 } from "node:fs";
115855
116273
  import { readFile as readFile34 } from "node:fs/promises";
115856
116274
  var RegressionExistsError = class extends Error {
115857
116275
  code = "E_REGRESSION_EXISTS";
@@ -115939,7 +116357,7 @@ function describeNoOracle(missionResult) {
115939
116357
  }
115940
116358
  async function runRegressionCapture(opts) {
115941
116359
  const recordingPath = safeChildPath(opts.regressionsDir, opts.id, { what: "regression id", suffix: ".recording.json" });
115942
- if (opts.force !== true && existsSync40(recordingPath)) {
116360
+ if (opts.force !== true && existsSync41(recordingPath)) {
115943
116361
  throw new RegressionExistsError(opts.id, opts.regressionsDir);
115944
116362
  }
115945
116363
  const raw = JSON.parse(await readFile34(opts.failingRecordingPath, "utf8"));
@@ -116181,12 +116599,12 @@ function registerRegressionCommands(program2, deps) {
116181
116599
  const { from, id, dir, attempts, summary: summary2, result: resultPath, fingerprint: fingerprint2, storageState, force, json: json2, param } = flags;
116182
116600
  if (refuseUnsafeName(program2, id, "regression id")) return;
116183
116601
  for (const [flag, path2] of [["--from", from], ["--result", resultPath]]) {
116184
- if (path2 !== void 0 && !existsSync41(path2)) {
116602
+ if (path2 !== void 0 && !existsSync42(path2)) {
116185
116603
  emitJson(program2, fail("E_REGRESSION_ARGS", `${flag} file not found: ${path2}`));
116186
116604
  return;
116187
116605
  }
116188
116606
  }
116189
- if (storageState !== void 0 && !existsSync41(storageState)) {
116607
+ if (storageState !== void 0 && !existsSync42(storageState)) {
116190
116608
  emitJson(program2, fail("E_REGRESSION_ARGS", `storage state not found: ${storageState}`));
116191
116609
  return;
116192
116610
  }
@@ -116283,7 +116701,7 @@ function registerRegressionCommands(program2, deps) {
116283
116701
  return;
116284
116702
  }
116285
116703
  const storageState = storageStateFlag ?? environment?.storageState;
116286
- if (storageState !== void 0 && !existsSync41(storageState)) {
116704
+ if (storageState !== void 0 && !existsSync42(storageState)) {
116287
116705
  emitJson(program2, fail("E_REGRESSION_ARGS", `storage state not found: ${storageState}`));
116288
116706
  return;
116289
116707
  }
@@ -116296,7 +116714,7 @@ function registerRegressionCommands(program2, deps) {
116296
116714
  }
116297
116715
  if (refuseUnsafeName(program2, id, "regression id")) return;
116298
116716
  const regressionsDir = resolveRegressionsDir(dir);
116299
- if (!existsSync41(join73(regressionsDir, `${id}.recording.json`))) {
116717
+ if (!existsSync42(join73(regressionsDir, `${id}.recording.json`))) {
116300
116718
  const notFound = new RegressionNotFoundError(id, regressionsDir);
116301
116719
  emitJson(program2, fail(notFound.code, notFound.message));
116302
116720
  return;
@@ -116354,7 +116772,7 @@ function registerRegressionCommands(program2, deps) {
116354
116772
  }
116355
116773
 
116356
116774
  // src/mission-api.ts
116357
- import { existsSync as existsSync42 } from "node:fs";
116775
+ import { existsSync as existsSync43 } from "node:fs";
116358
116776
  import { resolve as resolvePath19 } from "node:path";
116359
116777
  var UnknownMissionTargetError = class extends Error {
116360
116778
  };
@@ -116392,7 +116810,7 @@ function applyAuth(target, auth) {
116392
116810
  }
116393
116811
  if (auth.storageState !== void 0) {
116394
116812
  const abs = resolvePath19(auth.storageState);
116395
- if (!existsSync42(abs)) throw new MissionTargetAuthError(`storage state not found: ${abs}`);
116813
+ if (!existsSync43(abs)) throw new MissionTargetAuthError(`storage state not found: ${abs}`);
116396
116814
  base.storageState = abs;
116397
116815
  }
116398
116816
  if (auth.saveStorageState !== void 0) base.saveStorageState = auth.saveStorageState === true ? true : resolvePath19(auth.saveStorageState);
@@ -116437,7 +116855,7 @@ async function promoteMissionTarget(ctx, id) {
116437
116855
  }
116438
116856
 
116439
116857
  // src/mission-queue-cli.ts
116440
- import { readFileSync as readFileSync36 } from "node:fs";
116858
+ import { readFileSync as readFileSync37 } from "node:fs";
116441
116859
  import { Option as Option2 } from "commander";
116442
116860
 
116443
116861
  // src/mcp-api.ts
@@ -116496,6 +116914,9 @@ var ALLOWED_TOOLS = [
116496
116914
  // #401 — the assertion-strength lint promote_journey applies; #402 — its mutation proof.
116497
116915
  "lint_journey",
116498
116916
  "verify_journey",
116917
+ // #580 — the two-journey chain: discover once, prepare-only reply per approved request. Promoted
116918
+ // Journeys only (--allow-draft is CLI-only); ids and outcomes only, never record or reply text.
116919
+ "chain_journeys",
116499
116920
  // #425 — a sweep: many explore missions over a targets file, one aggregated result.
116500
116921
  "run_sweep",
116501
116922
  // #432 — the read-only review sheet a person reads before promote_journey (bound by its content hash).
@@ -117265,7 +117686,7 @@ var tagsArg = (args) => {
117265
117686
  };
117266
117687
 
117267
117688
  // src/mcp-tools/findings.ts
117268
- import { existsSync as existsSync43 } from "node:fs";
117689
+ import { existsSync as existsSync44 } from "node:fs";
117269
117690
  import { access, readFile as readFile37 } from "node:fs/promises";
117270
117691
  import { join as join77 } from "node:path";
117271
117692
  function findingTools(ctx) {
@@ -117355,7 +117776,7 @@ function findingTools(ctx) {
117355
117776
  };
117356
117777
  const verifyFixOptions = (args) => {
117357
117778
  const storageState = optPath(args, "storageState", pathRoots, { session: true });
117358
- if (storageState !== void 0 && !existsSync43(storageState)) throw new McpArgError(`storage state not found: ${storageState}`);
117779
+ if (storageState !== void 0 && !existsSync44(storageState)) throw new McpArgError(`storage state not found: ${storageState}`);
117359
117780
  const replays = optInt(args, "replays", 1);
117360
117781
  const invariantFiles = (optStringArray(args, "invariants") ?? []).map((f2, i) => optPath({ [`invariants[${i}]`]: f2 }, `invariants[${i}]`, pathRoots));
117361
117782
  const fixtures = optPath(args, "fixtures", pathRoots);
@@ -117484,7 +117905,7 @@ function findingTools(ctx) {
117484
117905
  }
117485
117906
 
117486
117907
  // src/mcp-tools/journey.ts
117487
- import { existsSync as existsSync44 } from "node:fs";
117908
+ import { existsSync as existsSync45 } from "node:fs";
117488
117909
  function journeyTools(ctx) {
117489
117910
  const { deps, pathRoots, credentialStore, runJourney } = ctx;
117490
117911
  const findCaps = deps.findCapabilities ?? ((query) => findCapabilities(new JourneyRegistry(new FsJourneyStore(deps.journeysDir)), query));
@@ -117497,7 +117918,7 @@ function journeyTools(ctx) {
117497
117918
  { ...deps.environmentsFile === void 0 ? {} : { environmentsFile: deps.environmentsFile }, ...deps.targetsConfigPath === void 0 ? {} : { targetsFile: deps.targetsConfigPath } }
117498
117919
  );
117499
117920
  const storageState = optPath(args, "storageState", pathRoots, { session: true }) ?? environment?.storageState;
117500
- if (args.storageState !== void 0 && storageState !== void 0 && !existsSync44(storageState)) throw new McpArgError(`storage state not found: ${storageState}`);
117921
+ if (args.storageState !== void 0 && storageState !== void 0 && !existsSync45(storageState)) throw new McpArgError(`storage state not found: ${storageState}`);
117501
117922
  const headed = optBool(args, "headed");
117502
117923
  const slowMo = optInt(args, "slowMo", 0);
117503
117924
  const recordVideo = optRecordVideo(args, pathRoots);
@@ -118111,7 +118532,7 @@ function registerMissionQueueCommands(program2, mission, deps) {
118111
118532
  let invariants;
118112
118533
  if (o.invariants !== void 0) {
118113
118534
  try {
118114
- invariants = JSON.parse(readFileSync36(o.invariants, "utf8"));
118535
+ invariants = JSON.parse(readFileSync37(o.invariants, "utf8"));
118115
118536
  } catch (e) {
118116
118537
  refuse4("E_MISSION_QUEUE_INVARIANTS", `cannot read invariants file ${o.invariants}: ${e instanceof Error ? e.message.split("\n")[0] : String(e)}`);
118117
118538
  return;
@@ -118868,7 +119289,7 @@ function registerUxCommands(program2, deps) {
118868
119289
  }
118869
119290
 
118870
119291
  // src/logs-retention.ts
118871
- import { readFileSync as readFileSync37, readdirSync as readdirSync11, rmSync as rmSync2, rmdirSync as rmdirSync2, statSync as statSync12 } from "node:fs";
119292
+ import { readFileSync as readFileSync38, readdirSync as readdirSync11, rmSync as rmSync2, rmdirSync as rmdirSync2, statSync as statSync12 } from "node:fs";
118872
119293
  import { join as join79 } from "node:path";
118873
119294
  var DEFAULT_LOGS_RETENTION = { ttlDays: 14, keepLatest: 50 };
118874
119295
  var LogsConfigError = class extends Error {
@@ -118877,7 +119298,7 @@ var LogsConfigError = class extends Error {
118877
119298
  function loadLogsRetention(path2 = resolveDataDir(["config.json"])) {
118878
119299
  let raw;
118879
119300
  try {
118880
- raw = readFileSync37(path2, "utf8");
119301
+ raw = readFileSync38(path2, "utf8");
118881
119302
  } catch (e) {
118882
119303
  if (typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT") return DEFAULT_LOGS_RETENTION;
118883
119304
  throw new LogsConfigError(`cannot read ${path2}: ${e instanceof Error ? e.message : String(e)}`);
@@ -118938,8 +119359,8 @@ function pruneLogs(root2, retention, opts = {}) {
118938
119359
  }
118939
119360
 
118940
119361
  // src/logs-cli.ts
118941
- import { readFileSync as readFileSync38 } from "node:fs";
118942
- import { existsSync as existsSync45 } from "node:fs";
119362
+ import { readFileSync as readFileSync39 } from "node:fs";
119363
+ import { existsSync as existsSync46 } from "node:fs";
118943
119364
  var LOG_WRITING_COMMANDS = /* @__PURE__ */ new Set(["explore", "explore-author-journey", "record", "ux", "check", "run", "verify-fix", "capture"]);
118944
119365
  function registerLogsCommands(program2, deps) {
118945
119366
  const configPath = deps.logs?.configPath ?? resolveDataDir(["config.json"]);
@@ -118977,7 +119398,7 @@ function registerLogsCommands(program2, deps) {
118977
119398
  const emit5 = (envelope) => emitJson(program2, envelope);
118978
119399
  if (o.real === true && o.fakeAi === true) return emit5(fail("E_LOGS_ARGS", "--real and --fake-ai are mutually exclusive \u2014 pass one, not both"));
118979
119400
  if (o.threshold !== void 0 && !(o.threshold >= 0 && o.threshold <= 1)) return emit5(fail("E_LOGS_ARGS", "--threshold must be a probability in 0..1"));
118980
- if (!existsSync45(signalsPathFor(o.result))) {
119401
+ if (!existsSync46(signalsPathFor(o.result))) {
118981
119402
  return emit5(fail("E_LOGS_ARGS", `no signals next to ${o.result} (${signalsPathFor(o.result)}): run explore with --log-source and --log-triage first`));
118982
119403
  }
118983
119404
  let secrets;
@@ -118990,7 +119411,7 @@ function registerLogsCommands(program2, deps) {
118990
119411
  throw err;
118991
119412
  }
118992
119413
  try {
118993
- const file2 = JSON.parse(readFileSync38(o.result, "utf8"));
119414
+ const file2 = JSON.parse(readFileSync39(o.result, "utf8"));
118994
119415
  if (typeof file2.result !== "object" || file2.result === null) return emit5(fail("E_LOGS_ARGS", `${o.result} is not a jevitate run result`));
118995
119416
  const judge = o.real === true ? (await buildExploreGateways(deps, { real: true, fakeAi: false, jevProvider: o.jevProvider })).judge : void 0;
118996
119417
  const updated = await triageRunResult(file2.result, o.result, {
@@ -119014,7 +119435,7 @@ result ${o.result}
119014
119435
  }
119015
119436
 
119016
119437
  // src/inbox-cli.ts
119017
- import { readFileSync as readFileSync39 } from "node:fs";
119438
+ import { readFileSync as readFileSync40 } from "node:fs";
119018
119439
  import { Option as Option3 } from "commander";
119019
119440
  var INBOX_DIR_HELP = "inbox store directory (default: ~/.jevitate/inbox \u2014 the dir `jevitate mcp` and `jevitate ui` use)";
119020
119441
  var REDACTED = "***REDACTED***";
@@ -119122,7 +119543,7 @@ function registerInboxCommands(program2, deps) {
119122
119543
  let findings;
119123
119544
  if (o.findings !== void 0) {
119124
119545
  try {
119125
- findings = JSON.parse(readFileSync39(o.findings, "utf8"));
119546
+ findings = JSON.parse(readFileSync40(o.findings, "utf8"));
119126
119547
  } catch (e) {
119127
119548
  const code = `E_${commandPath(cmd).toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_INPUT`;
119128
119549
  emitJsonOrRefusal(program2, fail(code, `cannot read --findings ${o.findings}: ${e instanceof Error ? e.message.split("\n")[0] : String(e)}`));