@riddledc/riddle-proof 0.7.152 → 0.7.153

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.
@@ -295,12 +295,14 @@ async function pollRiddleJob(config, jobId, options = {}) {
295
295
  const attempts = Math.max(1, Math.floor(options.attempts ?? (options.wait ? 300 : 1)));
296
296
  const intervalMs = Math.max(0, Math.floor(options.intervalMs ?? 2e3));
297
297
  const progressEveryMs = Math.max(0, Math.floor(options.progressEveryMs ?? 1e4));
298
+ const unsubmittedTimeoutMs = Math.max(0, Math.floor(options.unsubmittedTimeoutMs ?? 0));
298
299
  const startedAt = Date.now();
299
300
  let job = null;
300
301
  let lastSnapshot = null;
301
302
  let lastProgressAt = 0;
302
303
  let lastProgressKey = "";
303
304
  let preSubmissionElapsedMs = 0;
305
+ let unsubmittedTimedOut = false;
304
306
  for (let index = 0; index < attempts; index += 1) {
305
307
  job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
306
308
  const observedAt = Date.now();
@@ -331,7 +333,11 @@ async function pollRiddleJob(config, jobId, options = {}) {
331
333
  await options.onProgress(lastSnapshot);
332
334
  }
333
335
  }
336
+ unsubmittedTimedOut = Boolean(
337
+ options.wait && unsubmittedTimeoutMs > 0 && lastSnapshot.running_without_submission && !lastSnapshot.created_at && !lastSnapshot.submitted_at && preSubmissionElapsedMs >= unsubmittedTimeoutMs
338
+ );
334
339
  if (lastSnapshot.terminal) break;
340
+ if (unsubmittedTimedOut) break;
335
341
  if (index + 1 < attempts) {
336
342
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
337
343
  }
@@ -356,6 +362,8 @@ async function pollRiddleJob(config, jobId, options = {}) {
356
362
  ...snapshot,
357
363
  timed_out: timedOut,
358
364
  interval_ms: intervalMs,
365
+ unsubmitted_timeout: unsubmittedTimedOut || void 0,
366
+ unsubmitted_timeout_ms: unsubmittedTimedOut ? unsubmittedTimeoutMs : void 0,
359
367
  message: pollMessage(snapshot, timedOut)
360
368
  }
361
369
  };
package/dist/cli.cjs CHANGED
@@ -6856,12 +6856,14 @@ async function pollRiddleJob(config, jobId, options = {}) {
6856
6856
  const attempts = Math.max(1, Math.floor(options.attempts ?? (options.wait ? 300 : 1)));
6857
6857
  const intervalMs = Math.max(0, Math.floor(options.intervalMs ?? 2e3));
6858
6858
  const progressEveryMs = Math.max(0, Math.floor(options.progressEveryMs ?? 1e4));
6859
+ const unsubmittedTimeoutMs = Math.max(0, Math.floor(options.unsubmittedTimeoutMs ?? 0));
6859
6860
  const startedAt = Date.now();
6860
6861
  let job = null;
6861
6862
  let lastSnapshot = null;
6862
6863
  let lastProgressAt = 0;
6863
6864
  let lastProgressKey = "";
6864
6865
  let preSubmissionElapsedMs = 0;
6866
+ let unsubmittedTimedOut = false;
6865
6867
  for (let index = 0; index < attempts; index += 1) {
6866
6868
  job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
6867
6869
  const observedAt = Date.now();
@@ -6892,7 +6894,11 @@ async function pollRiddleJob(config, jobId, options = {}) {
6892
6894
  await options.onProgress(lastSnapshot);
6893
6895
  }
6894
6896
  }
6897
+ unsubmittedTimedOut = Boolean(
6898
+ options.wait && unsubmittedTimeoutMs > 0 && lastSnapshot.running_without_submission && !lastSnapshot.created_at && !lastSnapshot.submitted_at && preSubmissionElapsedMs >= unsubmittedTimeoutMs
6899
+ );
6895
6900
  if (lastSnapshot.terminal) break;
6901
+ if (unsubmittedTimedOut) break;
6896
6902
  if (index + 1 < attempts) {
6897
6903
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
6898
6904
  }
@@ -6917,6 +6923,8 @@ async function pollRiddleJob(config, jobId, options = {}) {
6917
6923
  ...snapshot,
6918
6924
  timed_out: timedOut,
6919
6925
  interval_ms: intervalMs,
6926
+ unsubmitted_timeout: unsubmittedTimedOut || void 0,
6927
+ unsubmitted_timeout_ms: unsubmittedTimedOut ? unsubmittedTimeoutMs : void 0,
6920
6928
  message: pollMessage(snapshot, timedOut)
6921
6929
  }
6922
6930
  };
@@ -14741,6 +14749,8 @@ function runProfileStrictOption(options) {
14741
14749
  function runProfileSplitViewportsOption(options) {
14742
14750
  return optionBoolean(options, "splitViewports") ?? false;
14743
14751
  }
14752
+ var DEFAULT_PROFILE_UNSUBMITTED_RETRY_TIMEOUT_MS = 9e4;
14753
+ var DEFAULT_PROFILE_UNSUBMITTED_RETRIES = 1;
14744
14754
  function optionNumber(options, ...keys) {
14745
14755
  for (const key of keys) {
14746
14756
  const value = optionString(options, key);
@@ -14748,6 +14758,11 @@ function optionNumber(options, ...keys) {
14748
14758
  }
14749
14759
  return void 0;
14750
14760
  }
14761
+ function optionInteger(options, fallback, ...keys) {
14762
+ const value = optionNumber(options, ...keys);
14763
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
14764
+ return Math.floor(value);
14765
+ }
14751
14766
  function profileOutputDirOption(options) {
14752
14767
  return optionString(options, "output") ?? optionString(options, "outputDir");
14753
14768
  }
@@ -15065,6 +15080,8 @@ function profileRiddleJobMarkdown(result) {
15065
15080
  const submittedAt = cliString(riddle.submitted_at);
15066
15081
  const completedAt = cliString(riddle.completed_at);
15067
15082
  const artifactRecovery = riddle.artifact_recovery === true;
15083
+ const retryCount = cliFiniteNumber(riddle.retry_count);
15084
+ const staleJobIds = Array.isArray(riddle.stale_job_ids) ? riddle.stale_job_ids.map((value) => cliString(value)).filter((value) => Boolean(value)) : [];
15068
15085
  const parts = [
15069
15086
  mode ? `mode ${markdownInlineCode(mode)}` : "",
15070
15087
  jobCount === void 0 ? "" : `jobs ${jobCount}`,
@@ -15084,6 +15101,9 @@ function profileRiddleJobMarkdown(result) {
15084
15101
  if (artifactRecovery) {
15085
15102
  lines.push("- artifact recovery: used artifacts endpoint after non-terminal poll");
15086
15103
  }
15104
+ if (retryCount !== void 0 && retryCount > 0) {
15105
+ lines.push(`- retry recovery: replaced ${retryCount} unsubmitted job${retryCount === 1 ? "" : "s"}${staleJobIds.length ? ` (${staleJobIds.map((value) => markdownInlineCode(value)).join(", ")})` : ""}`);
15106
+ }
15087
15107
  const splitJobs = Array.isArray(riddle.split_jobs) ? riddle.split_jobs.map(cliRecord).filter((job) => Boolean(job)) : [];
15088
15108
  for (const job of splitJobs.slice(0, 12)) {
15089
15109
  const viewport = cliString(job.viewport) || "viewport";
@@ -15093,6 +15113,7 @@ function profileRiddleJobMarkdown(result) {
15093
15113
  const splitElapsedMs = cliFiniteNumber(job.elapsed_ms);
15094
15114
  const splitPreSubmissionElapsedMs = cliFiniteNumber(job.pre_submission_elapsed_ms);
15095
15115
  const splitArtifactRecovery = job.artifact_recovery === true;
15116
+ const splitRetryCount = cliFiniteNumber(job.retry_count);
15096
15117
  lines.push(
15097
15118
  `- ${viewport}: ${[
15098
15119
  splitJobId ? `job ${markdownInlineCode(splitJobId)}` : "",
@@ -15100,6 +15121,7 @@ function profileRiddleJobMarkdown(result) {
15100
15121
  splitTerminal === void 0 ? "" : `terminal ${splitTerminal ? "true" : "false"}`,
15101
15122
  splitElapsedMs === void 0 ? "" : `elapsed ${formatPollDuration(splitElapsedMs)}`,
15102
15123
  splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}`,
15124
+ splitRetryCount === void 0 || splitRetryCount <= 0 ? "" : `retries ${splitRetryCount}`,
15103
15125
  splitArtifactRecovery ? "artifact recovery" : ""
15104
15126
  ].filter(Boolean).join(", ") || "job metadata unavailable"}`
15105
15127
  );
@@ -15739,6 +15761,7 @@ async function profileResultFromRiddleArtifacts(profile, artifacts, fallbackInpu
15739
15761
  }
15740
15762
  function withRiddleMetadata(result, input) {
15741
15763
  const poll = input.poll;
15764
+ const staleJobIds = input.staleJobIds?.filter(Boolean);
15742
15765
  return {
15743
15766
  ...result,
15744
15767
  riddle: {
@@ -15755,6 +15778,8 @@ function withRiddleMetadata(result, input) {
15755
15778
  attempt: poll?.attempt ?? result.riddle?.attempt,
15756
15779
  attempts: poll?.attempts ?? result.riddle?.attempts,
15757
15780
  timed_out: poll?.timed_out ?? result.riddle?.timed_out,
15781
+ retry_count: input.retryCount ?? result.riddle?.retry_count,
15782
+ stale_job_ids: staleJobIds?.length ? staleJobIds : result.riddle?.stale_job_ids,
15758
15783
  artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery
15759
15784
  },
15760
15785
  artifacts: {
@@ -15825,6 +15850,40 @@ function riddleMetadataFromPoll(jobId, poll) {
15825
15850
  timed_out: poll.poll?.timed_out
15826
15851
  };
15827
15852
  }
15853
+ function profileUnsubmittedRetryTimeoutMs(options) {
15854
+ return Math.max(0, optionInteger(
15855
+ options,
15856
+ DEFAULT_PROFILE_UNSUBMITTED_RETRY_TIMEOUT_MS,
15857
+ "unsubmittedTimeoutMs",
15858
+ "unsubmittedJobTimeoutMs",
15859
+ "submitTimeoutMs"
15860
+ ));
15861
+ }
15862
+ function profileUnsubmittedRetryLimit(options) {
15863
+ return Math.max(0, optionInteger(
15864
+ options,
15865
+ DEFAULT_PROFILE_UNSUBMITTED_RETRIES,
15866
+ "unsubmittedRetries",
15867
+ "unsubmittedJobRetries",
15868
+ "submitRetries"
15869
+ ));
15870
+ }
15871
+ function shouldRetryUnsubmittedRiddleJob(poll) {
15872
+ return poll.terminal !== true && poll.poll?.unsubmitted_timeout === true && !poll.poll.created_at && !poll.poll.submitted_at;
15873
+ }
15874
+ function riddlePollOptionsForProfile(options) {
15875
+ return {
15876
+ wait: true,
15877
+ attempts: optionNumber(options, "pollAttempts", "attempts"),
15878
+ intervalMs: optionNumber(options, "intervalMs"),
15879
+ progressEveryMs: optionNumber(options, "progressEveryMs"),
15880
+ unsubmittedTimeoutMs: profileUnsubmittedRetryTimeoutMs(options),
15881
+ onProgress: options.quiet !== true ? (snapshot) => {
15882
+ process.stderr.write(`${riddlePollProgressLine(snapshot)}
15883
+ `);
15884
+ } : void 0
15885
+ };
15886
+ }
15828
15887
  function profileForSplitViewport(profile, viewport) {
15829
15888
  return {
15830
15889
  ...profile,
@@ -15872,6 +15931,8 @@ function splitViewportRiddleMetadata(childRuns) {
15872
15931
  attempt: result.riddle?.attempt,
15873
15932
  attempts: result.riddle?.attempts,
15874
15933
  timed_out: result.riddle?.timed_out,
15934
+ retry_count: result.riddle?.retry_count,
15935
+ stale_job_ids: result.riddle?.stale_job_ids,
15875
15936
  artifact_recovery: result.riddle?.artifact_recovery
15876
15937
  }));
15877
15938
  return {
@@ -15883,6 +15944,8 @@ function splitViewportRiddleMetadata(childRuns) {
15883
15944
  queue_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.queue_elapsed_ms)),
15884
15945
  pre_submission_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.pre_submission_elapsed_ms)),
15885
15946
  elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.elapsed_ms)),
15947
+ retry_count: splitJobs.reduce((sum, job) => sum + (typeof job.retry_count === "number" && Number.isFinite(job.retry_count) ? job.retry_count : 0), 0),
15948
+ stale_job_ids: splitJobs.flatMap((job) => Array.isArray(job.stale_job_ids) ? job.stale_job_ids : []),
15886
15949
  split_jobs: splitJobs
15887
15950
  };
15888
15951
  }
@@ -15970,37 +16033,48 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
15970
16033
  const { client, runner } = input;
15971
16034
  const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
15972
16035
  let created;
15973
- try {
15974
- created = await client.runScript({
15975
- url: targetUrl,
15976
- script: buildRiddleProofProfileScript(profile),
15977
- viewport: profile.target.viewports[0],
15978
- timeoutSec: resolveRiddleProofProfileTimeoutSec(
15979
- profile,
15980
- optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0
15981
- ),
15982
- strict: runProfileStrictOption(options),
15983
- sync: options.sync === true ? true : void 0
15984
- });
15985
- } catch (error) {
15986
- return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error });
16036
+ let poll;
16037
+ let jobId = "";
16038
+ const staleJobIds = [];
16039
+ const retryLimit = profileUnsubmittedRetryLimit(options);
16040
+ const pollOptions = riddlePollOptionsForProfile(options);
16041
+ for (let attempt = 0; attempt <= retryLimit; attempt += 1) {
16042
+ try {
16043
+ created = await client.runScript({
16044
+ url: targetUrl,
16045
+ script: buildRiddleProofProfileScript(profile),
16046
+ viewport: profile.target.viewports[0],
16047
+ timeoutSec: resolveRiddleProofProfileTimeoutSec(
16048
+ profile,
16049
+ optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0
16050
+ ),
16051
+ strict: runProfileStrictOption(options),
16052
+ sync: options.sync === true ? true : void 0
16053
+ });
16054
+ } catch (error) {
16055
+ return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error });
16056
+ }
16057
+ jobId = typeof created.job_id === "string" ? created.job_id : typeof created.id === "string" ? created.id : "";
16058
+ if (!jobId) {
16059
+ const directResult = extractRiddleProofProfileResult(created);
16060
+ return directResult ? withRiddleMetadata(directResult, { artifacts: collectRiddleProfileArtifactRefs(created) }) : createRiddleProofProfileInsufficientResult({ profile, runner, error: "Riddle run response was missing job_id.", artifacts: collectRiddleProfileArtifactRefs(created) });
16061
+ }
16062
+ poll = await client.pollJob(jobId, pollOptions);
16063
+ if (attempt < retryLimit && shouldRetryUnsubmittedRiddleJob(poll)) {
16064
+ staleJobIds.push(jobId);
16065
+ if (options.quiet !== true) {
16066
+ process.stderr.write(`[riddle-poll] ${jobId} stayed unsubmitted for ${formatPollDuration(poll.poll?.pre_submission_elapsed_ms)}; retrying hosted run ${attempt + 1}/${retryLimit}
16067
+ `);
16068
+ }
16069
+ continue;
16070
+ }
16071
+ break;
15987
16072
  }
15988
- const jobId = typeof created.job_id === "string" ? created.job_id : typeof created.id === "string" ? created.id : "";
15989
- if (!jobId) {
15990
- const directResult = extractRiddleProofProfileResult(created);
15991
- return directResult ? withRiddleMetadata(directResult, { artifacts: collectRiddleProfileArtifactRefs(created) }) : createRiddleProofProfileInsufficientResult({ profile, runner, error: "Riddle run response was missing job_id.", artifacts: collectRiddleProfileArtifactRefs(created) });
16073
+ if (!poll) {
16074
+ return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error: "Riddle job polling did not produce a result." });
15992
16075
  }
15993
- const poll = await client.pollJob(jobId, {
15994
- wait: true,
15995
- attempts: optionNumber(options, "pollAttempts", "attempts"),
15996
- intervalMs: optionNumber(options, "intervalMs"),
15997
- progressEveryMs: optionNumber(options, "progressEveryMs"),
15998
- onProgress: options.quiet !== true ? (snapshot) => {
15999
- process.stderr.write(`${riddlePollProgressLine(snapshot)}
16000
- `);
16001
- } : void 0
16002
- });
16003
16076
  const artifacts = collectRiddleProfileArtifactRefs(poll.artifacts);
16077
+ const retryCount = staleJobIds.length || void 0;
16004
16078
  if (!poll.ok || !poll.terminal) {
16005
16079
  const recoveredResult = await recoverProfileResultFromRiddleArtifacts(profile, {
16006
16080
  client,
@@ -16008,12 +16082,18 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
16008
16082
  jobId,
16009
16083
  poll
16010
16084
  });
16011
- if (recoveredResult) return recoveredResult;
16085
+ if (recoveredResult) {
16086
+ return withRiddleMetadata(recoveredResult, { retryCount, staleJobIds });
16087
+ }
16012
16088
  return createRiddleProofProfileEnvironmentBlockedResult({
16013
16089
  profile,
16014
16090
  runner,
16015
16091
  error: `Riddle job ${jobId} ended with status ${poll.status || "unknown"}.`,
16016
- riddle: riddleMetadataFromPoll(jobId, poll),
16092
+ riddle: {
16093
+ ...riddleMetadataFromPoll(jobId, poll),
16094
+ retry_count: retryCount,
16095
+ stale_job_ids: staleJobIds.length ? staleJobIds : void 0
16096
+ },
16017
16097
  artifacts
16018
16098
  });
16019
16099
  }
@@ -16031,7 +16111,9 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
16031
16111
  status: poll.status,
16032
16112
  terminal: poll.terminal,
16033
16113
  poll: poll.poll,
16034
- artifacts
16114
+ artifacts,
16115
+ retryCount,
16116
+ staleJobIds
16035
16117
  });
16036
16118
  }
16037
16119
  async function runSplitViewportProfileForCli(profile, options, input) {
package/dist/cli.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  createRiddleApiClient,
19
19
  isTerminalRiddleJobStatus,
20
20
  parseRiddleViewport
21
- } from "./chunk-M3ZTY6PQ.js";
21
+ } from "./chunk-PPZ5A5MC.js";
22
22
  import {
23
23
  createDisabledRiddleProofAgentAdapter,
24
24
  readRiddleProofRunStatus,
@@ -108,6 +108,8 @@ function runProfileStrictOption(options) {
108
108
  function runProfileSplitViewportsOption(options) {
109
109
  return optionBoolean(options, "splitViewports") ?? false;
110
110
  }
111
+ var DEFAULT_PROFILE_UNSUBMITTED_RETRY_TIMEOUT_MS = 9e4;
112
+ var DEFAULT_PROFILE_UNSUBMITTED_RETRIES = 1;
111
113
  function optionNumber(options, ...keys) {
112
114
  for (const key of keys) {
113
115
  const value = optionString(options, key);
@@ -115,6 +117,11 @@ function optionNumber(options, ...keys) {
115
117
  }
116
118
  return void 0;
117
119
  }
120
+ function optionInteger(options, fallback, ...keys) {
121
+ const value = optionNumber(options, ...keys);
122
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
123
+ return Math.floor(value);
124
+ }
118
125
  function profileOutputDirOption(options) {
119
126
  return optionString(options, "output") ?? optionString(options, "outputDir");
120
127
  }
@@ -432,6 +439,8 @@ function profileRiddleJobMarkdown(result) {
432
439
  const submittedAt = cliString(riddle.submitted_at);
433
440
  const completedAt = cliString(riddle.completed_at);
434
441
  const artifactRecovery = riddle.artifact_recovery === true;
442
+ const retryCount = cliFiniteNumber(riddle.retry_count);
443
+ const staleJobIds = Array.isArray(riddle.stale_job_ids) ? riddle.stale_job_ids.map((value) => cliString(value)).filter((value) => Boolean(value)) : [];
435
444
  const parts = [
436
445
  mode ? `mode ${markdownInlineCode(mode)}` : "",
437
446
  jobCount === void 0 ? "" : `jobs ${jobCount}`,
@@ -451,6 +460,9 @@ function profileRiddleJobMarkdown(result) {
451
460
  if (artifactRecovery) {
452
461
  lines.push("- artifact recovery: used artifacts endpoint after non-terminal poll");
453
462
  }
463
+ if (retryCount !== void 0 && retryCount > 0) {
464
+ lines.push(`- retry recovery: replaced ${retryCount} unsubmitted job${retryCount === 1 ? "" : "s"}${staleJobIds.length ? ` (${staleJobIds.map((value) => markdownInlineCode(value)).join(", ")})` : ""}`);
465
+ }
454
466
  const splitJobs = Array.isArray(riddle.split_jobs) ? riddle.split_jobs.map(cliRecord).filter((job) => Boolean(job)) : [];
455
467
  for (const job of splitJobs.slice(0, 12)) {
456
468
  const viewport = cliString(job.viewport) || "viewport";
@@ -460,6 +472,7 @@ function profileRiddleJobMarkdown(result) {
460
472
  const splitElapsedMs = cliFiniteNumber(job.elapsed_ms);
461
473
  const splitPreSubmissionElapsedMs = cliFiniteNumber(job.pre_submission_elapsed_ms);
462
474
  const splitArtifactRecovery = job.artifact_recovery === true;
475
+ const splitRetryCount = cliFiniteNumber(job.retry_count);
463
476
  lines.push(
464
477
  `- ${viewport}: ${[
465
478
  splitJobId ? `job ${markdownInlineCode(splitJobId)}` : "",
@@ -467,6 +480,7 @@ function profileRiddleJobMarkdown(result) {
467
480
  splitTerminal === void 0 ? "" : `terminal ${splitTerminal ? "true" : "false"}`,
468
481
  splitElapsedMs === void 0 ? "" : `elapsed ${formatPollDuration(splitElapsedMs)}`,
469
482
  splitPreSubmissionElapsedMs === void 0 || splitPreSubmissionElapsedMs < 1e3 ? "" : `pre-submit ${formatPollDuration(splitPreSubmissionElapsedMs)}`,
483
+ splitRetryCount === void 0 || splitRetryCount <= 0 ? "" : `retries ${splitRetryCount}`,
470
484
  splitArtifactRecovery ? "artifact recovery" : ""
471
485
  ].filter(Boolean).join(", ") || "job metadata unavailable"}`
472
486
  );
@@ -1106,6 +1120,7 @@ async function profileResultFromRiddleArtifacts(profile, artifacts, fallbackInpu
1106
1120
  }
1107
1121
  function withRiddleMetadata(result, input) {
1108
1122
  const poll = input.poll;
1123
+ const staleJobIds = input.staleJobIds?.filter(Boolean);
1109
1124
  return {
1110
1125
  ...result,
1111
1126
  riddle: {
@@ -1122,6 +1137,8 @@ function withRiddleMetadata(result, input) {
1122
1137
  attempt: poll?.attempt ?? result.riddle?.attempt,
1123
1138
  attempts: poll?.attempts ?? result.riddle?.attempts,
1124
1139
  timed_out: poll?.timed_out ?? result.riddle?.timed_out,
1140
+ retry_count: input.retryCount ?? result.riddle?.retry_count,
1141
+ stale_job_ids: staleJobIds?.length ? staleJobIds : result.riddle?.stale_job_ids,
1125
1142
  artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery
1126
1143
  },
1127
1144
  artifacts: {
@@ -1192,6 +1209,40 @@ function riddleMetadataFromPoll(jobId, poll) {
1192
1209
  timed_out: poll.poll?.timed_out
1193
1210
  };
1194
1211
  }
1212
+ function profileUnsubmittedRetryTimeoutMs(options) {
1213
+ return Math.max(0, optionInteger(
1214
+ options,
1215
+ DEFAULT_PROFILE_UNSUBMITTED_RETRY_TIMEOUT_MS,
1216
+ "unsubmittedTimeoutMs",
1217
+ "unsubmittedJobTimeoutMs",
1218
+ "submitTimeoutMs"
1219
+ ));
1220
+ }
1221
+ function profileUnsubmittedRetryLimit(options) {
1222
+ return Math.max(0, optionInteger(
1223
+ options,
1224
+ DEFAULT_PROFILE_UNSUBMITTED_RETRIES,
1225
+ "unsubmittedRetries",
1226
+ "unsubmittedJobRetries",
1227
+ "submitRetries"
1228
+ ));
1229
+ }
1230
+ function shouldRetryUnsubmittedRiddleJob(poll) {
1231
+ return poll.terminal !== true && poll.poll?.unsubmitted_timeout === true && !poll.poll.created_at && !poll.poll.submitted_at;
1232
+ }
1233
+ function riddlePollOptionsForProfile(options) {
1234
+ return {
1235
+ wait: true,
1236
+ attempts: optionNumber(options, "pollAttempts", "attempts"),
1237
+ intervalMs: optionNumber(options, "intervalMs"),
1238
+ progressEveryMs: optionNumber(options, "progressEveryMs"),
1239
+ unsubmittedTimeoutMs: profileUnsubmittedRetryTimeoutMs(options),
1240
+ onProgress: options.quiet !== true ? (snapshot) => {
1241
+ process.stderr.write(`${riddlePollProgressLine(snapshot)}
1242
+ `);
1243
+ } : void 0
1244
+ };
1245
+ }
1195
1246
  function profileForSplitViewport(profile, viewport) {
1196
1247
  return {
1197
1248
  ...profile,
@@ -1239,6 +1290,8 @@ function splitViewportRiddleMetadata(childRuns) {
1239
1290
  attempt: result.riddle?.attempt,
1240
1291
  attempts: result.riddle?.attempts,
1241
1292
  timed_out: result.riddle?.timed_out,
1293
+ retry_count: result.riddle?.retry_count,
1294
+ stale_job_ids: result.riddle?.stale_job_ids,
1242
1295
  artifact_recovery: result.riddle?.artifact_recovery
1243
1296
  }));
1244
1297
  return {
@@ -1250,6 +1303,8 @@ function splitViewportRiddleMetadata(childRuns) {
1250
1303
  queue_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.queue_elapsed_ms)),
1251
1304
  pre_submission_elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.pre_submission_elapsed_ms)),
1252
1305
  elapsed_ms: sumDefinedNumbers(splitJobs.map((job) => job.elapsed_ms)),
1306
+ retry_count: splitJobs.reduce((sum, job) => sum + (typeof job.retry_count === "number" && Number.isFinite(job.retry_count) ? job.retry_count : 0), 0),
1307
+ stale_job_ids: splitJobs.flatMap((job) => Array.isArray(job.stale_job_ids) ? job.stale_job_ids : []),
1253
1308
  split_jobs: splitJobs
1254
1309
  };
1255
1310
  }
@@ -1337,37 +1392,48 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
1337
1392
  const { client, runner } = input;
1338
1393
  const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
1339
1394
  let created;
1340
- try {
1341
- created = await client.runScript({
1342
- url: targetUrl,
1343
- script: buildRiddleProofProfileScript(profile),
1344
- viewport: profile.target.viewports[0],
1345
- timeoutSec: resolveRiddleProofProfileTimeoutSec(
1346
- profile,
1347
- optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0
1348
- ),
1349
- strict: runProfileStrictOption(options),
1350
- sync: options.sync === true ? true : void 0
1351
- });
1352
- } catch (error) {
1353
- return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error });
1395
+ let poll;
1396
+ let jobId = "";
1397
+ const staleJobIds = [];
1398
+ const retryLimit = profileUnsubmittedRetryLimit(options);
1399
+ const pollOptions = riddlePollOptionsForProfile(options);
1400
+ for (let attempt = 0; attempt <= retryLimit; attempt += 1) {
1401
+ try {
1402
+ created = await client.runScript({
1403
+ url: targetUrl,
1404
+ script: buildRiddleProofProfileScript(profile),
1405
+ viewport: profile.target.viewports[0],
1406
+ timeoutSec: resolveRiddleProofProfileTimeoutSec(
1407
+ profile,
1408
+ optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0
1409
+ ),
1410
+ strict: runProfileStrictOption(options),
1411
+ sync: options.sync === true ? true : void 0
1412
+ });
1413
+ } catch (error) {
1414
+ return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error });
1415
+ }
1416
+ jobId = typeof created.job_id === "string" ? created.job_id : typeof created.id === "string" ? created.id : "";
1417
+ if (!jobId) {
1418
+ const directResult = extractRiddleProofProfileResult(created);
1419
+ return directResult ? withRiddleMetadata(directResult, { artifacts: collectRiddleProfileArtifactRefs(created) }) : createRiddleProofProfileInsufficientResult({ profile, runner, error: "Riddle run response was missing job_id.", artifacts: collectRiddleProfileArtifactRefs(created) });
1420
+ }
1421
+ poll = await client.pollJob(jobId, pollOptions);
1422
+ if (attempt < retryLimit && shouldRetryUnsubmittedRiddleJob(poll)) {
1423
+ staleJobIds.push(jobId);
1424
+ if (options.quiet !== true) {
1425
+ process.stderr.write(`[riddle-poll] ${jobId} stayed unsubmitted for ${formatPollDuration(poll.poll?.pre_submission_elapsed_ms)}; retrying hosted run ${attempt + 1}/${retryLimit}
1426
+ `);
1427
+ }
1428
+ continue;
1429
+ }
1430
+ break;
1354
1431
  }
1355
- const jobId = typeof created.job_id === "string" ? created.job_id : typeof created.id === "string" ? created.id : "";
1356
- if (!jobId) {
1357
- const directResult = extractRiddleProofProfileResult(created);
1358
- return directResult ? withRiddleMetadata(directResult, { artifacts: collectRiddleProfileArtifactRefs(created) }) : createRiddleProofProfileInsufficientResult({ profile, runner, error: "Riddle run response was missing job_id.", artifacts: collectRiddleProfileArtifactRefs(created) });
1432
+ if (!poll) {
1433
+ return createRiddleProofProfileEnvironmentBlockedResult({ profile, runner, error: "Riddle job polling did not produce a result." });
1359
1434
  }
1360
- const poll = await client.pollJob(jobId, {
1361
- wait: true,
1362
- attempts: optionNumber(options, "pollAttempts", "attempts"),
1363
- intervalMs: optionNumber(options, "intervalMs"),
1364
- progressEveryMs: optionNumber(options, "progressEveryMs"),
1365
- onProgress: options.quiet !== true ? (snapshot) => {
1366
- process.stderr.write(`${riddlePollProgressLine(snapshot)}
1367
- `);
1368
- } : void 0
1369
- });
1370
1435
  const artifacts = collectRiddleProfileArtifactRefs(poll.artifacts);
1436
+ const retryCount = staleJobIds.length || void 0;
1371
1437
  if (!poll.ok || !poll.terminal) {
1372
1438
  const recoveredResult = await recoverProfileResultFromRiddleArtifacts(profile, {
1373
1439
  client,
@@ -1375,12 +1441,18 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
1375
1441
  jobId,
1376
1442
  poll
1377
1443
  });
1378
- if (recoveredResult) return recoveredResult;
1444
+ if (recoveredResult) {
1445
+ return withRiddleMetadata(recoveredResult, { retryCount, staleJobIds });
1446
+ }
1379
1447
  return createRiddleProofProfileEnvironmentBlockedResult({
1380
1448
  profile,
1381
1449
  runner,
1382
1450
  error: `Riddle job ${jobId} ended with status ${poll.status || "unknown"}.`,
1383
- riddle: riddleMetadataFromPoll(jobId, poll),
1451
+ riddle: {
1452
+ ...riddleMetadataFromPoll(jobId, poll),
1453
+ retry_count: retryCount,
1454
+ stale_job_ids: staleJobIds.length ? staleJobIds : void 0
1455
+ },
1384
1456
  artifacts
1385
1457
  });
1386
1458
  }
@@ -1398,7 +1470,9 @@ async function runSingleRiddleProfileForCli(profile, options, input) {
1398
1470
  status: poll.status,
1399
1471
  terminal: poll.terminal,
1400
1472
  poll: poll.poll,
1401
- artifacts
1473
+ artifacts,
1474
+ retryCount,
1475
+ staleJobIds
1402
1476
  });
1403
1477
  }
1404
1478
  async function runSplitViewportProfileForCli(profile, options, input) {
package/dist/index.cjs CHANGED
@@ -16769,12 +16769,14 @@ async function pollRiddleJob(config, jobId, options = {}) {
16769
16769
  const attempts = Math.max(1, Math.floor(options.attempts ?? (options.wait ? 300 : 1)));
16770
16770
  const intervalMs = Math.max(0, Math.floor(options.intervalMs ?? 2e3));
16771
16771
  const progressEveryMs = Math.max(0, Math.floor(options.progressEveryMs ?? 1e4));
16772
+ const unsubmittedTimeoutMs = Math.max(0, Math.floor(options.unsubmittedTimeoutMs ?? 0));
16772
16773
  const startedAt = Date.now();
16773
16774
  let job = null;
16774
16775
  let lastSnapshot = null;
16775
16776
  let lastProgressAt = 0;
16776
16777
  let lastProgressKey = "";
16777
16778
  let preSubmissionElapsedMs = 0;
16779
+ let unsubmittedTimedOut = false;
16778
16780
  for (let index = 0; index < attempts; index += 1) {
16779
16781
  job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
16780
16782
  const observedAt = Date.now();
@@ -16805,7 +16807,11 @@ async function pollRiddleJob(config, jobId, options = {}) {
16805
16807
  await options.onProgress(lastSnapshot);
16806
16808
  }
16807
16809
  }
16810
+ unsubmittedTimedOut = Boolean(
16811
+ options.wait && unsubmittedTimeoutMs > 0 && lastSnapshot.running_without_submission && !lastSnapshot.created_at && !lastSnapshot.submitted_at && preSubmissionElapsedMs >= unsubmittedTimeoutMs
16812
+ );
16808
16813
  if (lastSnapshot.terminal) break;
16814
+ if (unsubmittedTimedOut) break;
16809
16815
  if (index + 1 < attempts) {
16810
16816
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
16811
16817
  }
@@ -16830,6 +16836,8 @@ async function pollRiddleJob(config, jobId, options = {}) {
16830
16836
  ...snapshot,
16831
16837
  timed_out: timedOut,
16832
16838
  interval_ms: intervalMs,
16839
+ unsubmitted_timeout: unsubmittedTimedOut || void 0,
16840
+ unsubmitted_timeout_ms: unsubmittedTimedOut ? unsubmittedTimeoutMs : void 0,
16833
16841
  message: pollMessage(snapshot, timedOut)
16834
16842
  }
16835
16843
  };
package/dist/index.js CHANGED
@@ -77,7 +77,7 @@ import {
77
77
  riddleRequestJson,
78
78
  runRiddleScript,
79
79
  runRiddleServerPreview
80
- } from "./chunk-M3ZTY6PQ.js";
80
+ } from "./chunk-PPZ5A5MC.js";
81
81
  import {
82
82
  DEFAULT_DIAGNOSTIC_ARRAY_LIMIT,
83
83
  DEFAULT_DIAGNOSTIC_HISTORY_LIMIT,
@@ -391,6 +391,8 @@ interface RiddleProofProfileResult {
391
391
  attempt?: number;
392
392
  attempts?: number;
393
393
  timed_out?: boolean;
394
+ retry_count?: number;
395
+ stale_job_ids?: string[];
394
396
  artifact_recovery?: boolean;
395
397
  split_jobs?: Array<{
396
398
  viewport: string;
@@ -403,6 +405,8 @@ interface RiddleProofProfileResult {
403
405
  attempt?: number;
404
406
  attempts?: number;
405
407
  timed_out?: boolean;
408
+ retry_count?: number;
409
+ stale_job_ids?: string[];
406
410
  artifact_recovery?: boolean;
407
411
  }>;
408
412
  };
package/dist/profile.d.ts CHANGED
@@ -391,6 +391,8 @@ interface RiddleProofProfileResult {
391
391
  attempt?: number;
392
392
  attempts?: number;
393
393
  timed_out?: boolean;
394
+ retry_count?: number;
395
+ stale_job_ids?: string[];
394
396
  artifact_recovery?: boolean;
395
397
  split_jobs?: Array<{
396
398
  viewport: string;
@@ -403,6 +405,8 @@ interface RiddleProofProfileResult {
403
405
  attempt?: number;
404
406
  attempts?: number;
405
407
  timed_out?: boolean;
408
+ retry_count?: number;
409
+ stale_job_ids?: string[];
406
410
  artifact_recovery?: boolean;
407
411
  }>;
408
412
  };
@@ -341,12 +341,14 @@ async function pollRiddleJob(config, jobId, options = {}) {
341
341
  const attempts = Math.max(1, Math.floor(options.attempts ?? (options.wait ? 300 : 1)));
342
342
  const intervalMs = Math.max(0, Math.floor(options.intervalMs ?? 2e3));
343
343
  const progressEveryMs = Math.max(0, Math.floor(options.progressEveryMs ?? 1e4));
344
+ const unsubmittedTimeoutMs = Math.max(0, Math.floor(options.unsubmittedTimeoutMs ?? 0));
344
345
  const startedAt = Date.now();
345
346
  let job = null;
346
347
  let lastSnapshot = null;
347
348
  let lastProgressAt = 0;
348
349
  let lastProgressKey = "";
349
350
  let preSubmissionElapsedMs = 0;
351
+ let unsubmittedTimedOut = false;
350
352
  for (let index = 0; index < attempts; index += 1) {
351
353
  job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
352
354
  const observedAt = Date.now();
@@ -377,7 +379,11 @@ async function pollRiddleJob(config, jobId, options = {}) {
377
379
  await options.onProgress(lastSnapshot);
378
380
  }
379
381
  }
382
+ unsubmittedTimedOut = Boolean(
383
+ options.wait && unsubmittedTimeoutMs > 0 && lastSnapshot.running_without_submission && !lastSnapshot.created_at && !lastSnapshot.submitted_at && preSubmissionElapsedMs >= unsubmittedTimeoutMs
384
+ );
380
385
  if (lastSnapshot.terminal) break;
386
+ if (unsubmittedTimedOut) break;
381
387
  if (index + 1 < attempts) {
382
388
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
383
389
  }
@@ -402,6 +408,8 @@ async function pollRiddleJob(config, jobId, options = {}) {
402
408
  ...snapshot,
403
409
  timed_out: timedOut,
404
410
  interval_ms: intervalMs,
411
+ unsubmitted_timeout: unsubmittedTimedOut || void 0,
412
+ unsubmitted_timeout_ms: unsubmittedTimedOut ? unsubmittedTimeoutMs : void 0,
405
413
  message: pollMessage(snapshot, timedOut)
406
414
  }
407
415
  };
@@ -24,6 +24,8 @@ interface RiddlePollProgressSnapshot {
24
24
  interface RiddlePollSummary extends RiddlePollProgressSnapshot {
25
25
  timed_out: boolean;
26
26
  interval_ms: number;
27
+ unsubmitted_timeout?: boolean;
28
+ unsubmitted_timeout_ms?: number;
27
29
  message?: string;
28
30
  }
29
31
  interface RiddlePollJobOptions {
@@ -31,6 +33,7 @@ interface RiddlePollJobOptions {
31
33
  attempts?: number;
32
34
  intervalMs?: number;
33
35
  progressEveryMs?: number;
36
+ unsubmittedTimeoutMs?: number;
34
37
  onProgress?: (snapshot: RiddlePollProgressSnapshot) => void | Promise<void>;
35
38
  }
36
39
  type RiddlePreviewFramework = "spa" | "static";
@@ -24,6 +24,8 @@ interface RiddlePollProgressSnapshot {
24
24
  interface RiddlePollSummary extends RiddlePollProgressSnapshot {
25
25
  timed_out: boolean;
26
26
  interval_ms: number;
27
+ unsubmitted_timeout?: boolean;
28
+ unsubmitted_timeout_ms?: number;
27
29
  message?: string;
28
30
  }
29
31
  interface RiddlePollJobOptions {
@@ -31,6 +33,7 @@ interface RiddlePollJobOptions {
31
33
  attempts?: number;
32
34
  intervalMs?: number;
33
35
  progressEveryMs?: number;
36
+ unsubmittedTimeoutMs?: number;
34
37
  onProgress?: (snapshot: RiddlePollProgressSnapshot) => void | Promise<void>;
35
38
  }
36
39
  type RiddlePreviewFramework = "spa" | "static";
@@ -12,7 +12,7 @@ import {
12
12
  riddleRequestJson,
13
13
  runRiddleScript,
14
14
  runRiddleServerPreview
15
- } from "./chunk-M3ZTY6PQ.js";
15
+ } from "./chunk-PPZ5A5MC.js";
16
16
  export {
17
17
  DEFAULT_RIDDLE_API_BASE_URL,
18
18
  DEFAULT_RIDDLE_API_KEY_FILE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.152",
3
+ "version": "0.7.153",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",