@workflow/world-testing 5.0.0-beta.55 → 5.0.0-beta.56

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.
@@ -21110,7 +21110,30 @@ var init_events2 = __esm({
21110
21110
  tokenRetentionUntil: external_exports2.coerce.date().optional(),
21111
21111
  metadata: SerializedDataSchema2.optional(),
21112
21112
  isWebhook: external_exports2.boolean().optional(),
21113
- isSystem: external_exports2.boolean().optional()
21113
+ isSystem: external_exports2.boolean().optional(),
21114
+ /**
21115
+ * `createHook({ experimental_force: true })`: when the token belongs to
21116
+ * another live run, the World takes it over — journaling
21117
+ * `hook_disposed{forceClaimedBy}` in that run's log and re-pointing the
21118
+ * token — instead of answering `hook_conflict`. Requires the
21119
+ * `hookForceClaim` capability.
21120
+ */
21121
+ force: external_exports2.boolean().optional(),
21122
+ /**
21123
+ * World-written on a forced creation that did take a token over: the
21124
+ * run and hook it was taken from, plus what a queue message to that run
21125
+ * needs (`workflowName`, `deploymentId`, `runSpecVersion`). Absent when
21126
+ * the token was free. The wake-targeting fields are in the LOG, not only
21127
+ * on the hook entity, so a replay can republish the victim's wake from
21128
+ * the row alone; see `publishForceClaimVictimWake` in @workflow/core.
21129
+ */
21130
+ forceClaimedFrom: external_exports2.object({
21131
+ runId: external_exports2.string(),
21132
+ hookId: external_exports2.string(),
21133
+ workflowName: external_exports2.string().optional(),
21134
+ deploymentId: external_exports2.string().optional(),
21135
+ runSpecVersion: external_exports2.number().optional()
21136
+ }).optional()
21114
21137
  })
21115
21138
  }));
21116
21139
  HookReceivedEventSchema2 = external_exports2.compile(BaseEventSchema2.extend({
@@ -21125,7 +21148,14 @@ var init_events2 = __esm({
21125
21148
  eventType: external_exports2.literal("hook_disposed"),
21126
21149
  correlationId: external_exports2.string(),
21127
21150
  eventData: external_exports2.object({
21128
- token: external_exports2.string().optional()
21151
+ token: external_exports2.string().optional(),
21152
+ /**
21153
+ * World-written: this disposal was not the run's own. Another run
21154
+ * took the hook's token with `experimental_force`, and this names
21155
+ * it. The hook consumer rejects the hook's awaiters with
21156
+ * `HookForceClaimedError` when it reads this row.
21157
+ */
21158
+ forceClaimedBy: external_exports2.object({ runId: external_exports2.string(), hookId: external_exports2.string() }).optional()
21129
21159
  }).optional()
21130
21160
  }));
21131
21161
  HookConflictEventSchema2 = external_exports2.compile(BaseEventSchema2.extend({
@@ -21135,7 +21165,18 @@ var init_events2 = __esm({
21135
21165
  token: external_exports2.string(),
21136
21166
  // TODO: Make this required once all persisted hook_conflict events and
21137
21167
  // remote World implementations always include the active hook owner's run ID.
21138
- conflictingRunId: external_exports2.string().optional()
21168
+ conflictingRunId: external_exports2.string().optional(),
21169
+ /**
21170
+ * Set when the creation asked for `force` and the World declined to take
21171
+ * the token over. `victim-spec-version`: the run holding the token was
21172
+ * started at a spec version below
21173
+ * `SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM`, so its runtime would not
21174
+ * understand the involuntary disposal — the forced hook gets the
21175
+ * ordinary `HookConflictError` it opted out of instead of stranding
21176
+ * that run. Absent on a conflict answered by a World that does not
21177
+ * implement forcing at all.
21178
+ */
21179
+ forceRefusedReason: external_exports2.literal("victim-spec-version").optional()
21139
21180
  })
21140
21181
  }));
21141
21182
  NoopEventSchema2 = external_exports2.compile(BaseEventSchema2.extend({
@@ -22035,7 +22076,7 @@ var init_queue2 = __esm({
22035
22076
  });
22036
22077
 
22037
22078
  // ../world/dist/hooks.js
22038
- var HookResumeContextSchema2, HOOK_RESUME_INPUT_VERSION2, HOOK_RESUME_DEDUP_VERSION2, HookResumeCapabilitiesSchema2, HookSchema2;
22079
+ var HookResumeContextSchema2, HOOK_RESUME_INPUT_VERSION2, HOOK_RESUME_DEDUP_VERSION2, HookResumeCapabilitiesSchema2, HookClaimedFromSchema2, HookSchema2;
22039
22080
  var init_hooks2 = __esm({
22040
22081
  "../world/dist/hooks.js"() {
22041
22082
  "use strict";
@@ -22076,6 +22117,13 @@ var init_hooks2 = __esm({
22076
22117
  // is engaged.
22077
22118
  hookResumeDedupVersion: external_exports2.number()
22078
22119
  }));
22120
+ HookClaimedFromSchema2 = external_exports2.compile(external_exports2.object({
22121
+ runId: external_exports2.string(),
22122
+ hookId: external_exports2.string(),
22123
+ workflowName: external_exports2.string().optional(),
22124
+ deploymentId: external_exports2.string().optional(),
22125
+ runSpecVersion: external_exports2.number().optional()
22126
+ }));
22079
22127
  HookSchema2 = external_exports2.compile(external_exports2.object({
22080
22128
  runId: external_exports2.string(),
22081
22129
  hookId: external_exports2.string(),
@@ -22103,7 +22151,12 @@ var init_hooks2 = __esm({
22103
22151
  // `resumeHook()` gates its lazy path on this being present and
22104
22152
  // current. Absent against an older/rolled-back server or when the kill switch
22105
22153
  // is active.
22106
- resumeCapabilities: HookResumeCapabilitiesSchema2.optional()
22154
+ resumeCapabilities: HookResumeCapabilitiesSchema2.optional(),
22155
+ // Set when this hook took its token from another run
22156
+ // (`experimental_force`). The wake-targeting fields are the victim run's,
22157
+ // so the claimer's runtime can publish the victim's wake without reading
22158
+ // a run it may not be able to reach; see `HookClaimedFromSchema`.
22159
+ claimedFrom: HookClaimedFromSchema2.optional()
22107
22160
  }));
22108
22161
  }
22109
22162
  });
@@ -22276,7 +22329,7 @@ function requiresNewerWorld(v) {
22276
22329
  return false;
22277
22330
  return v > SPEC_VERSION_MAX_SUPPORTED2;
22278
22331
  }
22279
- var SPEC_VERSION_LEGACY2, SPEC_VERSION_SUPPORTS_EVENT_SOURCING, SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT2, SPEC_VERSION_SUPPORTS_ATTRIBUTES2, SPEC_VERSION_SUPPORTS_COMPRESSION2, SPEC_VERSION_SUPPORTS_SLOT_IDENTITY2, SPEC_VERSION_SUPPORTS_SEALED_LOG2, SPEC_VERSION_CURRENT2, SEALED_LOG_ENV_VAR, SPEC_VERSION_MAX_SUPPORTED2;
22332
+ var SPEC_VERSION_LEGACY2, SPEC_VERSION_SUPPORTS_EVENT_SOURCING, SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT2, SPEC_VERSION_SUPPORTS_ATTRIBUTES2, SPEC_VERSION_SUPPORTS_COMPRESSION2, SPEC_VERSION_SUPPORTS_SLOT_IDENTITY2, SPEC_VERSION_SUPPORTS_SEALED_LOG, SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM2, SPEC_VERSION_CURRENT2, SEALED_LOG_ENV_VAR, SPEC_VERSION_MAX_SUPPORTED2;
22280
22333
  var init_spec_version2 = __esm({
22281
22334
  "../world/dist/spec-version.js"() {
22282
22335
  "use strict";
@@ -22287,11 +22340,12 @@ var init_spec_version2 = __esm({
22287
22340
  SPEC_VERSION_SUPPORTS_ATTRIBUTES2 = 4;
22288
22341
  SPEC_VERSION_SUPPORTS_COMPRESSION2 = 5;
22289
22342
  SPEC_VERSION_SUPPORTS_SLOT_IDENTITY2 = 6;
22290
- SPEC_VERSION_SUPPORTS_SEALED_LOG2 = 7;
22291
- SPEC_VERSION_CURRENT2 = SPEC_VERSION_SUPPORTS_SEALED_LOG2;
22343
+ SPEC_VERSION_SUPPORTS_SEALED_LOG = 7;
22344
+ SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM2 = 8;
22345
+ SPEC_VERSION_CURRENT2 = SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM2;
22292
22346
  SEALED_LOG_ENV_VAR = "WORKFLOW_SEALED_LOG";
22293
22347
  __name(mintedSpecVersion, "mintedSpecVersion");
22294
- SPEC_VERSION_MAX_SUPPORTED2 = SPEC_VERSION_SUPPORTS_SEALED_LOG2;
22348
+ SPEC_VERSION_MAX_SUPPORTED2 = SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM2;
22295
22349
  __name(isLegacySpecVersion2, "isLegacySpecVersion");
22296
22350
  __name(requiresNewerWorld, "requiresNewerWorld");
22297
22351
  }
@@ -22557,6 +22611,7 @@ __export(dist_exports, {
22557
22611
  HOOK_RESUME_DEDUP_VERSION: () => HOOK_RESUME_DEDUP_VERSION2,
22558
22612
  HOOK_RESUME_INPUT_VERSION: () => HOOK_RESUME_INPUT_VERSION2,
22559
22613
  HealthCheckPayloadSchema: () => HealthCheckPayloadSchema2,
22614
+ HookClaimedFromSchema: () => HookClaimedFromSchema2,
22560
22615
  HookCreatedEventSchema: () => HookCreatedEventSchema2,
22561
22616
  HookResumeCapabilitiesSchema: () => HookResumeCapabilitiesSchema2,
22562
22617
  HookSchema: () => HookSchema2,
@@ -22585,7 +22640,8 @@ __export(dist_exports, {
22585
22640
  SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT: () => SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT2,
22586
22641
  SPEC_VERSION_SUPPORTS_COMPRESSION: () => SPEC_VERSION_SUPPORTS_COMPRESSION2,
22587
22642
  SPEC_VERSION_SUPPORTS_EVENT_SOURCING: () => SPEC_VERSION_SUPPORTS_EVENT_SOURCING,
22588
- SPEC_VERSION_SUPPORTS_SEALED_LOG: () => SPEC_VERSION_SUPPORTS_SEALED_LOG2,
22643
+ SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM: () => SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM2,
22644
+ SPEC_VERSION_SUPPORTS_SEALED_LOG: () => SPEC_VERSION_SUPPORTS_SEALED_LOG,
22589
22645
  SPEC_VERSION_SUPPORTS_SLOT_IDENTITY: () => SPEC_VERSION_SUPPORTS_SLOT_IDENTITY2,
22590
22646
  STEP_EVENT_TYPES: () => STEP_EVENT_TYPES2,
22591
22647
  SerializedDataSchema: () => SerializedDataSchema2,
@@ -22730,7 +22786,7 @@ function buildFramedDetails2(hint3, slug) {
22730
22786
  out.push({ label: "docs", value: `${BASE_URL2}/${slug}` });
22731
22787
  return out;
22732
22788
  }
22733
- var BASE_URL2, ERROR_SLUGS2, WorkflowError2, WorkflowWorldError2, WorkflowRunFailedError2, WorkflowRunNotCompletedError2, WorkflowRuntimeError2, CorruptedEventLogError, ReplayDivergenceError, MaxEventsExceededError, RuntimeDecryptionError2, SerializationError2, StepNotRegisteredError2, WorkflowNotRegisteredError2, WorkflowDeploymentMismatchError, WorkflowRunNotFoundError2, HookConflictError2, HookNotFoundError2, EntityConflictError2, RunExpiredError2, StreamError2, StreamExpiredError2, TooEarlyError, ThrottleError2, PreconditionFailedError, WorkflowRunCancelledError2, RunNotSupportedError, FatalError2, RetryableError2, FATAL_ERROR_KEY2, RETRYABLE_ERROR_KEY2, HOOK_CONFLICT_ERROR_KEY2, RUNTIME_DECRYPTION_ERROR_KEY2, STREAM_ERROR_KEY2;
22789
+ var BASE_URL2, ERROR_SLUGS2, WorkflowError2, WorkflowWorldError2, WorkflowRunFailedError2, WorkflowRunNotCompletedError2, WorkflowRuntimeError2, CorruptedEventLogError, ReplayDivergenceError, MaxEventsExceededError, RuntimeDecryptionError2, SerializationError2, StepNotRegisteredError2, WorkflowNotRegisteredError2, WorkflowDeploymentMismatchError, WorkflowRunNotFoundError2, HookConflictError2, HookForceClaimedError2, HookNotFoundError2, EntityConflictError2, RunExpiredError2, StreamError2, StreamExpiredError2, TooEarlyError, ThrottleError2, PreconditionFailedError, WorkflowRunCancelledError2, RunNotSupportedError, FatalError2, RetryableError2, FATAL_ERROR_KEY2, RETRYABLE_ERROR_KEY2, HOOK_CONFLICT_ERROR_KEY2, HOOK_FORCE_CLAIMED_ERROR_KEY2, RUNTIME_DECRYPTION_ERROR_KEY2, STREAM_ERROR_KEY2;
22734
22790
  var init_dist7 = __esm({
22735
22791
  "../errors/dist/index.js"() {
22736
22792
  "use strict";
@@ -22750,6 +22806,7 @@ var init_dist7 = __esm({
22750
22806
  FETCH_IN_WORKFLOW_FUNCTION: "fetch-in-workflow",
22751
22807
  TIMEOUT_FUNCTIONS_IN_WORKFLOW: "timeout-in-workflow",
22752
22808
  HOOK_CONFLICT: "hook-conflict",
22809
+ HOOK_FORCE_CLAIMED: "hook-force-claimed",
22753
22810
  CORRUPTED_EVENT_LOG: "corrupted-event-log",
22754
22811
  REPLAY_DIVERGENCE: "replay-divergence",
22755
22812
  STEP_NOT_REGISTERED: "step-not-registered",
@@ -23043,6 +23100,28 @@ Caused by: ${options.cause.stack}`;
23043
23100
  return isError2(value) && value.name === "HookConflictError";
23044
23101
  }
23045
23102
  };
23103
+ HookForceClaimedError2 = class extends WorkflowError2 {
23104
+ static {
23105
+ __name(this, "HookForceClaimedError");
23106
+ }
23107
+ token;
23108
+ /** The run that took the token. */
23109
+ claimedByRunId;
23110
+ /** The hook (in `claimedByRunId`) the token now belongs to. */
23111
+ claimedByHookId;
23112
+ constructor(token, claimedByRunId, claimedByHookId) {
23113
+ super(`Hook token "${token}" was force-claimed by another workflow (run "${claimedByRunId}")`, { slug: ERROR_SLUGS2.HOOK_FORCE_CLAIMED });
23114
+ this.name = "HookForceClaimedError";
23115
+ this.token = token;
23116
+ this.claimedByRunId = claimedByRunId;
23117
+ if (claimedByHookId !== void 0) {
23118
+ this.claimedByHookId = claimedByHookId;
23119
+ }
23120
+ }
23121
+ static is(value) {
23122
+ return isError2(value) && value.name === "HookForceClaimedError";
23123
+ }
23124
+ };
23046
23125
  HookNotFoundError2 = class extends WorkflowError2 {
23047
23126
  static {
23048
23127
  __name(this, "HookNotFoundError");
@@ -23233,6 +23312,7 @@ Caused by: ${options.cause.stack}`;
23233
23312
  FATAL_ERROR_KEY2 = /* @__PURE__ */ Symbol.for("@workflow/errors//FatalError");
23234
23313
  RETRYABLE_ERROR_KEY2 = /* @__PURE__ */ Symbol.for("@workflow/errors//RetryableError");
23235
23314
  HOOK_CONFLICT_ERROR_KEY2 = /* @__PURE__ */ Symbol.for("@workflow/errors//HookConflictError");
23315
+ HOOK_FORCE_CLAIMED_ERROR_KEY2 = /* @__PURE__ */ Symbol.for("@workflow/errors//HookForceClaimedError");
23236
23316
  RUNTIME_DECRYPTION_ERROR_KEY2 = /* @__PURE__ */ Symbol.for("@workflow/errors//RuntimeDecryptionError");
23237
23317
  STREAM_ERROR_KEY2 = /* @__PURE__ */ Symbol.for("@workflow/errors//StreamError");
23238
23318
  if (typeof globalThis !== "undefined") {
@@ -23260,6 +23340,14 @@ Caused by: ${options.cause.stack}`;
23260
23340
  configurable: false
23261
23341
  });
23262
23342
  }
23343
+ if (!Object.hasOwn(globalThis, HOOK_FORCE_CLAIMED_ERROR_KEY2)) {
23344
+ Object.defineProperty(globalThis, HOOK_FORCE_CLAIMED_ERROR_KEY2, {
23345
+ value: HookForceClaimedError2,
23346
+ writable: false,
23347
+ enumerable: false,
23348
+ configurable: false
23349
+ });
23350
+ }
23263
23351
  if (!Object.hasOwn(globalThis, RUNTIME_DECRYPTION_ERROR_KEY2)) {
23264
23352
  Object.defineProperty(globalThis, RUNTIME_DECRYPTION_ERROR_KEY2, {
23265
23353
  value: RuntimeDecryptionError2,
@@ -54921,6 +55009,35 @@ function hookDisposeLockPath(basedir, hookId, tag) {
54921
55009
  const name = tag ? `${hookId}.disposed.${tag}` : `${hookId}.disposed`;
54922
55010
  return resolveWithinBase(basedir, ".locks", "hooks", name);
54923
55011
  }
55012
+ async function readHookDisposeLock(basedir, hookId, tag) {
55013
+ const candidates = [hookDisposeLockPath(basedir, hookId)];
55014
+ if (tag) {
55015
+ candidates.push(hookDisposeLockPath(basedir, hookId, tag));
55016
+ }
55017
+ for (const lockPath of candidates) {
55018
+ let content;
55019
+ try {
55020
+ content = await fs2.readFile(lockPath, "utf8");
55021
+ } catch {
55022
+ continue;
55023
+ }
55024
+ if (content.trim() === "")
55025
+ return { committed: true };
55026
+ try {
55027
+ const parsed = JSON.parse(content);
55028
+ const by = parsed.forceClaimedBy;
55029
+ if (by && typeof by.runId === "string" && typeof by.hookId === "string") {
55030
+ return {
55031
+ committed: true,
55032
+ forceClaimedBy: { runId: by.runId, hookId: by.hookId }
55033
+ };
55034
+ }
55035
+ } catch {
55036
+ }
55037
+ return { committed: true };
55038
+ }
55039
+ return { committed: false };
55040
+ }
54924
55041
  async function isHookDisposalCommitted(basedir, hookId, tag) {
54925
55042
  const candidates = [hookDisposeLockPath(basedir, hookId)];
54926
55043
  if (tag) {
@@ -55136,6 +55253,7 @@ var init_helpers2 = __esm({
55136
55253
  init_fs();
55137
55254
  __name(hashToken, "hashToken");
55138
55255
  __name(hookDisposeLockPath, "hookDisposeLockPath");
55256
+ __name(readHookDisposeLock, "readHookDisposeLock");
55139
55257
  __name(isHookDisposalCommitted, "isHookDisposalCommitted");
55140
55258
  __name(runTerminalMarkerPath, "runTerminalMarkerPath");
55141
55259
  __name(isRunTerminalCommitted, "isRunTerminalCommitted");
@@ -55153,7 +55271,17 @@ var init_helpers2 = __esm({
55153
55271
  // Legacy claims also omitted eventId. Their recovery marker pins the
55154
55272
  // canonical hook_created event before concurrent retries publish it.
55155
55273
  eventId: external_exports2.string().optional(),
55156
- tokenRetentionUntil: external_exports2.coerce.date().optional()
55274
+ tokenRetentionUntil: external_exports2.coerce.date().optional(),
55275
+ // Set when this claim took the token from another run
55276
+ // (`experimental_force`), so a retry that adopts the claim can still
55277
+ // report — and wake — the run it came from.
55278
+ claimedFrom: external_exports2.object({
55279
+ runId: external_exports2.string(),
55280
+ hookId: external_exports2.string(),
55281
+ workflowName: external_exports2.string().optional(),
55282
+ deploymentId: external_exports2.string().optional(),
55283
+ runSpecVersion: external_exports2.number().optional()
55284
+ }).optional()
55157
55285
  }));
55158
55286
  __name(readHookTokenClaim, "readHookTokenClaim");
55159
55287
  __name(withHookTokenClaimLock, "withHookTokenClaimLock");
@@ -55217,7 +55345,7 @@ async function writeHookCreatedIndexEntries(basedir, token, runId, hookId, event
55217
55345
  assertSafeEntityId("eventId", eventId);
55218
55346
  if (tag !== void 0)
55219
55347
  assertSafeEntityId("tag", tag);
55220
- const fileName = tag ? `${eventId}.${tag}.json` : `${eventId}.json`;
55348
+ const fileName = tag ? `${runId}-${eventId}.${tag}.json` : `${runId}-${eventId}.json`;
55221
55349
  const content = JSON.stringify({ runId });
55222
55350
  await Promise.all([
55223
55351
  writeExclusive(path5.join(tokenIndexDir(basedir, token), fileName), content),
@@ -55324,7 +55452,7 @@ async function ensureHookIndexesImpl(basedir) {
55324
55452
  });
55325
55453
  await writeExclusive(markerPath, "");
55326
55454
  }
55327
- async function findNewestIndexedHookCreatedEvent(basedir, index, matches, tag) {
55455
+ async function findIndexedHookCreatedEvent(basedir, index, accept, tag) {
55328
55456
  await ensureHookIndexes(basedir);
55329
55457
  let dir;
55330
55458
  try {
@@ -55332,6 +55460,10 @@ async function findNewestIndexedHookCreatedEvent(basedir, index, matches, tag) {
55332
55460
  } catch {
55333
55461
  return null;
55334
55462
  }
55463
+ const eventIdOf = /* @__PURE__ */ __name((entryId, runId) => {
55464
+ const stripped = stripTag(entryId);
55465
+ return stripped.startsWith(`${runId}-`) ? stripped.slice(runId.length + 1) : stripped;
55466
+ }, "eventIdOf");
55335
55467
  const entryIds = (await listJSONFiles(dir)).filter((fileId) => isVisibleToTag(fileId, tag)).sort((a, b) => stripTag(b).localeCompare(stripTag(a)));
55336
55468
  for (const entryId of entryIds) {
55337
55469
  let entry = null;
@@ -55344,7 +55476,7 @@ async function findNewestIndexedHookCreatedEvent(basedir, index, matches, tag) {
55344
55476
  }
55345
55477
  if (!entry)
55346
55478
  continue;
55347
- const eventId = stripTag(entryId);
55479
+ const eventId = eventIdOf(entryId, entry.runId);
55348
55480
  let eventPath;
55349
55481
  try {
55350
55482
  eventPath = taggedPath(basedir, "events", `${entry.runId}-${eventId}`, tagOf(entryId));
@@ -55358,7 +55490,7 @@ async function findNewestIndexedHookCreatedEvent(basedir, index, matches, tag) {
55358
55490
  continue;
55359
55491
  if (typeof event.correlationId !== "string")
55360
55492
  continue;
55361
- if (!matches(event))
55493
+ if (!await accept(event))
55362
55494
  continue;
55363
55495
  return event;
55364
55496
  }
@@ -55398,7 +55530,7 @@ var init_hook_index = __esm({
55398
55530
  __name(ensureHookIndexes, "ensureHookIndexes");
55399
55531
  __name(forEachConcurrent, "forEachConcurrent");
55400
55532
  __name(ensureHookIndexesImpl, "ensureHookIndexesImpl");
55401
- __name(findNewestIndexedHookCreatedEvent, "findNewestIndexedHookCreatedEvent");
55533
+ __name(findIndexedHookCreatedEvent, "findIndexedHookCreatedEvent");
55402
55534
  }
55403
55535
  });
55404
55536
 
@@ -55449,11 +55581,14 @@ function getHookCreatedToken(event) {
55449
55581
  return typeof token === "string" ? token : void 0;
55450
55582
  }
55451
55583
  function hookFromCreatedEvent(event) {
55452
- const { token, metadata, isWebhook, isSystem, tokenRetentionUntil } = event.eventData;
55584
+ const { token, metadata, isWebhook, isSystem, tokenRetentionUntil, forceClaimedFrom } = event.eventData;
55453
55585
  return {
55454
55586
  runId: event.runId,
55455
55587
  hookId: event.correlationId,
55456
55588
  token,
55589
+ // The journaled row is the durable record of a takeover; a rebuilt
55590
+ // entity must say where its token came from just as the original did.
55591
+ ...forceClaimedFrom !== void 0 && { claimedFrom: forceClaimedFrom },
55457
55592
  metadata,
55458
55593
  ownerId: "local-owner",
55459
55594
  projectId: "local-project",
@@ -55473,20 +55608,17 @@ async function isTerminalRunCache(basedir, runId, tag) {
55473
55608
  return run3 ? isTerminalWorkflowRunStatus2(run3.status) : false;
55474
55609
  }
55475
55610
  async function findAvailableHookCreatedEvent(basedir, index, matches, tag) {
55476
- const newest = await findNewestIndexedHookCreatedEvent(basedir, index, (event) => isMatchingHookCreatedEvent(event, matches), tag);
55477
- if (!newest || !isMatchingHookCreatedEvent(newest, matches)) {
55478
- return null;
55479
- }
55480
- if (await isTerminalRunCache(basedir, newest.runId, tag)) {
55481
- const retainedUntil = newest.eventData.tokenRetentionUntil;
55482
- if (!retainedUntil || retainedUntil.getTime() <= Date.now()) {
55483
- return null;
55611
+ const isLive = /* @__PURE__ */ __name(async (event) => {
55612
+ if (await isTerminalRunCache(basedir, event.runId, tag)) {
55613
+ const retainedUntil = event.eventData.tokenRetentionUntil;
55614
+ if (!retainedUntil || retainedUntil.getTime() <= Date.now()) {
55615
+ return false;
55616
+ }
55484
55617
  }
55485
- }
55486
- if (await isHookDisposalCommitted(basedir, newest.correlationId, tag)) {
55487
- return null;
55488
- }
55489
- return newest;
55618
+ return !await isHookDisposalCommitted(basedir, event.correlationId, tag);
55619
+ }, "isLive");
55620
+ const found2 = await findIndexedHookCreatedEvent(basedir, index, (event) => isMatchingHookCreatedEvent(event, matches) && isLive(event), tag);
55621
+ return found2 && isMatchingHookCreatedEvent(found2, matches) ? found2 : null;
55490
55622
  }
55491
55623
  async function restoreHookCachesFromEvent(basedir, event, tag) {
55492
55624
  const hook = hookFromCreatedEvent(event);
@@ -55496,7 +55628,8 @@ async function restoreHookCachesFromEvent(basedir, event, tag) {
55496
55628
  hookId: hook.hookId,
55497
55629
  runId: hook.runId,
55498
55630
  eventId: event.eventId,
55499
- tokenRetentionUntil: event.eventData.tokenRetentionUntil
55631
+ tokenRetentionUntil: event.eventData.tokenRetentionUntil,
55632
+ ...hook.claimedFrom && { claimedFrom: hook.claimedFrom }
55500
55633
  }));
55501
55634
  await writeHookByRunMarker(basedir, hook.runId, hook.hookId, tag);
55502
55635
  await writeExclusive(taggedPath(basedir, "hooks", hook.hookId, tag), JSON.stringify(hook, jsonReplacer, 2));
@@ -55521,36 +55654,53 @@ function createHooksStorage(basedir, tag) {
55521
55654
  return !await isTerminalRunCache(basedir, hook.runId, tag);
55522
55655
  }
55523
55656
  __name(isHookAvailable, "isHookAvailable");
55657
+ async function isForceDisposed(hook) {
55658
+ const lock2 = await readHookDisposeLock(basedir, hook.hookId, tag);
55659
+ return lock2.committed && lock2.forceClaimedBy !== void 0;
55660
+ }
55661
+ __name(isForceDisposed, "isForceDisposed");
55524
55662
  async function findHookByToken(token) {
55525
55663
  const claim3 = await readHookTokenClaim(hookTokenClaimPath(basedir, token));
55526
55664
  if (claim3?.hookId) {
55527
55665
  try {
55528
55666
  const hook = await readJSONWithFallback(basedir, "hooks", claim3.hookId, HookSchema2, tag);
55529
55667
  if (hook?.token === token) {
55530
- if (!await isHookAvailable(hook)) {
55668
+ if (await isHookAvailable(hook)) {
55669
+ return { ...hook, isWebhook: hook.isWebhook ?? true };
55670
+ }
55671
+ if (!await isForceDisposed(hook)) {
55531
55672
  throw new HookNotFoundError2(token);
55532
55673
  }
55533
- return { ...hook, isWebhook: hook.isWebhook ?? true };
55674
+ } else if (hook) {
55675
+ return null;
55534
55676
  }
55535
55677
  } catch (error121) {
55536
55678
  if (!UnsafeEntityIdError.is(error121)) {
55537
55679
  throw error121;
55538
55680
  }
55681
+ return null;
55539
55682
  }
55540
- return null;
55541
55683
  }
55542
55684
  const hooksDir = path6.join(basedir, "hooks");
55543
55685
  const files = await listJSONFiles(hooksDir);
55686
+ let disposedMatch = false;
55544
55687
  for (const file3 of files) {
55545
55688
  const hookPath = path6.join(hooksDir, `${file3}.json`);
55546
55689
  const hook = await readJSON(hookPath, HookSchema2);
55547
55690
  if (hook?.token === token) {
55548
- if (!await isHookAvailable(hook)) {
55549
- throw new HookNotFoundError2(token);
55691
+ if (await isHookAvailable(hook)) {
55692
+ return { ...hook, isWebhook: hook.isWebhook ?? true };
55693
+ }
55694
+ if (await isForceDisposed(hook)) {
55695
+ disposedMatch = true;
55696
+ continue;
55550
55697
  }
55551
- return { ...hook, isWebhook: hook.isWebhook ?? true };
55698
+ throw new HookNotFoundError2(token);
55552
55699
  }
55553
55700
  }
55701
+ if (disposedMatch) {
55702
+ throw new HookNotFoundError2(token);
55703
+ }
55554
55704
  return null;
55555
55705
  }
55556
55706
  __name(findHookByToken, "findHookByToken");
@@ -56816,6 +56966,16 @@ function createEventsStorage(basedir, tag) {
56816
56966
  }
56817
56967
  }
56818
56968
  __name(storeEvent, "storeEvent");
56969
+ async function refuseDisposedHookDelivery(hookId, token) {
56970
+ const lock2 = await readHookDisposeLock(basedir, hookId, tag);
56971
+ if (!lock2.committed)
56972
+ return;
56973
+ if (lock2.forceClaimedBy) {
56974
+ throw new HookForceClaimedError2(typeof token === "string" ? token : "", lock2.forceClaimedBy.runId, lock2.forceClaimedBy.hookId);
56975
+ }
56976
+ throw new HookNotFoundError2(hookId);
56977
+ }
56978
+ __name(refuseDisposedHookDelivery, "refuseDisposedHookDelivery");
56819
56979
  const queryRunEvents = /* @__PURE__ */ __name((runId, pagination) => paginatedFileSystemQuery({
56820
56980
  directory: path10.join(basedir, "events"),
56821
56981
  schema: ReadEventSchema,
@@ -57006,8 +57166,8 @@ function createEventsStorage(basedir, tag) {
57006
57166
  }
57007
57167
  }
57008
57168
  }
57009
- if (data.eventType === "hook_received" && await isHookDisposalCommitted(basedir, data.correlationId, tag)) {
57010
- throw new HookNotFoundError2(data.correlationId);
57169
+ if (data.eventType === "hook_received") {
57170
+ await refuseDisposedHookDelivery(data.correlationId, data.eventData?.token);
57011
57171
  }
57012
57172
  const existingHook = await readJSONWithFallback(basedir, "hooks", data.correlationId, HookSchema2, tag);
57013
57173
  if (!existingHook) {
@@ -57066,6 +57226,7 @@ function createEventsStorage(basedir, tag) {
57066
57226
  }
57067
57227
  }
57068
57228
  }
57229
+ let prePublishedEvent;
57069
57230
  let event = {
57070
57231
  ...data,
57071
57232
  runId: effectiveRunId,
@@ -57437,13 +57598,15 @@ function createEventsStorage(basedir, tag) {
57437
57598
  ) {
57438
57599
  const hookData = data.eventData;
57439
57600
  const constraintPath = hookTokenClaimPath(basedir, hookData.token);
57440
- const claimContent = JSON.stringify({
57601
+ let claimedFrom;
57602
+ const claimContent = /* @__PURE__ */ __name(() => JSON.stringify({
57441
57603
  token: hookData.token,
57442
57604
  hookId: data.correlationId,
57443
57605
  runId: effectiveRunId,
57444
57606
  eventId,
57445
- tokenRetentionUntil: hookData.tokenRetentionUntil
57446
- });
57607
+ tokenRetentionUntil: hookData.tokenRetentionUntil,
57608
+ ...claimedFrom && { claimedFrom }
57609
+ }), "claimContent");
57447
57610
  const claimResult = await withHookTokenClaimLock(basedir, hookData.token, async (signal) => {
57448
57611
  let existingClaim = await readHookTokenClaim(constraintPath);
57449
57612
  if (!existingClaim) {
@@ -57454,26 +57617,114 @@ function createEventsStorage(basedir, tag) {
57454
57617
  }
57455
57618
  if (!existingClaim) {
57456
57619
  signal.throwIfAborted();
57457
- assert3(await writeExclusive(constraintPath, claimContent));
57620
+ assert3(await writeExclusive(constraintPath, claimContent()));
57458
57621
  return { status: "claimed" };
57459
57622
  }
57460
57623
  if (existingClaim.runId === effectiveRunId && existingClaim.hookId === data.correlationId) {
57461
57624
  return { status: "owned", claim: existingClaim };
57462
57625
  }
57463
57626
  if (!await isHookTokenClaimReleasable(basedir, existingClaim, tag)) {
57464
- return { status: "conflict", claim: existingClaim };
57627
+ if (hookData.force !== true || !existingClaim.hookId) {
57628
+ return { status: "conflict", claim: existingClaim };
57629
+ }
57630
+ signal.throwIfAborted();
57631
+ const victimRun = await readJSONWithFallback(basedir, "runs", existingClaim.runId, WorkflowRunSchema2, tag);
57632
+ const victimRunning = victimRun !== null && !isTerminalWorkflowRunStatus2(victimRun.status);
57633
+ claimedFrom = {
57634
+ runId: existingClaim.runId,
57635
+ hookId: existingClaim.hookId,
57636
+ ...victimRunning && {
57637
+ workflowName: victimRun.workflowName,
57638
+ deploymentId: victimRun.deploymentId,
57639
+ runSpecVersion: victimRun.specVersion
57640
+ }
57641
+ };
57642
+ if (victimRunning && (victimRun.specVersion ?? SPEC_VERSION_LEGACY2) < SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM2) {
57643
+ return {
57644
+ status: "conflict",
57645
+ claim: existingClaim,
57646
+ forceRefusedReason: "victim-spec-version"
57647
+ };
57648
+ }
57649
+ const journaledEventId = await findExistingHookCreatedEventId(basedir, effectiveRunId, data.correlationId);
57650
+ if (journaledEventId) {
57651
+ eventId = journaledEventId;
57652
+ prePublishedEvent = { ...event, eventId };
57653
+ } else {
57654
+ prePublishedEvent = await storeEvent({
57655
+ ...event,
57656
+ eventData: {
57657
+ ...event.eventData,
57658
+ forceClaimedFrom: claimedFrom
57659
+ }
57660
+ });
57661
+ eventId = prePublishedEvent.eventId;
57662
+ }
57663
+ await writeHookByRunMarker(basedir, effectiveRunId, data.correlationId, tag);
57664
+ await writeJSON(taggedPath(basedir, "hooks", data.correlationId, tag), {
57665
+ runId: effectiveRunId,
57666
+ hookId: data.correlationId,
57667
+ token: hookData.token,
57668
+ metadata: hookData.metadata,
57669
+ ownerId: "local-owner",
57670
+ projectId: "local-project",
57671
+ environment: "local",
57672
+ createdAt: now2,
57673
+ specVersion: effectiveSpecVersion,
57674
+ isWebhook: hookData.isWebhook ?? false,
57675
+ isSystem: hookData.isSystem ?? false,
57676
+ tokenRetentionUntil: hookData.tokenRetentionUntil,
57677
+ claimedFrom
57678
+ }, { overwrite: true });
57679
+ const lockWritten = await writeExclusive(hookDisposeLockPath(basedir, existingClaim.hookId, tag), JSON.stringify({
57680
+ forceClaimedBy: {
57681
+ runId: effectiveRunId,
57682
+ hookId: data.correlationId
57683
+ }
57684
+ }));
57685
+ if (victimRunning) {
57686
+ if (lockWritten) {
57687
+ const disposedEvent = {
57688
+ eventType: "hook_disposed",
57689
+ correlationId: existingClaim.hookId,
57690
+ eventData: {
57691
+ token: hookData.token,
57692
+ forceClaimedBy: {
57693
+ runId: effectiveRunId,
57694
+ hookId: data.correlationId
57695
+ }
57696
+ },
57697
+ runId: existingClaim.runId,
57698
+ eventId: await mintEventId(existingClaim.runId),
57699
+ createdAt: /* @__PURE__ */ new Date(),
57700
+ specVersion: victimRun.specVersion
57701
+ };
57702
+ await storeEvent(disposedEvent);
57703
+ }
57704
+ }
57465
57705
  }
57466
57706
  signal.throwIfAborted();
57467
- await deleteJSON(constraintPath);
57707
+ if (claimedFrom) {
57708
+ await write(constraintPath, claimContent(), {
57709
+ overwrite: true
57710
+ });
57711
+ } else {
57712
+ await deleteJSON(constraintPath);
57713
+ }
57468
57714
  if (existingClaim.hookId) {
57469
57715
  await deleteJSON(taggedPath(basedir, "hooks", existingClaim.hookId, tag));
57470
57716
  await deleteJSON(hookRecoveryMarkerPath(basedir, hookData.token, existingClaim.runId, existingClaim.hookId));
57471
57717
  await deleteHookByRunMarker(basedir, existingClaim.runId, existingClaim.hookId, tag);
57472
57718
  }
57473
57719
  signal.throwIfAborted();
57474
- assert3(await writeExclusive(constraintPath, claimContent));
57720
+ if (!claimedFrom) {
57721
+ assert3(await writeExclusive(constraintPath, claimContent()));
57722
+ }
57475
57723
  return { status: "claimed" };
57476
57724
  });
57725
+ if (claimResult.status === "owned" && claimResult.claim.claimedFrom) {
57726
+ claimedFrom = claimResult.claim.claimedFrom;
57727
+ }
57477
57728
  if (claimResult.status === "owned") {
57478
57729
  const existingClaim = claimResult.claim;
57479
57730
  let canonicalEventId;
@@ -57515,7 +57766,10 @@ function createEventsStorage(basedir, tag) {
57515
57766
  correlationId: data.correlationId,
57516
57767
  eventData: {
57517
57768
  token: hookData.token,
57518
- conflictingRunId: existingClaim.runId
57769
+ conflictingRunId: existingClaim.runId,
57770
+ ...claimResult.forceRefusedReason !== void 0 && {
57771
+ forceRefusedReason: claimResult.forceRefusedReason
57772
+ }
57519
57773
  },
57520
57774
  runId: effectiveRunId,
57521
57775
  eventId,
@@ -57543,6 +57797,18 @@ function createEventsStorage(basedir, tag) {
57543
57797
  hasMore: conflictDelta.hasMore
57544
57798
  };
57545
57799
  }
57800
+ if (claimedFrom) {
57801
+ event = {
57802
+ ...event,
57803
+ eventData: {
57804
+ ...event.eventData,
57805
+ // Wake-targeting fields included: a replay of this run
57806
+ // republishes the victim's wake from this row alone when the
57807
+ // invocation that created the hook died before waking it.
57808
+ forceClaimedFrom: claimedFrom
57809
+ }
57810
+ };
57811
+ }
57546
57812
  const persistedHookData = event.eventData;
57547
57813
  hook = {
57548
57814
  runId: effectiveRunId,
@@ -57557,9 +57823,10 @@ function createEventsStorage(basedir, tag) {
57557
57823
  specVersion: effectiveSpecVersion,
57558
57824
  isWebhook: persistedHookData.isWebhook ?? false,
57559
57825
  isSystem: persistedHookData.isSystem ?? false,
57560
- tokenRetentionUntil: persistedHookData.tokenRetentionUntil
57826
+ tokenRetentionUntil: persistedHookData.tokenRetentionUntil,
57827
+ ...claimedFrom && { claimedFrom }
57561
57828
  };
57562
- hookEntityWriteOptions = claimResult.status === "owned" ? { overwrite: true } : void 0;
57829
+ hookEntityWriteOptions = claimResult.status === "owned" || claimedFrom ? { overwrite: true } : void 0;
57563
57830
  await writeHookCreatedIndexEntries(basedir, hookData.token, effectiveRunId, data.correlationId, eventId, tag);
57564
57831
  } else if (data.eventType === "hook_disposed") {
57565
57832
  const lockPath = hookDisposeLockPath(basedir, data.correlationId, tag);
@@ -57617,8 +57884,14 @@ function createEventsStorage(basedir, tag) {
57617
57884
  };
57618
57885
  await writeJSON(taggedPath(basedir, "waits", waitCompositeKey, tag), wait, { overwrite: true });
57619
57886
  }
57620
- if (data.eventType === "hook_received" && data.correlationId && await isHookDisposalCommitted(basedir, data.correlationId, tag)) {
57621
- throw new HookNotFoundError2(data.correlationId);
57887
+ if (data.eventType === "hook_received" && data.correlationId) {
57888
+ await refuseDisposedHookDelivery(data.correlationId, data.eventData?.token);
57889
+ }
57890
+ if (data.eventType === "hook_created" && data.correlationId) {
57891
+ const own3 = await readHookDisposeLock(basedir, data.correlationId, tag);
57892
+ if (own3.committed && own3.forceClaimedBy) {
57893
+ throw new EntityConflictError2(`Hook "${data.correlationId}" was force-claimed by another run before its creation was journaled`);
57894
+ }
57622
57895
  }
57623
57896
  let eventPath = taggedPath(basedir, "events", `${effectiveRunId}-${eventId}`, tag);
57624
57897
  let serializedEvent = JSON.stringify(event, jsonReplacer, 2);
@@ -57641,7 +57914,13 @@ function createEventsStorage(basedir, tag) {
57641
57914
  return true;
57642
57915
  }, "bumpEventSlot");
57643
57916
  let eventPublished = false;
57644
- for (let attempt = 0; ; attempt++) {
57917
+ if (prePublishedEvent) {
57918
+ event = prePublishedEvent;
57919
+ eventPath = taggedPath(basedir, "events", `${effectiveRunId}-${eventId}`, tag);
57920
+ serializedEvent = JSON.stringify(event, jsonReplacer, 2);
57921
+ eventPublished = true;
57922
+ }
57923
+ for (let attempt = 0; !eventPublished; attempt++) {
57645
57924
  if (data.eventType === "hook_received") {
57646
57925
  const terminalByMarker = await isRunTerminalCommitted(basedir, effectiveRunId, tag);
57647
57926
  const runNow = terminalByMarker ? null : await readJSONWithFallback(basedir, "runs", effectiveRunId, WorkflowRunSchema2, tag);
@@ -58010,7 +58289,11 @@ function createWorld(args) {
58010
58289
  // `(runId, resumeId)` via a filesystem sidecar claim (see
58011
58290
  // events-storage.ts `claimHookResume`), so resumeHook()'s parallel fast
58012
58291
  // path converges on one event in dev exactly as it does on Vercel.
58013
- hookResumeDedup: true
58292
+ hookResumeDedup: true,
58293
+ // The token claim lock serializes the takeover behind
58294
+ // `createHook({ experimental_force: true })`; see the hook_created
58295
+ // branch of storage/events-storage.ts.
58296
+ hookForceClaim: true
58014
58297
  },
58015
58298
  ...queue,
58016
58299
  ...storage,
@@ -61842,7 +62125,7 @@ var version4;
61842
62125
  var init_version3 = __esm({
61843
62126
  "../world-vercel/dist/version.js"() {
61844
62127
  "use strict";
61845
- version4 = "5.0.0-beta.50";
62128
+ version4 = "5.0.0-beta.51";
61846
62129
  }
61847
62130
  });
61848
62131
 
@@ -62424,6 +62707,10 @@ function headersToRecord(headers) {
62424
62707
  }
62425
62708
  function errorForResponse(status, message, opts = {}) {
62426
62709
  const { retryAfter, code: code3, url: url3, mitigated, details } = opts;
62710
+ if (status === 409 && code3 === "hook-force-claimed") {
62711
+ const claimedBy = details && typeof details === "object" ? details.claimedBy : void 0;
62712
+ return new HookForceClaimedError2("", typeof claimedBy?.runId === "string" ? claimedBy.runId : "unknown", typeof claimedBy?.hookId === "string" ? claimedBy.hookId : void 0);
62713
+ }
62427
62714
  if (status === 409)
62428
62715
  return new EntityConflictError2(message);
62429
62716
  if (status === 410) {
@@ -75074,7 +75361,9 @@ function collectErrorMarkers(err, depth = 0) {
75074
75361
  function isRetryableEventPostError(err) {
75075
75362
  if (err instanceof ReplayEventObserverError)
75076
75363
  return false;
75077
- if (EntityConflictError2.is(err) || RunExpiredError2.is(err) || TooEarlyError.is(err) || ThrottleError2.is(err)) {
75364
+ if (EntityConflictError2.is(err) || // A takeover redirect is definitive for THIS target; the runtime's
75365
+ // resume path follows the token instead of re-issuing the write here.
75366
+ HookForceClaimedError2.is(err) || RunExpiredError2.is(err) || TooEarlyError.is(err) || ThrottleError2.is(err)) {
75078
75367
  return false;
75079
75368
  }
75080
75369
  if (WorkflowWorldError2.is(err) || StreamError2.is(err)) {
@@ -75603,6 +75892,8 @@ function buildPostFrameMeta(input3) {
75603
75892
  meta5.hookIsWebhook = input3.hookIsWebhook;
75604
75893
  if (input3.hookIsSystem !== void 0)
75605
75894
  meta5.hookIsSystem = input3.hookIsSystem;
75895
+ if (input3.hookForce !== void 0)
75896
+ meta5.hookForce = input3.hookForce;
75606
75897
  if (input3.resumeId !== void 0)
75607
75898
  meta5.resumeId = input3.resumeId;
75608
75899
  if (input3.errorCode !== void 0)
@@ -75670,8 +75961,13 @@ function errorFromV4Response(statusCode, responseHeaders, errorBody, opName, url
75670
75961
  message = record3.message;
75671
75962
  if (typeof record3.code === "string")
75672
75963
  code3 = record3.code;
75964
+ else if (typeof record3.error === "string")
75965
+ code3 = record3.error;
75673
75966
  if (statusCode === 412)
75674
75967
  details = decodePreconditionDetails(record3);
75968
+ if (statusCode === 409 && code3 === "hook-force-claimed") {
75969
+ details = { claimedBy: record3.claimedBy };
75970
+ }
75675
75971
  } else if (text) {
75676
75972
  message += ` ${text}`;
75677
75973
  }
@@ -76645,6 +76941,9 @@ function splitEventDataForV4(data) {
76645
76941
  if (typeof eventData.isSystem === "boolean") {
76646
76942
  meta5.hookIsSystem = eventData.isSystem;
76647
76943
  }
76944
+ if (eventData.force === true) {
76945
+ meta5.hookForce = true;
76946
+ }
76648
76947
  if (typeof eventData.errorCode === "string") {
76649
76948
  meta5.errorCode = eventData.errorCode;
76650
76949
  }
@@ -76797,6 +77096,10 @@ async function createWorkflowRunEvent(id, data, params, config4) {
76797
77096
  } catch (err) {
76798
77097
  if (err instanceof ReplayEventObserverError)
76799
77098
  throw err.error;
77099
+ if (HookForceClaimedError2.is(err) && data.eventType === "hook_received") {
77100
+ const token = data.eventData?.token;
77101
+ throw new HookForceClaimedError2(typeof token === "string" ? token : err.token, err.claimedByRunId, err.claimedByHookId);
77102
+ }
76800
77103
  if (isHookEventRequiringExistence(data.eventType) && WorkflowWorldError2.is(err) && err.status === 404 && data.correlationId) {
76801
77104
  throw new HookNotFoundError2(data.correlationId);
76802
77105
  }
@@ -78269,7 +78572,15 @@ function createWorld2(config4) {
78269
78572
  maxConcurrency: true,
78270
78573
  // Vercel deployments are atomic and immutable, so a deployment id names
78271
78574
  // one fixed build for its whole lifetime.
78272
- deploymentAffinity: true
78575
+ deploymentAffinity: true,
78576
+ // The server implements the takeover protocol behind
78577
+ // `createHook({ experimental_force: true })` (workflow-server
78578
+ // docs/hook-force-claim.md). Static rather than attested per lookup,
78579
+ // because the decision is made at `createHook()` time, before any
78580
+ // lookup; against a server that has it switched off (or predates it)
78581
+ // a forced creation is answered with `hook_conflict`, which the
78582
+ // runtime reports as an unsupported-World failure rather than a win.
78583
+ hookForceClaim: true
78273
78584
  // NOTE: the backend half of resumeHook()'s lazy path (that
78274
78585
  // the server enforces the `(runId, resumeId)` dedup constraint) is
78275
78586
  // NO LONGER a static world capability here. It is attested per-lookup by
@@ -82231,6 +82542,21 @@ function getCommonReducers2(_global2 = globalThis) {
82231
82542
  }
82232
82543
  return reduced;
82233
82544
  }, "HookConflictError"),
82545
+ HookForceClaimedError: /* @__PURE__ */ __name((value) => {
82546
+ const base = reduceNamedErrorSubclassBase2("HookForceClaimedError", value);
82547
+ if (!base)
82548
+ return false;
82549
+ const reduced = {
82550
+ ...base,
82551
+ token: readProperty2(value, "token"),
82552
+ claimedByRunId: readProperty2(value, "claimedByRunId")
82553
+ };
82554
+ const claimedByHookId = readProperty2(value, "claimedByHookId");
82555
+ if (claimedByHookId !== void 0) {
82556
+ reduced.claimedByHookId = claimedByHookId;
82557
+ }
82558
+ return reduced;
82559
+ }, "HookForceClaimedError"),
82234
82560
  RangeError: makeErrorSubclassReducer2("RangeError"),
82235
82561
  ReferenceError: makeErrorSubclassReducer2("ReferenceError"),
82236
82562
  // RetryableError carries an extra `retryAfter` Date that we serialize as
@@ -82415,6 +82741,16 @@ function getCommonRevivers3(global2 = globalThis) {
82415
82741
  }
82416
82742
  return error121;
82417
82743
  }, "HookConflictError"),
82744
+ HookForceClaimedError: /* @__PURE__ */ __name((value) => {
82745
+ const Ctor = global2[/* @__PURE__ */ Symbol.for("@workflow/errors//HookForceClaimedError")] ?? HookForceClaimedError2;
82746
+ const error121 = new Ctor(value.token, value.claimedByRunId, value.claimedByHookId);
82747
+ if (value.stack !== void 0)
82748
+ error121.stack = value.stack;
82749
+ if ("cause" in value) {
82750
+ error121.cause = value.cause;
82751
+ }
82752
+ return error121;
82753
+ }, "HookForceClaimedError"),
82418
82754
  RangeError: makeErrorSubclassReviver2(global2, "RangeError"),
82419
82755
  ReferenceError: makeErrorSubclassReviver2(global2, "ReferenceError"),
82420
82756
  RetryableError: /* @__PURE__ */ __name((value) => {
@@ -86052,7 +86388,7 @@ var version5;
86052
86388
  var init_version4 = __esm({
86053
86389
  "../core/dist/version.js"() {
86054
86390
  "use strict";
86055
- version5 = "5.0.0-beta.55";
86391
+ version5 = "5.0.0-beta.56";
86056
86392
  }
86057
86393
  });
86058
86394
 
@@ -86523,6 +86859,112 @@ var init_helpers3 = __esm({
86523
86859
  }
86524
86860
  });
86525
86861
 
86862
+ // ../core/dist/runtime/hook-wake.js
86863
+ function isRetryableWakeError2(error121, isDeploymentUnavailableError) {
86864
+ if (isDeploymentUnavailableError?.(error121))
86865
+ return false;
86866
+ const status = error121 ?? {};
86867
+ const code3 = status.status ?? status.statusCode;
86868
+ if (typeof code3 === "number") {
86869
+ return code3 >= 500 || code3 === 408 || code3 === 429;
86870
+ }
86871
+ const name = error121?.name;
86872
+ return name !== "BadRequestError" && name !== "UnauthorizedError" && name !== "ForbiddenError";
86873
+ }
86874
+ async function publishHookWakeWithRetry2(publish, isDeploymentUnavailableError) {
86875
+ let lastError;
86876
+ for (let attempt = 0; attempt <= HOOK_WAKE_RETRY_DELAYS_MS2.length; attempt++) {
86877
+ try {
86878
+ await publish();
86879
+ return;
86880
+ } catch (error121) {
86881
+ lastError = error121;
86882
+ if (!isRetryableWakeError2(error121, isDeploymentUnavailableError))
86883
+ break;
86884
+ const delayMs = HOOK_WAKE_RETRY_DELAYS_MS2[attempt];
86885
+ if (delayMs !== void 0) {
86886
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
86887
+ }
86888
+ }
86889
+ }
86890
+ if (HookNotFoundError2.is(lastError)) {
86891
+ throw new WorkflowRuntimeError2("The hook resume was committed, but its workflow wake could not be published", { cause: lastError });
86892
+ }
86893
+ throw lastError;
86894
+ }
86895
+ async function publishForceClaimVictimWake(world, claimerRunId, hook) {
86896
+ const from = hook.claimedFrom;
86897
+ if (!from || from.runId === claimerRunId)
86898
+ return "skipped";
86899
+ if (from.workflowName === void 0) {
86900
+ runtimeLogger2.warn("Force-claimed a hook from a run whose workflow name is unknown; not waking it", { workflowRunId: claimerRunId, victimRunId: from.runId });
86901
+ return "skipped";
86902
+ }
86903
+ try {
86904
+ await publishHookWakeWithRetry2(() => world.queue(getWorkflowQueueName2(from.workflowName), { runId: from.runId }, {
86905
+ ...from.deploymentId !== void 0 && {
86906
+ deploymentId: from.deploymentId
86907
+ },
86908
+ specVersion: from.runSpecVersion ?? SPEC_VERSION_LEGACY2,
86909
+ idempotencyKey: `hook-force-claim-${hook.hookId}`
86910
+ }), world.isDeploymentUnavailableError?.bind(world));
86911
+ return "published";
86912
+ } catch (error121) {
86913
+ runtimeLogger2.error("Force-claimed a hook token but could not wake the run it was taken from; it will read the disposal on its next invocation", {
86914
+ workflowRunId: claimerRunId,
86915
+ victimRunId: from.runId,
86916
+ error: error121 instanceof Error ? error121.message : String(error121)
86917
+ });
86918
+ return "failed";
86919
+ }
86920
+ }
86921
+ function forcedCreationOwingWake(events) {
86922
+ if (!events)
86923
+ return void 0;
86924
+ for (let i = events.length - 1; i >= 0; i--) {
86925
+ const event = events[i];
86926
+ if (event.eventType === "hook_received" || event.eventType === "noop" || event.eventType === "hook_disposed" && event.eventData?.forceClaimedBy !== void 0) {
86927
+ continue;
86928
+ }
86929
+ return event.eventType === "hook_created" && event.eventData.forceClaimedFrom !== void 0 ? event : void 0;
86930
+ }
86931
+ return void 0;
86932
+ }
86933
+ async function republishOwedForceClaimVictimWake(world, runId, events) {
86934
+ const owed = forcedCreationOwingWake(events);
86935
+ if (!owed)
86936
+ return;
86937
+ const claimedFrom = owed.eventData.forceClaimedFrom;
86938
+ const outcome = await publishForceClaimVictimWake(world, runId, {
86939
+ hookId: owed.correlationId,
86940
+ claimedFrom
86941
+ });
86942
+ if (outcome !== "skipped") {
86943
+ runtimeLogger2.info("Republished the wake of a force-claimed hook victim", {
86944
+ workflowRunId: runId,
86945
+ hookId: owed.correlationId,
86946
+ victimRunId: claimedFrom.runId,
86947
+ victimWake: outcome
86948
+ });
86949
+ }
86950
+ }
86951
+ var HOOK_WAKE_RETRY_DELAYS_MS2;
86952
+ var init_hook_wake2 = __esm({
86953
+ "../core/dist/runtime/hook-wake.js"() {
86954
+ "use strict";
86955
+ init_dist7();
86956
+ init_dist6();
86957
+ init_logger2();
86958
+ init_helpers3();
86959
+ HOOK_WAKE_RETRY_DELAYS_MS2 = [25, 100];
86960
+ __name(isRetryableWakeError2, "isRetryableWakeError");
86961
+ __name(publishHookWakeWithRetry2, "publishHookWakeWithRetry");
86962
+ __name(publishForceClaimVictimWake, "publishForceClaimVictimWake");
86963
+ __name(forcedCreationOwingWake, "forcedCreationOwingWake");
86964
+ __name(republishOwedForceClaimVictimWake, "republishOwedForceClaimVictimWake");
86965
+ }
86966
+ });
86967
+
86526
86968
  // ../core/dist/runtime/invocations.js
86527
86969
  async function handleInvocation(world, runId, requestId, payload) {
86528
86970
  const parsed = HookInvocationSchema2.safeParse(payload);
@@ -86741,38 +87183,6 @@ async function computeResumePayloadDigest2(bytes) {
86741
87183
  }
86742
87184
  return hex5;
86743
87185
  }
86744
- function isRetryableWakeError2(error121, isDeploymentUnavailableError) {
86745
- if (isDeploymentUnavailableError?.(error121))
86746
- return false;
86747
- const status = error121 ?? {};
86748
- const code3 = status.status ?? status.statusCode;
86749
- if (typeof code3 === "number") {
86750
- return code3 >= 500 || code3 === 408 || code3 === 429;
86751
- }
86752
- const name = error121?.name;
86753
- return name !== "BadRequestError" && name !== "UnauthorizedError" && name !== "ForbiddenError";
86754
- }
86755
- async function publishHookWakeWithRetry2(publish, isDeploymentUnavailableError) {
86756
- let lastError;
86757
- for (let attempt = 0; attempt <= HOOK_WAKE_RETRY_DELAYS_MS2.length; attempt++) {
86758
- try {
86759
- await publish();
86760
- return;
86761
- } catch (error121) {
86762
- lastError = error121;
86763
- if (!isRetryableWakeError2(error121, isDeploymentUnavailableError))
86764
- break;
86765
- const delayMs = HOOK_WAKE_RETRY_DELAYS_MS2[attempt];
86766
- if (delayMs !== void 0) {
86767
- await new Promise((resolve3) => setTimeout(resolve3, delayMs));
86768
- }
86769
- }
86770
- }
86771
- if (HookNotFoundError2.is(lastError)) {
86772
- throw new WorkflowRuntimeError2("The hook resume was committed, but its workflow wake could not be published", { cause: lastError });
86773
- }
86774
- throw lastError;
86775
- }
86776
87186
  function resumeContextFromRun2(run3) {
86777
87187
  const coreVersion = run3.executionContext?.workflowCoreVersion;
86778
87188
  const traceCarrier = run3.executionContext?.traceCarrier;
@@ -86848,154 +87258,255 @@ async function getHookByToken3(token) {
86848
87258
  async function resumeHook2(tokenOrHook, payload, encryptionKeyOverride) {
86849
87259
  return resumeHookImpl2(tokenOrHook, payload, encryptionKeyOverride, false, Date.now());
86850
87260
  }
86851
- async function resumeHookImpl2(tokenOrHook, payload, encryptionKeyOverride, hookFreshlyLookedUp, resumeRequestedAtMs) {
87261
+ async function resumeHookImpl2(tokenOrHook, payload, encryptionKeyOverride, hookFreshlyLookedUp, resumeRequestedAtMs, webhookOnly = false) {
86852
87262
  return await waitedUntil2(() => {
86853
87263
  return trace5("hook.resume", async (span) => {
86854
87264
  const world = await getWorldLazy2();
86855
- try {
86856
- const suppliedToken = typeof tokenOrHook === "string";
86857
- const hook = suppliedToken ? await world.hooks.getByToken(tokenOrHook) : tokenOrHook;
86858
- const hookResumeCapabilitiesAreFresh = suppliedToken || hookFreshlyLookedUp;
86859
- const info = await resolveHookResumeInfo2(hook);
86860
- const { resumeContext } = info;
86861
- span?.setAttributes({
86862
- ...HookToken2(hook.token),
86863
- ...HookId2(hook.hookId),
86864
- ...WorkflowRunId4(hook.runId),
86865
- "workflow.hook.resume_context_source": info.source
86866
- });
86867
- if (info.run && isTerminalWorkflowRunStatus2(info.run.status)) {
86868
- throw new HookNotFoundError2(hook.token);
86869
- }
86870
- const capabilities = getRunCapabilities2(resumeContext.workflowCoreVersion);
86871
- let payloadKey;
86872
- const runPublicKey = encryptionKeyOverride ? (
86873
- // The caller already holds a key (resumeWebhook resolved one to
86874
- // hydrate hook metadata), so sealing would add an ECDH for no
86875
- // saved round trip. Reuse what it resolved.
86876
- void 0
86877
- ) : decodeRunPublicKey2(resumeContext.encryptionPublicKey);
86878
- if (runPublicKey) {
86879
- payloadKey = sealTo2(runPublicKey);
86880
- } else {
86881
- let encryptionKey = encryptionKeyOverride ?? await resolveHookEncryptionKey2(hook, info);
86882
- if (!capabilities.supportedFormats.has(SerializationFormat2.ENCRYPTED)) {
86883
- encryptionKey = void 0;
86884
- }
86885
- payloadKey = encryptionKey;
86886
- }
86887
- const compression = (resumeContext.runSpecVersion ?? 0) >= SPEC_VERSION_SUPPORTS_COMPRESSION2 && capabilities.supportedFormats.has(SerializationFormat2.GZIP);
86888
- const ops = [];
86889
- const readbackOps = [];
86890
- const v1Compat = isLegacySpecVersion2(hook.specVersion);
86891
- const dehydratedPayload = await dehydrateStepReturnValue2(payload, hook.runId, payloadKey, ops, globalThis, v1Compat, capabilities.framedByteStreams, compression, void 0, readbackOps);
86892
- await Promise.all(ops.map((op) => op.catch((error121) => {
86893
- if (error121 !== void 0)
86894
- throw error121;
86895
- })));
86896
- safeWaitUntil2(Promise.all(readbackOps), (err) => {
86897
- if (err === void 0)
86898
- return;
86899
- runtimeLogger2.warn("Background readback of hook payload failed", {
86900
- workflowRunId: hook.runId,
86901
- hookId: hook.hookId,
86902
- error: err instanceof Error ? err.message : String(err)
87265
+ const token = typeof tokenOrHook === "string" ? tokenOrHook : tokenOrHook.token;
87266
+ let target2 = tokenOrHook;
87267
+ let fresh = hookFreshlyLookedUp;
87268
+ let keyOverride = encryptionKeyOverride;
87269
+ let redirects = 0;
87270
+ let lookupsAfterRedirect = 0;
87271
+ const MAX_LOOKUPS_AFTER_REDIRECT = 5;
87272
+ const resumeId = generateResumeId2();
87273
+ const assertResendable = /* @__PURE__ */ __name((cause) => {
87274
+ if (payload instanceof Request && payload.body !== null && payload.bodyUsed) {
87275
+ span?.setAttributes({
87276
+ "workflow.hook.resume_redirect_unresendable": true
86903
87277
  });
86904
- });
86905
- span?.setAttributes({
86906
- ...WorkflowName2(resumeContext.workflowName)
86907
- });
86908
- const originLink = await linkToTraceCarrier2(resumeContext.traceCarrier);
86909
- if (originLink) {
86910
- span?.addLink?.(originLink);
87278
+ throw new WorkflowRuntimeError2(`Hook token "${token}" changed owner while this webhook request was being delivered; a request body can be sent only once and it was not delivered anywhere \u2014 retry the request, which will reach the token's new owner`, { cause });
86911
87279
  }
86912
- if (world.capabilities?.invoke === true && !v1Compat && dehydratedPayload instanceof Uint8Array) {
86913
- if (!world.invoke) {
86914
- throw new WorkflowRuntimeError2("World advertises invoke without implementing it");
86915
- }
86916
- span?.setAttributes({ "workflow.hook.resume_strategy": "invoke" });
86917
- const result2 = HookInvocationResultSchema2.parse(await world.invoke(hook.runId, {
86918
- type: "hook_resume",
86919
- version: 1,
86920
- hookId: hook.hookId,
86921
- token: hook.token,
86922
- payload: dehydratedPayload
86923
- }, { idempotencyKey: generateResumeId2() }));
86924
- if (result2.status === "rejected") {
86925
- if (result2.code === "HOOK_NOT_FOUND")
86926
- throw new HookNotFoundError2(hook.token);
86927
- throw new WorkflowRuntimeError2("Executor rejected the hook invocation input");
86928
- }
86929
- span?.setAttributes(HookResumeCommitted2(true));
86930
- return asLazyMetadataHook2(hook);
86931
- }
86932
- const queueName = getWorkflowQueueName2(resumeContext.workflowName);
86933
- const queueOptions = {
86934
- deploymentId: resumeContext.deploymentId,
86935
- specVersion: resumeContext.runSpecVersion ?? SPEC_VERSION_LEGACY2
86936
- };
86937
- const backendDedupSupported = (hookResumeCapabilitiesAreFresh ? hook.resumeCapabilities?.hookResumeDedupVersion ?? 0 : 0) >= HOOK_RESUME_DEDUP_VERSION2 || world.capabilities?.hookResumeDedup === true;
86938
- const canClaimResume = backendDedupSupported && !v1Compat && dehydratedPayload instanceof Uint8Array;
86939
- span?.setAttributes({
86940
- "workflow.hook.resume_strategy": "sequential"
86941
- });
86942
- const resumeId = canClaimResume ? generateResumeId2() : void 0;
86943
- const payloadDigest = canClaimResume ? await computeResumePayloadDigest2(dehydratedPayload) : void 0;
86944
- if (resumeId) {
86945
- span?.setAttributes({ "workflow.hook.resume_id": resumeId });
86946
- }
86947
- const isHookGoneError = /* @__PURE__ */ __name((err) => HookNotFoundError2.is(err) || RunExpiredError2.is(err), "isHookGoneError");
87280
+ }, "assertResendable");
87281
+ for (; ; ) {
86948
87282
  try {
86949
- await world.events.create(hook.runId, {
86950
- eventType: "hook_received",
86951
- specVersion: SPEC_VERSION_CURRENT2,
86952
- correlationId: hook.hookId,
86953
- eventData: {
86954
- ...v1Compat ? {} : { token: hook.token },
86955
- payload: dehydratedPayload
86956
- }
86957
- }, {
86958
- v1Compat,
86959
- ...resumeId && payloadDigest ? { resumeId, resumePayloadDigest: payloadDigest } : {}
86960
- });
87283
+ return await resumeHookAttempt2(world, span, target2, payload, keyOverride, fresh, resumeRequestedAtMs, resumeId, webhookOnly);
86961
87284
  } catch (err) {
86962
- if (isHookGoneError(err)) {
86963
- throw new HookNotFoundError2(hook.token);
87285
+ if (err !== null && typeof err === "object" && notFoundReasons2.get(err) === "not-webhook") {
87286
+ throw err;
87287
+ }
87288
+ if (redirects >= MAX_FORCE_CLAIM_REDIRECTS2) {
87289
+ if (HookForceClaimedError2.is(err)) {
87290
+ span?.setAttributes({
87291
+ "workflow.hook.resume_redirects_exhausted": true
87292
+ });
87293
+ throw new WorkflowRuntimeError2(`Hook token "${token}" changed owner ${redirects} times while this resume was in flight; nothing was delivered \u2014 retry the resume`, { cause: err });
87294
+ }
87295
+ throw err;
87296
+ }
87297
+ if (HookForceClaimedError2.is(err)) {
87298
+ assertResendable(err);
87299
+ redirects++;
87300
+ span?.setAttributes({
87301
+ "workflow.hook.resume_redirects": redirects,
87302
+ "workflow.hook.resume_redirect_to_run": err.claimedByRunId
87303
+ });
87304
+ target2 = token;
87305
+ fresh = true;
87306
+ keyOverride = void 0;
87307
+ lookupsAfterRedirect = 0;
87308
+ continue;
87309
+ }
87310
+ if (HookNotFoundError2.is(err) && redirects > 0 && typeof target2 === "string" && lookupsAfterRedirect < MAX_LOOKUPS_AFTER_REDIRECT) {
87311
+ assertResendable(err);
87312
+ lookupsAfterRedirect++;
87313
+ await new Promise((resolve3) => setTimeout(resolve3, 50));
87314
+ continue;
87315
+ }
87316
+ if (HookNotFoundError2.is(err) && notFoundReasons2.get(err) !== "lookup") {
87317
+ const attemptedHookId = typeof target2 === "string" ? void 0 : target2.hookId;
87318
+ let relocated;
87319
+ try {
87320
+ relocated = await world.hooks.getByToken(token);
87321
+ } catch (lookupError) {
87322
+ if (!HookNotFoundError2.is(lookupError)) {
87323
+ runtimeLogger2.warn("Hook resume: re-resolving the token after a not-found failed; surfacing the original error", {
87324
+ token,
87325
+ error: lookupError instanceof Error ? lookupError.message : String(lookupError)
87326
+ });
87327
+ }
87328
+ }
87329
+ if (relocated?.claimedFrom !== void 0 && relocated.hookId !== attemptedHookId && (attemptedHookId === void 0 || relocated.claimedFrom.hookId === attemptedHookId) && !(webhookOnly && relocated.isWebhook === false)) {
87330
+ assertResendable(err);
87331
+ redirects++;
87332
+ span?.setAttributes({
87333
+ "workflow.hook.resume_redirects": redirects,
87334
+ "workflow.hook.resume_redirect_to_run": relocated.runId
87335
+ });
87336
+ target2 = relocated;
87337
+ fresh = true;
87338
+ keyOverride = void 0;
87339
+ continue;
87340
+ }
86964
87341
  }
86965
87342
  throw err;
86966
87343
  }
86967
- span?.setAttributes(HookResumeCommitted2(true));
86968
- const queuePublishRequestedAtMs = Date.now();
86969
- await publishHookWakeWithRetry2(() => world.queue(queueName, {
86970
- runId: hook.runId,
86971
- traceCarrier: resumeContext.traceCarrier ?? void 0,
86972
- hookResumeTiming: {
86973
- resumeRequestedAtMs,
86974
- queuePublishRequestedAtMs,
86975
- strategy: "sequential"
86976
- }
86977
- }, {
86978
- ...queueOptions,
86979
- // Dedup retried publishes whose response was lost: a
86980
- // duplicate wake is harmless for correctness (deterministic
86981
- // replay) but costs a full replay of the run, and the queue
86982
- // accepts a repeated idempotency key by delivering only one
86983
- // of the messages. Claim-less writes have no resumeId and
86984
- // keep the previous behavior.
86985
- ...resumeId ? { idempotencyKey: `hook-${resumeId}` } : {}
86986
- }), world.isDeploymentUnavailableError?.bind(world));
86987
- span?.setAttributes(HookWakePublished2(true));
86988
- return asLazyMetadataHook2(hook);
86989
- } catch (err) {
86990
- span?.setAttributes({
86991
- ...HookToken2(typeof tokenOrHook === "string" ? tokenOrHook : tokenOrHook.token),
86992
- ...HookFound2(false)
86993
- });
86994
- throw err;
86995
87344
  }
86996
87345
  });
86997
87346
  });
86998
87347
  }
87348
+ async function resumeHookAttempt2(world, span, tokenOrHook, payload, encryptionKeyOverride, hookFreshlyLookedUp, resumeRequestedAtMs, logicalResumeId, webhookOnly) {
87349
+ try {
87350
+ const suppliedToken = typeof tokenOrHook === "string";
87351
+ let hook;
87352
+ if (suppliedToken) {
87353
+ try {
87354
+ hook = await world.hooks.getByToken(tokenOrHook);
87355
+ } catch (lookupError) {
87356
+ if (HookNotFoundError2.is(lookupError)) {
87357
+ notFoundReasons2.set(lookupError, "lookup");
87358
+ }
87359
+ throw lookupError;
87360
+ }
87361
+ } else {
87362
+ hook = tokenOrHook;
87363
+ }
87364
+ if (webhookOnly && hook.isWebhook === false) {
87365
+ const notWebhook = new HookNotFoundError2(hook.token);
87366
+ notFoundReasons2.set(notWebhook, "not-webhook");
87367
+ throw notWebhook;
87368
+ }
87369
+ const hookResumeCapabilitiesAreFresh = suppliedToken || hookFreshlyLookedUp;
87370
+ const info = await resolveHookResumeInfo2(hook);
87371
+ const { resumeContext } = info;
87372
+ span?.setAttributes({
87373
+ ...HookToken2(hook.token),
87374
+ ...HookId2(hook.hookId),
87375
+ ...WorkflowRunId4(hook.runId),
87376
+ "workflow.hook.resume_context_source": info.source
87377
+ });
87378
+ if (info.run && isTerminalWorkflowRunStatus2(info.run.status)) {
87379
+ throw new HookNotFoundError2(hook.token);
87380
+ }
87381
+ const capabilities = getRunCapabilities2(resumeContext.workflowCoreVersion);
87382
+ let payloadKey;
87383
+ const runPublicKey = encryptionKeyOverride ? (
87384
+ // The caller already holds a key (resumeWebhook resolved one to
87385
+ // hydrate hook metadata), so sealing would add an ECDH for no
87386
+ // saved round trip. Reuse what it resolved.
87387
+ void 0
87388
+ ) : decodeRunPublicKey2(resumeContext.encryptionPublicKey);
87389
+ if (runPublicKey) {
87390
+ payloadKey = sealTo2(runPublicKey);
87391
+ } else {
87392
+ let encryptionKey = encryptionKeyOverride ?? await resolveHookEncryptionKey2(hook, info);
87393
+ if (!capabilities.supportedFormats.has(SerializationFormat2.ENCRYPTED)) {
87394
+ encryptionKey = void 0;
87395
+ }
87396
+ payloadKey = encryptionKey;
87397
+ }
87398
+ const compression = (resumeContext.runSpecVersion ?? 0) >= SPEC_VERSION_SUPPORTS_COMPRESSION2 && capabilities.supportedFormats.has(SerializationFormat2.GZIP);
87399
+ const ops = [];
87400
+ const readbackOps = [];
87401
+ const v1Compat = isLegacySpecVersion2(hook.specVersion);
87402
+ const dehydratedPayload = await dehydrateStepReturnValue2(payload, hook.runId, payloadKey, ops, globalThis, v1Compat, capabilities.framedByteStreams, compression, void 0, readbackOps);
87403
+ await Promise.all(ops.map((op) => op.catch((error121) => {
87404
+ if (error121 !== void 0)
87405
+ throw error121;
87406
+ })));
87407
+ safeWaitUntil2(Promise.all(readbackOps), (err) => {
87408
+ if (err === void 0)
87409
+ return;
87410
+ runtimeLogger2.warn("Background readback of hook payload failed", {
87411
+ workflowRunId: hook.runId,
87412
+ hookId: hook.hookId,
87413
+ error: err instanceof Error ? err.message : String(err)
87414
+ });
87415
+ });
87416
+ if (world.capabilities?.invoke === true && !v1Compat && dehydratedPayload instanceof Uint8Array) {
87417
+ if (!world.invoke) {
87418
+ throw new WorkflowRuntimeError2("World advertises invoke without implementing it");
87419
+ }
87420
+ span?.setAttributes({ "workflow.hook.resume_strategy": "invoke" });
87421
+ const result2 = HookInvocationResultSchema2.parse(await world.invoke(hook.runId, {
87422
+ type: "hook_resume",
87423
+ version: 1,
87424
+ hookId: hook.hookId,
87425
+ token: hook.token,
87426
+ payload: dehydratedPayload
87427
+ }, { idempotencyKey: logicalResumeId }));
87428
+ if (result2.status === "rejected") {
87429
+ if (result2.code === "HOOK_NOT_FOUND")
87430
+ throw new HookNotFoundError2(hook.token);
87431
+ throw new WorkflowRuntimeError2("Executor rejected the hook invocation input");
87432
+ }
87433
+ span?.setAttributes(HookResumeCommitted2(true));
87434
+ return asLazyMetadataHook2(hook);
87435
+ }
87436
+ span?.setAttributes({
87437
+ ...WorkflowName2(resumeContext.workflowName)
87438
+ });
87439
+ const originLink = await linkToTraceCarrier2(resumeContext.traceCarrier);
87440
+ if (originLink) {
87441
+ span?.addLink?.(originLink);
87442
+ }
87443
+ const queueName = getWorkflowQueueName2(resumeContext.workflowName);
87444
+ const queueOptions = {
87445
+ deploymentId: resumeContext.deploymentId,
87446
+ specVersion: resumeContext.runSpecVersion ?? SPEC_VERSION_LEGACY2
87447
+ };
87448
+ const backendDedupSupported = (hookResumeCapabilitiesAreFresh ? hook.resumeCapabilities?.hookResumeDedupVersion ?? 0 : 0) >= HOOK_RESUME_DEDUP_VERSION2 || world.capabilities?.hookResumeDedup === true;
87449
+ const canClaimResume = backendDedupSupported && !v1Compat && dehydratedPayload instanceof Uint8Array;
87450
+ span?.setAttributes({
87451
+ "workflow.hook.resume_strategy": "sequential"
87452
+ });
87453
+ const resumeId = canClaimResume ? logicalResumeId : void 0;
87454
+ const payloadDigest = canClaimResume ? await computeResumePayloadDigest2(dehydratedPayload) : void 0;
87455
+ if (resumeId) {
87456
+ span?.setAttributes({ "workflow.hook.resume_id": resumeId });
87457
+ }
87458
+ const isHookGoneError = /* @__PURE__ */ __name((err) => HookNotFoundError2.is(err) || RunExpiredError2.is(err), "isHookGoneError");
87459
+ try {
87460
+ await world.events.create(hook.runId, {
87461
+ eventType: "hook_received",
87462
+ specVersion: SPEC_VERSION_CURRENT2,
87463
+ correlationId: hook.hookId,
87464
+ eventData: {
87465
+ ...v1Compat ? {} : { token: hook.token },
87466
+ payload: dehydratedPayload
87467
+ }
87468
+ }, {
87469
+ v1Compat,
87470
+ ...resumeId && payloadDigest ? { resumeId, resumePayloadDigest: payloadDigest } : {}
87471
+ });
87472
+ } catch (err) {
87473
+ if (HookForceClaimedError2.is(err))
87474
+ throw err;
87475
+ if (isHookGoneError(err)) {
87476
+ throw new HookNotFoundError2(hook.token);
87477
+ }
87478
+ throw err;
87479
+ }
87480
+ span?.setAttributes(HookResumeCommitted2(true));
87481
+ const queuePublishRequestedAtMs = Date.now();
87482
+ await publishHookWakeWithRetry2(() => world.queue(queueName, {
87483
+ runId: hook.runId,
87484
+ traceCarrier: resumeContext.traceCarrier ?? void 0,
87485
+ hookResumeTiming: {
87486
+ resumeRequestedAtMs,
87487
+ queuePublishRequestedAtMs,
87488
+ strategy: "sequential"
87489
+ }
87490
+ }, {
87491
+ ...queueOptions,
87492
+ // Dedup retried publishes whose response was lost: a
87493
+ // duplicate wake is harmless for correctness (deterministic
87494
+ // replay) but costs a full replay of the run, and the queue
87495
+ // accepts a repeated idempotency key by delivering only one
87496
+ // of the messages. Claim-less writes have no resumeId and
87497
+ // keep the previous behavior.
87498
+ ...resumeId ? { idempotencyKey: `hook-${resumeId}` } : {}
87499
+ }), world.isDeploymentUnavailableError?.bind(world));
87500
+ span?.setAttributes(HookWakePublished2(true));
87501
+ return asLazyMetadataHook2(hook);
87502
+ } catch (err) {
87503
+ span?.setAttributes({
87504
+ ...HookToken2(typeof tokenOrHook === "string" ? tokenOrHook : tokenOrHook.token),
87505
+ ...HookFound2(false)
87506
+ });
87507
+ throw err;
87508
+ }
87509
+ }
86999
87510
  async function resumeWebhook2(token, request) {
87000
87511
  const resumeRequestedAtMs = Date.now();
87001
87512
  const world = await getWorldLazy2();
@@ -87019,7 +87530,7 @@ async function resumeWebhook2(token, request) {
87019
87530
  } else {
87020
87531
  response = new Response(null, { status: 202 });
87021
87532
  }
87022
- await resumeHookImpl2(hook, request, metadataEncryptionKey(), true, resumeRequestedAtMs);
87533
+ await resumeHookImpl2(hook, request, metadataEncryptionKey(), true, resumeRequestedAtMs, true);
87023
87534
  if (responseReadable) {
87024
87535
  const reader = responseReadable.getReader();
87025
87536
  const chunk = await reader.read();
@@ -87035,7 +87546,7 @@ async function resumeWebhook2(token, request) {
87035
87546
  }
87036
87547
  return response;
87037
87548
  }
87038
- var generateResumeId2, HOOK_WAKE_RETRY_DELAYS_MS2;
87549
+ var generateResumeId2, MAX_FORCE_CLAIM_REDIRECTS2, notFoundReasons2;
87039
87550
  var init_resume_hook2 = __esm({
87040
87551
  "../core/dist/runtime/resume-hook.js"() {
87041
87552
  "use strict";
@@ -87053,13 +87564,11 @@ var init_resume_hook2 = __esm({
87053
87564
  init_telemetry4();
87054
87565
  init_get_world_lazy2();
87055
87566
  init_helpers3();
87567
+ init_hook_wake2();
87056
87568
  init_invocations2();
87057
87569
  init_wait_until2();
87058
87570
  generateResumeId2 = monotonicFactory2();
87059
87571
  __name(computeResumePayloadDigest2, "computeResumePayloadDigest");
87060
- HOOK_WAKE_RETRY_DELAYS_MS2 = [25, 100];
87061
- __name(isRetryableWakeError2, "isRetryableWakeError");
87062
- __name(publishHookWakeWithRetry2, "publishHookWakeWithRetry");
87063
87572
  __name(resumeContextFromRun2, "resumeContextFromRun");
87064
87573
  __name(resolveHookResumeInfo2, "resolveHookResumeInfo");
87065
87574
  __name(resolveHookEncryptionKey2, "resolveHookEncryptionKey");
@@ -87068,7 +87577,10 @@ var init_resume_hook2 = __esm({
87068
87577
  __name(asLazyMetadataHook2, "asLazyMetadataHook");
87069
87578
  __name(getHookByToken3, "getHookByToken");
87070
87579
  __name(resumeHook2, "resumeHook");
87580
+ MAX_FORCE_CLAIM_REDIRECTS2 = 3;
87581
+ notFoundReasons2 = /* @__PURE__ */ new WeakMap();
87071
87582
  __name(resumeHookImpl2, "resumeHookImpl");
87583
+ __name(resumeHookAttempt2, "resumeHookAttempt");
87072
87584
  __name(resumeWebhook2, "resumeWebhook");
87073
87585
  }
87074
87586
  });
@@ -89352,6 +89864,11 @@ function getInlineOwnershipLeaseSeconds() {
89352
89864
  function getReplayDivergenceMaxRetries() {
89353
89865
  return envNumber2("WORKFLOW_REPLAY_DIVERGENCE_MAX_RETRIES", REPLAY_DIVERGENCE_MAX_RETRIES, { integer: true });
89354
89866
  }
89867
+ function getOpenWaitClockSkewMs() {
89868
+ return envNumber2("WORKFLOW_OPEN_WAIT_CLOCK_SKEW_MS", OPEN_WAIT_CLOCK_SKEW_MS, {
89869
+ integer: true
89870
+ });
89871
+ }
89355
89872
  function getPreconditionMaxInProcessRestarts() {
89356
89873
  return envNumber2("WORKFLOW_PRECONDITION_MAX_INPROCESS_RESTARTS", PRECONDITION_MAX_INPROCESS_RESTARTS, { integer: true });
89357
89874
  }
@@ -89364,7 +89881,7 @@ function getPreconditionReinvokeDelaySeconds() {
89364
89881
  function getDeploymentMismatchMaxRetries() {
89365
89882
  return envNumber2("WORKFLOW_DEPLOYMENT_MISMATCH_MAX_RETRIES", DEPLOYMENT_MISMATCH_MAX_RETRIES, { integer: true, min: 0 });
89366
89883
  }
89367
- var MAX_QUEUE_DELIVERIES, REPLAY_TIMEOUT_MS, MIN_REPLAY_TIMEOUT_MS, MAX_REPLAY_TIMEOUT_MS, warned, REPLAY_TIMEOUT_MAX_RETRIES, MAX_INLINE_STEPS, MIN_MAX_INLINE_STEPS, MAX_MAX_INLINE_STEPS, MAX_RESILIENT_STEP_INPUT_BYTES, MAX_BATCH_FANOUT_EVENTS, INLINE_OWNERSHIP_LEASE_SECONDS, MAX_INLINE_OWNERSHIP_LEASE_SECONDS, REPLAY_DIVERGENCE_MAX_RETRIES, PRECONDITION_MAX_INPROCESS_RESTARTS, PRECONDITION_MAX_REINVOCATIONS, PRECONDITION_REINVOKE_DELAY_SECONDS, DEPLOYMENT_MISMATCH_MAX_RETRIES;
89884
+ var MAX_QUEUE_DELIVERIES, REPLAY_TIMEOUT_MS, MIN_REPLAY_TIMEOUT_MS, MAX_REPLAY_TIMEOUT_MS, warned, REPLAY_TIMEOUT_MAX_RETRIES, MAX_INLINE_STEPS, MIN_MAX_INLINE_STEPS, MAX_MAX_INLINE_STEPS, MAX_RESILIENT_STEP_INPUT_BYTES, MAX_BATCH_FANOUT_EVENTS, INLINE_OWNERSHIP_LEASE_SECONDS, MAX_INLINE_OWNERSHIP_LEASE_SECONDS, REPLAY_DIVERGENCE_MAX_RETRIES, OPEN_WAIT_CLOCK_SKEW_MS, PRECONDITION_MAX_INPROCESS_RESTARTS, PRECONDITION_MAX_REINVOCATIONS, PRECONDITION_REINVOKE_DELAY_SECONDS, DEPLOYMENT_MISMATCH_MAX_RETRIES;
89368
89885
  var init_constants3 = __esm({
89369
89886
  "../core/dist/runtime/constants.js"() {
89370
89887
  "use strict";
@@ -89405,6 +89922,8 @@ var init_constants3 = __esm({
89405
89922
  __name(getInlineOwnershipLeaseSeconds, "getInlineOwnershipLeaseSeconds");
89406
89923
  REPLAY_DIVERGENCE_MAX_RETRIES = 3;
89407
89924
  __name(getReplayDivergenceMaxRetries, "getReplayDivergenceMaxRetries");
89925
+ OPEN_WAIT_CLOCK_SKEW_MS = 3e4;
89926
+ __name(getOpenWaitClockSkewMs, "getOpenWaitClockSkewMs");
89408
89927
  PRECONDITION_MAX_INPROCESS_RESTARTS = 3;
89409
89928
  __name(getPreconditionMaxInProcessRestarts, "getPreconditionMaxInProcessRestarts");
89410
89929
  PRECONDITION_MAX_REINVOCATIONS = 5;
@@ -96645,6 +97164,28 @@ function createQuickJSSerde(vm, capturedRoot) {
96645
97164
  reduced.cause = shape.cause;
96646
97165
  return reduced;
96647
97166
  }, "HookConflictError"),
97167
+ HookForceClaimedError: /* @__PURE__ */ __name((value) => {
97168
+ if (!isHandle(value) || !value.isError)
97169
+ return false;
97170
+ if (chainedString(value, "name") !== "HookForceClaimedError")
97171
+ return false;
97172
+ const shape = reduceErrorShape(value);
97173
+ const reduced = {
97174
+ message: shape.message,
97175
+ stack: shape.stack,
97176
+ token: own3(value, "token"),
97177
+ claimedByRunId: own3(value, "claimedByRunId")
97178
+ };
97179
+ const claimedByHookId = own3(value, "claimedByHookId");
97180
+ if (claimedByHookId && !claimedByHookId.isUndefined) {
97181
+ reduced.claimedByHookId = claimedByHookId;
97182
+ } else {
97183
+ claimedByHookId?.dispose();
97184
+ }
97185
+ if (Object.hasOwn(shape, "cause"))
97186
+ reduced.cause = shape.cause;
97187
+ return reduced;
97188
+ }, "HookForceClaimedError"),
96648
97189
  RangeError: namedErrorSubclassReducer("RangeError"),
96649
97190
  ReferenceError: namedErrorSubclassReducer("ReferenceError"),
96650
97191
  RetryableError: /* @__PURE__ */ __name((value) => {
@@ -97232,6 +97773,42 @@ function createQuickJSSerde(vm, capturedRoot) {
97232
97773
  }
97233
97774
  return error121;
97234
97775
  }, "HookConflictError"),
97776
+ HookForceClaimedError: /* @__PURE__ */ __name((value) => {
97777
+ const cls = registeredErrorClass("@workflow/errors//HookForceClaimedError");
97778
+ let error121;
97779
+ if (cls) {
97780
+ const token = own3(value, "token") ?? vm.undefined;
97781
+ const claimedByRunId = own3(value, "claimedByRunId") ?? vm.undefined;
97782
+ const claimedByHookId = own3(value, "claimedByHookId") ?? vm.undefined;
97783
+ error121 = vm.construct(cls, token, claimedByRunId, claimedByHookId);
97784
+ if (token !== vm.undefined)
97785
+ token.dispose();
97786
+ if (claimedByRunId !== vm.undefined)
97787
+ claimedByRunId.dispose();
97788
+ if (claimedByHookId !== vm.undefined)
97789
+ claimedByHookId.dispose();
97790
+ const stack = own3(value, "stack");
97791
+ if (stack && !stack.isUndefined)
97792
+ define2(error121, "stack", stack);
97793
+ stack?.dispose();
97794
+ if (guestHasOwn(value, "cause")) {
97795
+ const cause = own3(value, "cause") ?? vm.undefined;
97796
+ define2(error121, "cause", cause);
97797
+ if (cause !== vm.undefined)
97798
+ cause.dispose();
97799
+ }
97800
+ cls.dispose();
97801
+ } else {
97802
+ error121 = buildError(i.Error, value, { name: "HookForceClaimedError" });
97803
+ for (const field of ["token", "claimedByRunId", "claimedByHookId"]) {
97804
+ const handle = own3(value, field);
97805
+ if (handle && !handle.isUndefined)
97806
+ define2(error121, field, handle);
97807
+ handle?.dispose();
97808
+ }
97809
+ }
97810
+ return error121;
97811
+ }, "HookForceClaimedError"),
97235
97812
  RangeError: namedErrorSubclassReviver("RangeError"),
97236
97813
  ReferenceError: namedErrorSubclassReviver("ReferenceError"),
97237
97814
  RetryableError: /* @__PURE__ */ __name((value) => {
@@ -97829,6 +98406,7 @@ var init_quickjs_serde = __esm({
97829
98406
  "workflow-class-registry",
97830
98407
  "@workflow/errors//FatalError",
97831
98408
  "@workflow/errors//HookConflictError",
98409
+ "@workflow/errors//HookForceClaimedError",
97832
98410
  "@workflow/errors//RetryableError",
97833
98411
  "@workflow/errors//RuntimeDecryptionError",
97834
98412
  "@workflow/errors//StreamError"
@@ -98520,13 +99098,30 @@ async function processEvents(vm, serde, events, advanceClock, encryptionKey) {
98520
99098
  case "hook_conflict": {
98521
99099
  const conflictToken = eventData?.token ?? "unknown";
98522
99100
  const conflictingRunId = eventData?.conflictingRunId;
99101
+ const forceRefusedReason = eventData?.forceRefusedReason;
98523
99102
  const didSettle = vm.dump(vm.evalCode(`(function(){
98524
99103
  var cid = ${JSON.stringify(cid)};
98525
99104
  var token = ${JSON.stringify(conflictToken)};
98526
99105
  var conflictingRunId = ${JSON.stringify(conflictingRunId ?? null)};
99106
+ var forceRefused = ${JSON.stringify(forceRefusedReason !== void 0)};
98527
99107
  var ErrCls = globalThis[Symbol.for('@workflow/errors//HookConflictError')];
98528
99108
  var err;
98529
- if (typeof ErrCls === 'function') {
99109
+ var hookState = globalThis.__hooks && globalThis.__hooks[cid];
99110
+ // A forced hook asked for a guarantee the World could not give
99111
+ // (older server, kill switch): a misconfiguration, not the
99112
+ // ordinary conflict the caller opted out of. Mirrors hook.ts.
99113
+ var forced = !!(hookState && hookState.force) && !forceRefused;
99114
+ if (forced) {
99115
+ var FatalCls = globalThis[Symbol.for('@workflow/errors//FatalError')];
99116
+ var forcedMessage = 'createHook({ experimental_force: true }) for token "' + token + '" was answered with a hook_conflict: the configured World does not support force-claiming hook tokens' + (conflictingRunId ? ' (run "' + conflictingRunId + '" holds it)' : '') + '.';
99117
+ if (typeof FatalCls === 'function') {
99118
+ err = new FatalCls(forcedMessage);
99119
+ } else {
99120
+ err = new Error(forcedMessage);
99121
+ err.name = 'FatalError';
99122
+ err.fatal = true;
99123
+ }
99124
+ } else if (typeof ErrCls === 'function') {
98530
99125
  err = new ErrCls(token, conflictingRunId || undefined);
98531
99126
  } else {
98532
99127
  err = new Error('Hook token "' + token + '" is already in use by another workflow');
@@ -98535,7 +99130,7 @@ async function processEvents(vm, serde, events, advanceClock, encryptionKey) {
98535
99130
  if (conflictingRunId) err.conflictingRunId = conflictingRunId;
98536
99131
  }
98537
99132
  var run = null;
98538
- if (conflictingRunId) {
99133
+ if (conflictingRunId && !forced) {
98539
99134
  var reg = globalThis[Symbol.for('workflow-class-registry')];
98540
99135
  var RunCls = reg && reg.get('class//workflow//Run');
98541
99136
  var des = RunCls && RunCls[Symbol.for('workflow-deserialize')];
@@ -98599,6 +99194,48 @@ async function processEvents(vm, serde, events, advanceClock, encryptionKey) {
98599
99194
  break;
98600
99195
  }
98601
99196
  case "hook_disposed": {
99197
+ const claimedBy = eventData?.forceClaimedBy;
99198
+ if (claimedBy && typeof claimedBy.runId === "string") {
99199
+ const hookState = vm.dump(vm.evalCode(`(function(){
99200
+ var cid = ${JSON.stringify(cid)};
99201
+ var state = globalThis.__hooks && globalThis.__hooks[cid];
99202
+ var token = state ? state.token : ${JSON.stringify(eventData?.token ?? "")};
99203
+ var claimedByRunId = ${JSON.stringify(claimedBy.runId)};
99204
+ var claimedByHookId = ${JSON.stringify(claimedBy.hookId ?? null)};
99205
+ var ErrCls = globalThis[Symbol.for('@workflow/errors//HookForceClaimedError')];
99206
+ var err;
99207
+ if (typeof ErrCls === 'function') {
99208
+ err = new ErrCls(token, claimedByRunId, claimedByHookId || undefined);
99209
+ } else {
99210
+ err = new Error('Hook token "' + token + '" was force-claimed by another workflow (run "' + claimedByRunId + '")');
99211
+ err.name = 'HookForceClaimedError';
99212
+ err.token = token;
99213
+ err.claimedByRunId = claimedByRunId;
99214
+ if (claimedByHookId) err.claimedByHookId = claimedByHookId;
99215
+ }
99216
+ var settled = false;
99217
+ if (state) {
99218
+ state.forceClaimed = err;
99219
+ var gc = state.getConflictResolvers;
99220
+ state.getConflictResolvers = [];
99221
+ for (var i = 0; i < gc.length; i++) { gc[i].resolve(null); settled = true; }
99222
+ }
99223
+ if (globalThis.__resolvers[cid]) {
99224
+ globalThis.__resolvers[cid].reject(err);
99225
+ delete globalThis.__resolvers[cid];
99226
+ settled = true;
99227
+ }
99228
+ return settled;
99229
+ })()`));
99230
+ if (hookState) {
99231
+ resolved = true;
99232
+ let b;
99233
+ do {
99234
+ b = vm.executePendingJobs();
99235
+ } while (b > 0);
99236
+ }
99237
+ markCreated(vm, cidJs);
99238
+ }
98602
99239
  markCreated(vm, cidJs, "hook_dispose");
98603
99240
  break;
98604
99241
  }
@@ -99357,6 +99994,20 @@ globalThis[Symbol.for("WORKFLOW_CREATE_HOOK")] = function(options) {
99357
99994
  unsupportedRetentionError.fatal = true;
99358
99995
  throw unsupportedRetentionError;
99359
99996
  }
99997
+ if (options.experimental_force === true) {
99998
+ if (options.token === undefined || options.token === null) {
99999
+ throw new Error('\`createHook()\` was called with \`experimental_force: true\` but no \`token\`. Force-claiming only applies to an explicit token another run may hold.');
100000
+ }
100001
+ if (options.isWebhook === true) {
100002
+ throw new Error('Webhook hooks do not support \`experimental_force\`. Use a non-webhook \`createHook()\` with an explicit token.');
100003
+ }
100004
+ if (globalThis.__worldCapabilities?.hookForceClaim !== true) {
100005
+ var unsupportedForceError = new Error('The configured World does not support \`experimental_force\` for Hooks.');
100006
+ unsupportedForceError.name = "FatalError";
100007
+ unsupportedForceError.fatal = true;
100008
+ throw unsupportedForceError;
100009
+ }
100010
+ }
99360
100011
  var token = options.token || globalThis.__generateNanoid();
99361
100012
  var correlationId = "hook_" + globalThis.__generateUlid();
99362
100013
  var isDisposed = false;
@@ -99393,6 +100044,7 @@ globalThis[Symbol.for("WORKFLOW_CREATE_HOOK")] = function(options) {
99393
100044
  token: token,
99394
100045
  tokenRetentionUntil: tokenRetentionUntil,
99395
100046
  isWebhook: !!options.isWebhook,
100047
+ force: options.experimental_force === true,
99396
100048
  metadata: options.metadata,
99397
100049
  hasCreatedEvent: false,
99398
100050
  };
@@ -99408,6 +100060,11 @@ globalThis[Symbol.for("WORKFLOW_CREATE_HOOK")] = function(options) {
99408
100060
  token: token,
99409
100061
  created: false,
99410
100062
  conflict: null,
100063
+ // Set by the host on a hook_disposed{forceClaimedBy}: another run took
100064
+ // the token. Buffered payloads (delivered before the takeover) are still
100065
+ // drained; every await after them rejects with this error.
100066
+ forceClaimed: null,
100067
+ force: options.experimental_force === true,
99411
100068
  getConflictResolvers: [],
99412
100069
  };
99413
100070
 
@@ -99421,6 +100078,10 @@ globalThis[Symbol.for("WORKFLOW_CREATE_HOOK")] = function(options) {
99421
100078
  if (buf && buf.length > 0) {
99422
100079
  return Promise.resolve(buf.shift());
99423
100080
  }
100081
+ var claimedState = globalThis.__hooks[correlationId];
100082
+ if (claimedState && claimedState.forceClaimed) {
100083
+ return Promise.reject(claimedState.forceClaimed);
100084
+ }
99424
100085
  return new Promise(function(resolve, reject) {
99425
100086
  globalThis.__resolvers[correlationId] = { resolve: resolve, reject: reject };
99426
100087
  });
@@ -99436,7 +100097,9 @@ globalThis[Symbol.for("WORKFLOW_CREATE_HOOK")] = function(options) {
99436
100097
  // hook_disposed here would be rejected by the world's
99437
100098
  // hook-existence validation.
99438
100099
  var state = globalThis.__hooks[correlationId];
99439
- if (!state || !state.conflict) {
100100
+ // A force-claimed hook is already disposed \u2014 the takeover journaled
100101
+ // its hook_disposed \u2014 so there is nothing left to dispose either.
100102
+ if (!state || (!state.conflict && !state.forceClaimed)) {
99440
100103
  // Signal to the entrypoint to create a hook_disposed event. The
99441
100104
  // token is carried so the entrypoint can order same-token hook
99442
100105
  // operations sequentially (a dispose must release the token before
@@ -99828,9 +100491,19 @@ async function dispatchPendingOps(params) {
99828
100491
  isWebhook: hook.isWebhook,
99829
100492
  // System hooks (AbortController) are exempt from user
99830
100493
  // token namespace conflict checks.
99831
- ...hook.isSystem ? { isSystem: true } : {}
100494
+ ...hook.isSystem ? { isSystem: true } : {},
100495
+ ...hook.force ? { force: true } : {}
99832
100496
  }
99833
100497
  }, hookDeltaCursor !== void 0 ? { sinceCursor: hookDeltaCursor } : void 0);
100498
+ if (result2.hook?.claimedFrom) {
100499
+ const outcome = await publishForceClaimVictimWake(world, runId, result2.hook);
100500
+ runtimeLogger2.info("Hook token force-claimed from another run", {
100501
+ workflowRunId: runId,
100502
+ hookId: hook.correlationId,
100503
+ victimRunId: result2.hook.claimedFrom.runId,
100504
+ victimWake: outcome
100505
+ });
100506
+ }
99834
100507
  if (result2.event?.eventType === "hook_conflict") {
99835
100508
  await queueMessage(world, getWorkflowQueueName2(workflowRun.workflowName, namespace), {
99836
100509
  runId,
@@ -99919,16 +100592,23 @@ async function dispatchPendingOps(params) {
99919
100592
  hookOpsByToken.set(key, [op]);
99920
100593
  }
99921
100594
  }
99922
- for (const group of hookOpsByToken.values()) {
99923
- opsPromises.push((async () => {
99924
- for (const op of group) {
99925
- if (op.type === "hook") {
99926
- await processHookOp(op);
99927
- } else {
99928
- await processHookDisposeOp(op);
99929
- }
100595
+ const runHookGroup = /* @__PURE__ */ __name(async (group) => {
100596
+ for (const op of group) {
100597
+ if (op.type === "hook") {
100598
+ await processHookOp(op);
100599
+ } else {
100600
+ await processHookDisposeOp(op);
99930
100601
  }
99931
- })());
100602
+ }
100603
+ }, "runHookGroup");
100604
+ const hookGroups = [...hookOpsByToken.values()];
100605
+ const holdsForcedCreation = /* @__PURE__ */ __name((group) => group.some((op) => op.type === "hook" && op.force === true && !op.hasCreatedEvent), "holdsForcedCreation");
100606
+ for (const group of hookGroups.filter(holdsForcedCreation)) {
100607
+ await runHookGroup(group);
100608
+ }
100609
+ for (const group of hookGroups) {
100610
+ if (!holdsForcedCreation(group))
100611
+ opsPromises.push(runHookGroup(group));
99932
100612
  }
99933
100613
  for (const op of pendingOperations) {
99934
100614
  if (op.type === "step" && !op.hasCreatedEvent && !skipStepCreation?.has(op.correlationId)) {
@@ -100160,6 +100840,7 @@ async function runWorkflowWithQuickJS(params) {
100160
100840
  events = allEvents;
100161
100841
  loadedCursor = cursor;
100162
100842
  }
100843
+ await republishOwedForceClaimVictimWake(world, runId, events);
100163
100844
  const logView = new QuickJSLogView(events, loadedCursor);
100164
100845
  const createEvent = /* @__PURE__ */ __name(async (data, eventParams) => {
100165
100846
  const result3 = await world.events.create(runId, data, {
@@ -100904,6 +101585,7 @@ var init_quickjs_entrypoint = __esm({
100904
101585
  init_constants3();
100905
101586
  init_get_port_lazy();
100906
101587
  init_helpers3();
101588
+ init_hook_wake2();
100907
101589
  init_lifecycle_hooks();
100908
101590
  init_quickjs_log_view();
100909
101591
  init_quickjs_runtime();
@@ -101352,6 +102034,7 @@ var StepNotRegisteredError;
101352
102034
  var WorkflowNotRegisteredError;
101353
102035
  var WorkflowRunNotFoundError;
101354
102036
  var HookConflictError;
102037
+ var HookForceClaimedError;
101355
102038
  var HookNotFoundError;
101356
102039
  var EntityConflictError;
101357
102040
  var RunExpiredError;
@@ -101364,6 +102047,7 @@ var RetryableError;
101364
102047
  var FATAL_ERROR_KEY;
101365
102048
  var RETRYABLE_ERROR_KEY;
101366
102049
  var HOOK_CONFLICT_ERROR_KEY;
102050
+ var HOOK_FORCE_CLAIMED_ERROR_KEY;
101367
102051
  var RUNTIME_DECRYPTION_ERROR_KEY;
101368
102052
  var STREAM_ERROR_KEY;
101369
102053
  var init_dist2 = __esm2({
@@ -101385,6 +102069,7 @@ var init_dist2 = __esm2({
101385
102069
  FETCH_IN_WORKFLOW_FUNCTION: "fetch-in-workflow",
101386
102070
  TIMEOUT_FUNCTIONS_IN_WORKFLOW: "timeout-in-workflow",
101387
102071
  HOOK_CONFLICT: "hook-conflict",
102072
+ HOOK_FORCE_CLAIMED: "hook-force-claimed",
101388
102073
  CORRUPTED_EVENT_LOG: "corrupted-event-log",
101389
102074
  REPLAY_DIVERGENCE: "replay-divergence",
101390
102075
  STEP_NOT_REGISTERED: "step-not-registered",
@@ -101644,6 +102329,33 @@ Caused by: ${options.cause.stack}`;
101644
102329
  return isError(value) && value.name === "HookConflictError";
101645
102330
  }
101646
102331
  };
102332
+ HookForceClaimedError = class extends WorkflowError {
102333
+ static {
102334
+ __name(this, "HookForceClaimedError");
102335
+ }
102336
+ static {
102337
+ __name2(this, "HookForceClaimedError");
102338
+ }
102339
+ token;
102340
+ /** The run that took the token. */
102341
+ claimedByRunId;
102342
+ /** The hook (in `claimedByRunId`) the token now belongs to. */
102343
+ claimedByHookId;
102344
+ constructor(token, claimedByRunId, claimedByHookId) {
102345
+ super(`Hook token "${token}" was force-claimed by another workflow (run "${claimedByRunId}")`, {
102346
+ slug: ERROR_SLUGS.HOOK_FORCE_CLAIMED
102347
+ });
102348
+ this.name = "HookForceClaimedError";
102349
+ this.token = token;
102350
+ this.claimedByRunId = claimedByRunId;
102351
+ if (claimedByHookId !== void 0) {
102352
+ this.claimedByHookId = claimedByHookId;
102353
+ }
102354
+ }
102355
+ static is(value) {
102356
+ return isError(value) && value.name === "HookForceClaimedError";
102357
+ }
102358
+ };
101647
102359
  HookNotFoundError = class extends WorkflowError {
101648
102360
  static {
101649
102361
  __name(this, "HookNotFoundError");
@@ -101823,6 +102535,7 @@ Caused by: ${options.cause.stack}`;
101823
102535
  FATAL_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//FatalError");
101824
102536
  RETRYABLE_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//RetryableError");
101825
102537
  HOOK_CONFLICT_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//HookConflictError");
102538
+ HOOK_FORCE_CLAIMED_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//HookForceClaimedError");
101826
102539
  RUNTIME_DECRYPTION_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//RuntimeDecryptionError");
101827
102540
  STREAM_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//StreamError");
101828
102541
  if (typeof globalThis !== "undefined") {
@@ -101850,6 +102563,14 @@ Caused by: ${options.cause.stack}`;
101850
102563
  configurable: false
101851
102564
  });
101852
102565
  }
102566
+ if (!Object.hasOwn(globalThis, HOOK_FORCE_CLAIMED_ERROR_KEY)) {
102567
+ Object.defineProperty(globalThis, HOOK_FORCE_CLAIMED_ERROR_KEY, {
102568
+ value: HookForceClaimedError,
102569
+ writable: false,
102570
+ enumerable: false,
102571
+ configurable: false
102572
+ });
102573
+ }
101853
102574
  if (!Object.hasOwn(globalThis, RUNTIME_DECRYPTION_ERROR_KEY)) {
101854
102575
  Object.defineProperty(globalThis, RUNTIME_DECRYPTION_ERROR_KEY, {
101855
102576
  value: RuntimeDecryptionError,
@@ -101926,7 +102647,7 @@ var SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT;
101926
102647
  var SPEC_VERSION_SUPPORTS_ATTRIBUTES;
101927
102648
  var SPEC_VERSION_SUPPORTS_COMPRESSION;
101928
102649
  var SPEC_VERSION_SUPPORTS_SLOT_IDENTITY;
101929
- var SPEC_VERSION_SUPPORTS_SEALED_LOG;
102650
+ var SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM;
101930
102651
  var SPEC_VERSION_CURRENT;
101931
102652
  var SPEC_VERSION_MAX_SUPPORTED;
101932
102653
  var init_spec_version = __esm2({
@@ -101938,9 +102659,9 @@ var init_spec_version = __esm2({
101938
102659
  SPEC_VERSION_SUPPORTS_ATTRIBUTES = 4;
101939
102660
  SPEC_VERSION_SUPPORTS_COMPRESSION = 5;
101940
102661
  SPEC_VERSION_SUPPORTS_SLOT_IDENTITY = 6;
101941
- SPEC_VERSION_SUPPORTS_SEALED_LOG = 7;
101942
- SPEC_VERSION_CURRENT = SPEC_VERSION_SUPPORTS_SEALED_LOG;
101943
- SPEC_VERSION_MAX_SUPPORTED = SPEC_VERSION_SUPPORTS_SEALED_LOG;
102662
+ SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM = 8;
102663
+ SPEC_VERSION_CURRENT = SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM;
102664
+ SPEC_VERSION_MAX_SUPPORTED = SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM;
101944
102665
  __name2(isLegacySpecVersion, "isLegacySpecVersion");
101945
102666
  }
101946
102667
  });
@@ -126901,7 +127622,30 @@ var init_events = __esm2({
126901
127622
  tokenRetentionUntil: external_exports.coerce.date().optional(),
126902
127623
  metadata: SerializedDataSchema.optional(),
126903
127624
  isWebhook: external_exports.boolean().optional(),
126904
- isSystem: external_exports.boolean().optional()
127625
+ isSystem: external_exports.boolean().optional(),
127626
+ /**
127627
+ * `createHook({ experimental_force: true })`: when the token belongs to
127628
+ * another live run, the World takes it over — journaling
127629
+ * `hook_disposed{forceClaimedBy}` in that run's log and re-pointing the
127630
+ * token — instead of answering `hook_conflict`. Requires the
127631
+ * `hookForceClaim` capability.
127632
+ */
127633
+ force: external_exports.boolean().optional(),
127634
+ /**
127635
+ * World-written on a forced creation that did take a token over: the
127636
+ * run and hook it was taken from, plus what a queue message to that run
127637
+ * needs (`workflowName`, `deploymentId`, `runSpecVersion`). Absent when
127638
+ * the token was free. The wake-targeting fields are in the LOG, not only
127639
+ * on the hook entity, so a replay can republish the victim's wake from
127640
+ * the row alone; see `publishForceClaimVictimWake` in @workflow/core.
127641
+ */
127642
+ forceClaimedFrom: external_exports.object({
127643
+ runId: external_exports.string(),
127644
+ hookId: external_exports.string(),
127645
+ workflowName: external_exports.string().optional(),
127646
+ deploymentId: external_exports.string().optional(),
127647
+ runSpecVersion: external_exports.number().optional()
127648
+ }).optional()
126905
127649
  })
126906
127650
  }));
126907
127651
  HookReceivedEventSchema = external_exports.compile(BaseEventSchema.extend({
@@ -126916,7 +127660,17 @@ var init_events = __esm2({
126916
127660
  eventType: external_exports.literal("hook_disposed"),
126917
127661
  correlationId: external_exports.string(),
126918
127662
  eventData: external_exports.object({
126919
- token: external_exports.string().optional()
127663
+ token: external_exports.string().optional(),
127664
+ /**
127665
+ * World-written: this disposal was not the run's own. Another run
127666
+ * took the hook's token with `experimental_force`, and this names
127667
+ * it. The hook consumer rejects the hook's awaiters with
127668
+ * `HookForceClaimedError` when it reads this row.
127669
+ */
127670
+ forceClaimedBy: external_exports.object({
127671
+ runId: external_exports.string(),
127672
+ hookId: external_exports.string()
127673
+ }).optional()
126920
127674
  }).optional()
126921
127675
  }));
126922
127676
  HookConflictEventSchema = external_exports.compile(BaseEventSchema.extend({
@@ -126926,7 +127680,18 @@ var init_events = __esm2({
126926
127680
  token: external_exports.string(),
126927
127681
  // TODO: Make this required once all persisted hook_conflict events and
126928
127682
  // remote World implementations always include the active hook owner's run ID.
126929
- conflictingRunId: external_exports.string().optional()
127683
+ conflictingRunId: external_exports.string().optional(),
127684
+ /**
127685
+ * Set when the creation asked for `force` and the World declined to take
127686
+ * the token over. `victim-spec-version`: the run holding the token was
127687
+ * started at a spec version below
127688
+ * `SPEC_VERSION_SUPPORTS_HOOK_FORCE_CLAIM`, so its runtime would not
127689
+ * understand the involuntary disposal — the forced hook gets the
127690
+ * ordinary `HookConflictError` it opted out of instead of stranding
127691
+ * that run. Absent on a conflict answered by a World that does not
127692
+ * implement forcing at all.
127693
+ */
127694
+ forceRefusedReason: external_exports.literal("victim-spec-version").optional()
126930
127695
  })
126931
127696
  }));
126932
127697
  NoopEventSchema = external_exports.compile(BaseEventSchema.extend({
@@ -127812,6 +128577,7 @@ var HookResumeContextSchema;
127812
128577
  var HOOK_RESUME_INPUT_VERSION;
127813
128578
  var HOOK_RESUME_DEDUP_VERSION;
127814
128579
  var HookResumeCapabilitiesSchema;
128580
+ var HookClaimedFromSchema;
127815
128581
  var HookSchema;
127816
128582
  var init_hooks = __esm2({
127817
128583
  "../world/dist/hooks.js"() {
@@ -127853,6 +128619,13 @@ var init_hooks = __esm2({
127853
128619
  // is engaged.
127854
128620
  hookResumeDedupVersion: external_exports.number()
127855
128621
  }));
128622
+ HookClaimedFromSchema = external_exports.compile(external_exports.object({
128623
+ runId: external_exports.string(),
128624
+ hookId: external_exports.string(),
128625
+ workflowName: external_exports.string().optional(),
128626
+ deploymentId: external_exports.string().optional(),
128627
+ runSpecVersion: external_exports.number().optional()
128628
+ }));
127856
128629
  HookSchema = external_exports.compile(external_exports.object({
127857
128630
  runId: external_exports.string(),
127858
128631
  hookId: external_exports.string(),
@@ -127880,7 +128653,12 @@ var init_hooks = __esm2({
127880
128653
  // `resumeHook()` gates its lazy path on this being present and
127881
128654
  // current. Absent against an older/rolled-back server or when the kill switch
127882
128655
  // is active.
127883
- resumeCapabilities: HookResumeCapabilitiesSchema.optional()
128656
+ resumeCapabilities: HookResumeCapabilitiesSchema.optional(),
128657
+ // Set when this hook took its token from another run
128658
+ // (`experimental_force`). The wake-targeting fields are the victim run's,
128659
+ // so the claimer's runtime can publish the victim's wake without reading
128660
+ // a run it may not be able to reach; see `HookClaimedFromSchema`.
128661
+ claimedFrom: HookClaimedFromSchema.optional()
127884
128662
  }));
127885
128663
  }
127886
128664
  });
@@ -128618,6 +129396,20 @@ function getCommonReducers(_global2 = globalThis) {
128618
129396
  }
128619
129397
  return reduced;
128620
129398
  }, "HookConflictError"),
129399
+ HookForceClaimedError: /* @__PURE__ */ __name2((value) => {
129400
+ const base = reduceNamedErrorSubclassBase("HookForceClaimedError", value);
129401
+ if (!base) return false;
129402
+ const reduced = {
129403
+ ...base,
129404
+ token: readProperty(value, "token"),
129405
+ claimedByRunId: readProperty(value, "claimedByRunId")
129406
+ };
129407
+ const claimedByHookId = readProperty(value, "claimedByHookId");
129408
+ if (claimedByHookId !== void 0) {
129409
+ reduced.claimedByHookId = claimedByHookId;
129410
+ }
129411
+ return reduced;
129412
+ }, "HookForceClaimedError"),
128621
129413
  RangeError: makeErrorSubclassReducer("RangeError"),
128622
129414
  ReferenceError: makeErrorSubclassReducer("ReferenceError"),
128623
129415
  // RetryableError carries an extra `retryAfter` Date that we serialize as
@@ -128793,6 +129585,15 @@ function getCommonRevivers(global2 = globalThis) {
128793
129585
  }
128794
129586
  return error612;
128795
129587
  }, "HookConflictError"),
129588
+ HookForceClaimedError: /* @__PURE__ */ __name2((value) => {
129589
+ const Ctor = global2[/* @__PURE__ */ Symbol.for("@workflow/errors//HookForceClaimedError")] ?? HookForceClaimedError;
129590
+ const error612 = new Ctor(value.token, value.claimedByRunId, value.claimedByHookId);
129591
+ if (value.stack !== void 0) error612.stack = value.stack;
129592
+ if ("cause" in value) {
129593
+ error612.cause = value.cause;
129594
+ }
129595
+ return error612;
129596
+ }, "HookForceClaimedError"),
128796
129597
  RangeError: makeErrorSubclassReviver(global2, "RangeError"),
128797
129598
  ReferenceError: makeErrorSubclassReviver(global2, "ReferenceError"),
128798
129599
  RetryableError: /* @__PURE__ */ __name2((value) => {
@@ -134375,7 +135176,7 @@ var version2;
134375
135176
  var init_version = __esm2({
134376
135177
  "../core/dist/version.js"() {
134377
135178
  "use strict";
134378
- version2 = "5.0.0-beta.55";
135179
+ version2 = "5.0.0-beta.56";
134379
135180
  }
134380
135181
  });
134381
135182
  function getWorkflowQueueName(workflowName, namespace) {
@@ -134562,6 +135363,56 @@ var init_helpers = __esm2({
134562
135363
  __name2(healthCheck, "healthCheck");
134563
135364
  }
134564
135365
  });
135366
+ function isRetryableWakeError(error612, isDeploymentUnavailableError) {
135367
+ if (isDeploymentUnavailableError?.(error612)) return false;
135368
+ const status = error612 ?? {};
135369
+ const code22 = status.status ?? status.statusCode;
135370
+ if (typeof code22 === "number") {
135371
+ return code22 >= 500 || code22 === 408 || code22 === 429;
135372
+ }
135373
+ const name = error612?.name;
135374
+ return name !== "BadRequestError" && name !== "UnauthorizedError" && name !== "ForbiddenError";
135375
+ }
135376
+ __name(isRetryableWakeError, "isRetryableWakeError");
135377
+ async function publishHookWakeWithRetry(publish, isDeploymentUnavailableError) {
135378
+ let lastError;
135379
+ for (let attempt = 0; attempt <= HOOK_WAKE_RETRY_DELAYS_MS.length; attempt++) {
135380
+ try {
135381
+ await publish();
135382
+ return;
135383
+ } catch (error612) {
135384
+ lastError = error612;
135385
+ if (!isRetryableWakeError(error612, isDeploymentUnavailableError)) break;
135386
+ const delayMs = HOOK_WAKE_RETRY_DELAYS_MS[attempt];
135387
+ if (delayMs !== void 0) {
135388
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
135389
+ }
135390
+ }
135391
+ }
135392
+ if (HookNotFoundError.is(lastError)) {
135393
+ throw new WorkflowRuntimeError("The hook resume was committed, but its workflow wake could not be published", {
135394
+ cause: lastError
135395
+ });
135396
+ }
135397
+ throw lastError;
135398
+ }
135399
+ __name(publishHookWakeWithRetry, "publishHookWakeWithRetry");
135400
+ var HOOK_WAKE_RETRY_DELAYS_MS;
135401
+ var init_hook_wake = __esm2({
135402
+ "../core/dist/runtime/hook-wake.js"() {
135403
+ "use strict";
135404
+ init_dist2();
135405
+ init_dist4();
135406
+ init_logger();
135407
+ init_helpers();
135408
+ HOOK_WAKE_RETRY_DELAYS_MS = [
135409
+ 25,
135410
+ 100
135411
+ ];
135412
+ __name2(isRetryableWakeError, "isRetryableWakeError");
135413
+ __name2(publishHookWakeWithRetry, "publishHookWakeWithRetry");
135414
+ }
135415
+ });
134565
135416
  var HookInvocationSchema;
134566
135417
  var HookInvocationResultSchema;
134567
135418
  var init_invocations = __esm2({
@@ -139611,40 +140462,6 @@ async function computeResumePayloadDigest(bytes) {
139611
140462
  return hex32;
139612
140463
  }
139613
140464
  __name(computeResumePayloadDigest, "computeResumePayloadDigest");
139614
- function isRetryableWakeError(error612, isDeploymentUnavailableError) {
139615
- if (isDeploymentUnavailableError?.(error612)) return false;
139616
- const status = error612 ?? {};
139617
- const code22 = status.status ?? status.statusCode;
139618
- if (typeof code22 === "number") {
139619
- return code22 >= 500 || code22 === 408 || code22 === 429;
139620
- }
139621
- const name = error612?.name;
139622
- return name !== "BadRequestError" && name !== "UnauthorizedError" && name !== "ForbiddenError";
139623
- }
139624
- __name(isRetryableWakeError, "isRetryableWakeError");
139625
- async function publishHookWakeWithRetry(publish, isDeploymentUnavailableError) {
139626
- let lastError;
139627
- for (let attempt = 0; attempt <= HOOK_WAKE_RETRY_DELAYS_MS.length; attempt++) {
139628
- try {
139629
- await publish();
139630
- return;
139631
- } catch (error612) {
139632
- lastError = error612;
139633
- if (!isRetryableWakeError(error612, isDeploymentUnavailableError)) break;
139634
- const delayMs = HOOK_WAKE_RETRY_DELAYS_MS[attempt];
139635
- if (delayMs !== void 0) {
139636
- await new Promise((resolve3) => setTimeout(resolve3, delayMs));
139637
- }
139638
- }
139639
- }
139640
- if (HookNotFoundError.is(lastError)) {
139641
- throw new WorkflowRuntimeError("The hook resume was committed, but its workflow wake could not be published", {
139642
- cause: lastError
139643
- });
139644
- }
139645
- throw lastError;
139646
- }
139647
- __name(publishHookWakeWithRetry, "publishHookWakeWithRetry");
139648
140465
  function resumeContextFromRun(run22) {
139649
140466
  const coreVersion = run22.executionContext?.workflowCoreVersion;
139650
140467
  const traceCarrier = run22.executionContext?.traceCarrier;
@@ -139734,164 +140551,269 @@ async function resumeHook(tokenOrHook, payload, encryptionKeyOverride) {
139734
140551
  return resumeHookImpl(tokenOrHook, payload, encryptionKeyOverride, false, Date.now());
139735
140552
  }
139736
140553
  __name(resumeHook, "resumeHook");
139737
- async function resumeHookImpl(tokenOrHook, payload, encryptionKeyOverride, hookFreshlyLookedUp, resumeRequestedAtMs) {
140554
+ async function resumeHookImpl(tokenOrHook, payload, encryptionKeyOverride, hookFreshlyLookedUp, resumeRequestedAtMs, webhookOnly = false) {
139738
140555
  return await waitedUntil(() => {
139739
140556
  return trace("hook.resume", async (span) => {
139740
140557
  const world = await getWorldLazy();
139741
- try {
139742
- const suppliedToken = typeof tokenOrHook === "string";
139743
- const hook = suppliedToken ? await world.hooks.getByToken(tokenOrHook) : tokenOrHook;
139744
- const hookResumeCapabilitiesAreFresh = suppliedToken || hookFreshlyLookedUp;
139745
- const info = await resolveHookResumeInfo(hook);
139746
- const { resumeContext } = info;
139747
- span?.setAttributes({
139748
- ...HookToken(hook.token),
139749
- ...HookId(hook.hookId),
139750
- ...WorkflowRunId(hook.runId),
139751
- "workflow.hook.resume_context_source": info.source
139752
- });
139753
- if (info.run && isTerminalWorkflowRunStatus(info.run.status)) {
139754
- throw new HookNotFoundError(hook.token);
139755
- }
139756
- const capabilities = getRunCapabilities(resumeContext.workflowCoreVersion);
139757
- let payloadKey;
139758
- const runPublicKey = encryptionKeyOverride ? (
139759
- // hydrate hook metadata), so sealing would add an ECDH for no
139760
- // saved round trip. Reuse what it resolved.
139761
- void 0
139762
- ) : decodeRunPublicKey(resumeContext.encryptionPublicKey);
139763
- if (runPublicKey) {
139764
- payloadKey = sealTo(runPublicKey);
139765
- } else {
139766
- let encryptionKey = encryptionKeyOverride ?? await resolveHookEncryptionKey(hook, info);
139767
- if (!capabilities.supportedFormats.has(SerializationFormat.ENCRYPTED)) {
139768
- encryptionKey = void 0;
139769
- }
139770
- payloadKey = encryptionKey;
139771
- }
139772
- const compression = (resumeContext.runSpecVersion ?? 0) >= SPEC_VERSION_SUPPORTS_COMPRESSION && capabilities.supportedFormats.has(SerializationFormat.GZIP);
139773
- const ops = [];
139774
- const readbackOps = [];
139775
- const v1Compat = isLegacySpecVersion(hook.specVersion);
139776
- const dehydratedPayload = await dehydrateStepReturnValue(payload, hook.runId, payloadKey, ops, globalThis, v1Compat, capabilities.framedByteStreams, compression, void 0, readbackOps);
139777
- await Promise.all(ops.map((op) => op.catch((error612) => {
139778
- if (error612 !== void 0) throw error612;
139779
- })));
139780
- safeWaitUntil(Promise.all(readbackOps), (err) => {
139781
- if (err === void 0) return;
139782
- runtimeLogger.warn("Background readback of hook payload failed", {
139783
- workflowRunId: hook.runId,
139784
- hookId: hook.hookId,
139785
- error: err instanceof Error ? err.message : String(err)
139786
- });
139787
- });
139788
- span?.setAttributes({
139789
- ...WorkflowName(resumeContext.workflowName)
139790
- });
139791
- const originLink = await linkToTraceCarrier(resumeContext.traceCarrier);
139792
- if (originLink) {
139793
- span?.addLink?.(originLink);
139794
- }
139795
- if (world.capabilities?.invoke === true && !v1Compat && dehydratedPayload instanceof Uint8Array) {
139796
- if (!world.invoke) {
139797
- throw new WorkflowRuntimeError("World advertises invoke without implementing it");
139798
- }
140558
+ const token = typeof tokenOrHook === "string" ? tokenOrHook : tokenOrHook.token;
140559
+ let target2 = tokenOrHook;
140560
+ let fresh = hookFreshlyLookedUp;
140561
+ let keyOverride = encryptionKeyOverride;
140562
+ let redirects = 0;
140563
+ let lookupsAfterRedirect = 0;
140564
+ const MAX_LOOKUPS_AFTER_REDIRECT = 5;
140565
+ const resumeId = generateResumeId();
140566
+ const assertResendable = /* @__PURE__ */ __name2((cause) => {
140567
+ if (payload instanceof Request && payload.body !== null && payload.bodyUsed) {
139799
140568
  span?.setAttributes({
139800
- "workflow.hook.resume_strategy": "invoke"
140569
+ "workflow.hook.resume_redirect_unresendable": true
139801
140570
  });
139802
- const result2 = HookInvocationResultSchema.parse(await world.invoke(hook.runId, {
139803
- type: "hook_resume",
139804
- version: 1,
139805
- hookId: hook.hookId,
139806
- token: hook.token,
139807
- payload: dehydratedPayload
139808
- }, {
139809
- idempotencyKey: generateResumeId()
139810
- }));
139811
- if (result2.status === "rejected") {
139812
- if (result2.code === "HOOK_NOT_FOUND") throw new HookNotFoundError(hook.token);
139813
- throw new WorkflowRuntimeError("Executor rejected the hook invocation input");
139814
- }
139815
- span?.setAttributes(HookResumeCommitted(true));
139816
- return asLazyMetadataHook(hook);
139817
- }
139818
- const queueName = getWorkflowQueueName(resumeContext.workflowName);
139819
- const queueOptions = {
139820
- deploymentId: resumeContext.deploymentId,
139821
- specVersion: resumeContext.runSpecVersion ?? SPEC_VERSION_LEGACY
139822
- };
139823
- const backendDedupSupported = (hookResumeCapabilitiesAreFresh ? hook.resumeCapabilities?.hookResumeDedupVersion ?? 0 : 0) >= HOOK_RESUME_DEDUP_VERSION || world.capabilities?.hookResumeDedup === true;
139824
- const canClaimResume = backendDedupSupported && !v1Compat && dehydratedPayload instanceof Uint8Array;
139825
- span?.setAttributes({
139826
- "workflow.hook.resume_strategy": "sequential"
139827
- });
139828
- const resumeId = canClaimResume ? generateResumeId() : void 0;
139829
- const payloadDigest = canClaimResume ? await computeResumePayloadDigest(dehydratedPayload) : void 0;
139830
- if (resumeId) {
139831
- span?.setAttributes({
139832
- "workflow.hook.resume_id": resumeId
140571
+ throw new WorkflowRuntimeError(`Hook token "${token}" changed owner while this webhook request was being delivered; a request body can be sent only once and it was not delivered anywhere \u2014 retry the request, which will reach the token's new owner`, {
140572
+ cause
139833
140573
  });
139834
140574
  }
139835
- const isHookGoneError = /* @__PURE__ */ __name2((err) => HookNotFoundError.is(err) || RunExpiredError.is(err), "isHookGoneError");
140575
+ }, "assertResendable");
140576
+ for (; ; ) {
139836
140577
  try {
139837
- await world.events.create(hook.runId, {
139838
- eventType: "hook_received",
139839
- specVersion: SPEC_VERSION_CURRENT,
139840
- correlationId: hook.hookId,
139841
- eventData: {
139842
- ...v1Compat ? {} : {
139843
- token: hook.token
139844
- },
139845
- payload: dehydratedPayload
139846
- }
139847
- }, {
139848
- v1Compat,
139849
- ...resumeId && payloadDigest ? {
139850
- resumeId,
139851
- resumePayloadDigest: payloadDigest
139852
- } : {}
139853
- });
140578
+ return await resumeHookAttempt(world, span, target2, payload, keyOverride, fresh, resumeRequestedAtMs, resumeId, webhookOnly);
139854
140579
  } catch (err) {
139855
- if (isHookGoneError(err)) {
139856
- throw new HookNotFoundError(hook.token);
140580
+ if (err !== null && typeof err === "object" && notFoundReasons.get(err) === "not-webhook") {
140581
+ throw err;
140582
+ }
140583
+ if (redirects >= MAX_FORCE_CLAIM_REDIRECTS) {
140584
+ if (HookForceClaimedError.is(err)) {
140585
+ span?.setAttributes({
140586
+ "workflow.hook.resume_redirects_exhausted": true
140587
+ });
140588
+ throw new WorkflowRuntimeError(`Hook token "${token}" changed owner ${redirects} times while this resume was in flight; nothing was delivered \u2014 retry the resume`, {
140589
+ cause: err
140590
+ });
140591
+ }
140592
+ throw err;
140593
+ }
140594
+ if (HookForceClaimedError.is(err)) {
140595
+ assertResendable(err);
140596
+ redirects++;
140597
+ span?.setAttributes({
140598
+ "workflow.hook.resume_redirects": redirects,
140599
+ "workflow.hook.resume_redirect_to_run": err.claimedByRunId
140600
+ });
140601
+ target2 = token;
140602
+ fresh = true;
140603
+ keyOverride = void 0;
140604
+ lookupsAfterRedirect = 0;
140605
+ continue;
140606
+ }
140607
+ if (HookNotFoundError.is(err) && redirects > 0 && typeof target2 === "string" && lookupsAfterRedirect < MAX_LOOKUPS_AFTER_REDIRECT) {
140608
+ assertResendable(err);
140609
+ lookupsAfterRedirect++;
140610
+ await new Promise((resolve3) => setTimeout(resolve3, 50));
140611
+ continue;
140612
+ }
140613
+ if (HookNotFoundError.is(err) && notFoundReasons.get(err) !== "lookup") {
140614
+ const attemptedHookId = typeof target2 === "string" ? void 0 : target2.hookId;
140615
+ let relocated;
140616
+ try {
140617
+ relocated = await world.hooks.getByToken(token);
140618
+ } catch (lookupError) {
140619
+ if (!HookNotFoundError.is(lookupError)) {
140620
+ runtimeLogger.warn("Hook resume: re-resolving the token after a not-found failed; surfacing the original error", {
140621
+ token,
140622
+ error: lookupError instanceof Error ? lookupError.message : String(lookupError)
140623
+ });
140624
+ }
140625
+ }
140626
+ if (relocated?.claimedFrom !== void 0 && relocated.hookId !== attemptedHookId && (attemptedHookId === void 0 || relocated.claimedFrom.hookId === attemptedHookId) && !(webhookOnly && relocated.isWebhook === false)) {
140627
+ assertResendable(err);
140628
+ redirects++;
140629
+ span?.setAttributes({
140630
+ "workflow.hook.resume_redirects": redirects,
140631
+ "workflow.hook.resume_redirect_to_run": relocated.runId
140632
+ });
140633
+ target2 = relocated;
140634
+ fresh = true;
140635
+ keyOverride = void 0;
140636
+ continue;
140637
+ }
139857
140638
  }
139858
140639
  throw err;
139859
140640
  }
139860
- span?.setAttributes(HookResumeCommitted(true));
139861
- const queuePublishRequestedAtMs = Date.now();
139862
- await publishHookWakeWithRetry(() => world.queue(queueName, {
139863
- runId: hook.runId,
139864
- traceCarrier: resumeContext.traceCarrier ?? void 0,
139865
- hookResumeTiming: {
139866
- resumeRequestedAtMs,
139867
- queuePublishRequestedAtMs,
139868
- strategy: "sequential"
139869
- }
139870
- }, {
139871
- ...queueOptions,
139872
- // Dedup retried publishes whose response was lost: a
139873
- // duplicate wake is harmless for correctness (deterministic
139874
- // replay) but costs a full replay of the run, and the queue
139875
- // accepts a repeated idempotency key by delivering only one
139876
- // of the messages. Claim-less writes have no resumeId and
139877
- // keep the previous behavior.
139878
- ...resumeId ? {
139879
- idempotencyKey: `hook-${resumeId}`
139880
- } : {}
139881
- }), world.isDeploymentUnavailableError?.bind(world));
139882
- span?.setAttributes(HookWakePublished(true));
139883
- return asLazyMetadataHook(hook);
139884
- } catch (err) {
139885
- span?.setAttributes({
139886
- ...HookToken(typeof tokenOrHook === "string" ? tokenOrHook : tokenOrHook.token),
139887
- ...HookFound(false)
139888
- });
139889
- throw err;
139890
140641
  }
139891
140642
  });
139892
140643
  });
139893
140644
  }
139894
140645
  __name(resumeHookImpl, "resumeHookImpl");
140646
+ async function resumeHookAttempt(world, span, tokenOrHook, payload, encryptionKeyOverride, hookFreshlyLookedUp, resumeRequestedAtMs, logicalResumeId, webhookOnly) {
140647
+ try {
140648
+ const suppliedToken = typeof tokenOrHook === "string";
140649
+ let hook;
140650
+ if (suppliedToken) {
140651
+ try {
140652
+ hook = await world.hooks.getByToken(tokenOrHook);
140653
+ } catch (lookupError) {
140654
+ if (HookNotFoundError.is(lookupError)) {
140655
+ notFoundReasons.set(lookupError, "lookup");
140656
+ }
140657
+ throw lookupError;
140658
+ }
140659
+ } else {
140660
+ hook = tokenOrHook;
140661
+ }
140662
+ if (webhookOnly && hook.isWebhook === false) {
140663
+ const notWebhook = new HookNotFoundError(hook.token);
140664
+ notFoundReasons.set(notWebhook, "not-webhook");
140665
+ throw notWebhook;
140666
+ }
140667
+ const hookResumeCapabilitiesAreFresh = suppliedToken || hookFreshlyLookedUp;
140668
+ const info = await resolveHookResumeInfo(hook);
140669
+ const { resumeContext } = info;
140670
+ span?.setAttributes({
140671
+ ...HookToken(hook.token),
140672
+ ...HookId(hook.hookId),
140673
+ ...WorkflowRunId(hook.runId),
140674
+ "workflow.hook.resume_context_source": info.source
140675
+ });
140676
+ if (info.run && isTerminalWorkflowRunStatus(info.run.status)) {
140677
+ throw new HookNotFoundError(hook.token);
140678
+ }
140679
+ const capabilities = getRunCapabilities(resumeContext.workflowCoreVersion);
140680
+ let payloadKey;
140681
+ const runPublicKey = encryptionKeyOverride ? (
140682
+ // hydrate hook metadata), so sealing would add an ECDH for no
140683
+ // saved round trip. Reuse what it resolved.
140684
+ void 0
140685
+ ) : decodeRunPublicKey(resumeContext.encryptionPublicKey);
140686
+ if (runPublicKey) {
140687
+ payloadKey = sealTo(runPublicKey);
140688
+ } else {
140689
+ let encryptionKey = encryptionKeyOverride ?? await resolveHookEncryptionKey(hook, info);
140690
+ if (!capabilities.supportedFormats.has(SerializationFormat.ENCRYPTED)) {
140691
+ encryptionKey = void 0;
140692
+ }
140693
+ payloadKey = encryptionKey;
140694
+ }
140695
+ const compression = (resumeContext.runSpecVersion ?? 0) >= SPEC_VERSION_SUPPORTS_COMPRESSION && capabilities.supportedFormats.has(SerializationFormat.GZIP);
140696
+ const ops = [];
140697
+ const readbackOps = [];
140698
+ const v1Compat = isLegacySpecVersion(hook.specVersion);
140699
+ const dehydratedPayload = await dehydrateStepReturnValue(payload, hook.runId, payloadKey, ops, globalThis, v1Compat, capabilities.framedByteStreams, compression, void 0, readbackOps);
140700
+ await Promise.all(ops.map((op) => op.catch((error612) => {
140701
+ if (error612 !== void 0) throw error612;
140702
+ })));
140703
+ safeWaitUntil(Promise.all(readbackOps), (err) => {
140704
+ if (err === void 0) return;
140705
+ runtimeLogger.warn("Background readback of hook payload failed", {
140706
+ workflowRunId: hook.runId,
140707
+ hookId: hook.hookId,
140708
+ error: err instanceof Error ? err.message : String(err)
140709
+ });
140710
+ });
140711
+ if (world.capabilities?.invoke === true && !v1Compat && dehydratedPayload instanceof Uint8Array) {
140712
+ if (!world.invoke) {
140713
+ throw new WorkflowRuntimeError("World advertises invoke without implementing it");
140714
+ }
140715
+ span?.setAttributes({
140716
+ "workflow.hook.resume_strategy": "invoke"
140717
+ });
140718
+ const result2 = HookInvocationResultSchema.parse(await world.invoke(hook.runId, {
140719
+ type: "hook_resume",
140720
+ version: 1,
140721
+ hookId: hook.hookId,
140722
+ token: hook.token,
140723
+ payload: dehydratedPayload
140724
+ }, {
140725
+ idempotencyKey: logicalResumeId
140726
+ }));
140727
+ if (result2.status === "rejected") {
140728
+ if (result2.code === "HOOK_NOT_FOUND") throw new HookNotFoundError(hook.token);
140729
+ throw new WorkflowRuntimeError("Executor rejected the hook invocation input");
140730
+ }
140731
+ span?.setAttributes(HookResumeCommitted(true));
140732
+ return asLazyMetadataHook(hook);
140733
+ }
140734
+ span?.setAttributes({
140735
+ ...WorkflowName(resumeContext.workflowName)
140736
+ });
140737
+ const originLink = await linkToTraceCarrier(resumeContext.traceCarrier);
140738
+ if (originLink) {
140739
+ span?.addLink?.(originLink);
140740
+ }
140741
+ const queueName = getWorkflowQueueName(resumeContext.workflowName);
140742
+ const queueOptions = {
140743
+ deploymentId: resumeContext.deploymentId,
140744
+ specVersion: resumeContext.runSpecVersion ?? SPEC_VERSION_LEGACY
140745
+ };
140746
+ const backendDedupSupported = (hookResumeCapabilitiesAreFresh ? hook.resumeCapabilities?.hookResumeDedupVersion ?? 0 : 0) >= HOOK_RESUME_DEDUP_VERSION || world.capabilities?.hookResumeDedup === true;
140747
+ const canClaimResume = backendDedupSupported && !v1Compat && dehydratedPayload instanceof Uint8Array;
140748
+ span?.setAttributes({
140749
+ "workflow.hook.resume_strategy": "sequential"
140750
+ });
140751
+ const resumeId = canClaimResume ? logicalResumeId : void 0;
140752
+ const payloadDigest = canClaimResume ? await computeResumePayloadDigest(dehydratedPayload) : void 0;
140753
+ if (resumeId) {
140754
+ span?.setAttributes({
140755
+ "workflow.hook.resume_id": resumeId
140756
+ });
140757
+ }
140758
+ const isHookGoneError = /* @__PURE__ */ __name2((err) => HookNotFoundError.is(err) || RunExpiredError.is(err), "isHookGoneError");
140759
+ try {
140760
+ await world.events.create(hook.runId, {
140761
+ eventType: "hook_received",
140762
+ specVersion: SPEC_VERSION_CURRENT,
140763
+ correlationId: hook.hookId,
140764
+ eventData: {
140765
+ ...v1Compat ? {} : {
140766
+ token: hook.token
140767
+ },
140768
+ payload: dehydratedPayload
140769
+ }
140770
+ }, {
140771
+ v1Compat,
140772
+ ...resumeId && payloadDigest ? {
140773
+ resumeId,
140774
+ resumePayloadDigest: payloadDigest
140775
+ } : {}
140776
+ });
140777
+ } catch (err) {
140778
+ if (HookForceClaimedError.is(err)) throw err;
140779
+ if (isHookGoneError(err)) {
140780
+ throw new HookNotFoundError(hook.token);
140781
+ }
140782
+ throw err;
140783
+ }
140784
+ span?.setAttributes(HookResumeCommitted(true));
140785
+ const queuePublishRequestedAtMs = Date.now();
140786
+ await publishHookWakeWithRetry(() => world.queue(queueName, {
140787
+ runId: hook.runId,
140788
+ traceCarrier: resumeContext.traceCarrier ?? void 0,
140789
+ hookResumeTiming: {
140790
+ resumeRequestedAtMs,
140791
+ queuePublishRequestedAtMs,
140792
+ strategy: "sequential"
140793
+ }
140794
+ }, {
140795
+ ...queueOptions,
140796
+ // Dedup retried publishes whose response was lost: a
140797
+ // duplicate wake is harmless for correctness (deterministic
140798
+ // replay) but costs a full replay of the run, and the queue
140799
+ // accepts a repeated idempotency key by delivering only one
140800
+ // of the messages. Claim-less writes have no resumeId and
140801
+ // keep the previous behavior.
140802
+ ...resumeId ? {
140803
+ idempotencyKey: `hook-${resumeId}`
140804
+ } : {}
140805
+ }), world.isDeploymentUnavailableError?.bind(world));
140806
+ span?.setAttributes(HookWakePublished(true));
140807
+ return asLazyMetadataHook(hook);
140808
+ } catch (err) {
140809
+ span?.setAttributes({
140810
+ ...HookToken(typeof tokenOrHook === "string" ? tokenOrHook : tokenOrHook.token),
140811
+ ...HookFound(false)
140812
+ });
140813
+ throw err;
140814
+ }
140815
+ }
140816
+ __name(resumeHookAttempt, "resumeHookAttempt");
139895
140817
  async function resumeWebhook(token, request) {
139896
140818
  const resumeRequestedAtMs = Date.now();
139897
140819
  const world = await getWorldLazy();
@@ -139919,7 +140841,7 @@ async function resumeWebhook(token, request) {
139919
140841
  status: 202
139920
140842
  });
139921
140843
  }
139922
- await resumeHookImpl(hook, request, metadataEncryptionKey(), true, resumeRequestedAtMs);
140844
+ await resumeHookImpl(hook, request, metadataEncryptionKey(), true, resumeRequestedAtMs, true);
139923
140845
  if (responseReadable) {
139924
140846
  const reader = responseReadable.getReader();
139925
140847
  const chunk = await reader.read();
@@ -139937,7 +140859,8 @@ async function resumeWebhook(token, request) {
139937
140859
  }
139938
140860
  __name(resumeWebhook, "resumeWebhook");
139939
140861
  var generateResumeId;
139940
- var HOOK_WAKE_RETRY_DELAYS_MS;
140862
+ var MAX_FORCE_CLAIM_REDIRECTS;
140863
+ var notFoundReasons;
139941
140864
  var init_resume_hook = __esm2({
139942
140865
  "../core/dist/runtime/resume-hook.js"() {
139943
140866
  "use strict";
@@ -139955,16 +140878,11 @@ var init_resume_hook = __esm2({
139955
140878
  init_telemetry();
139956
140879
  init_get_world_lazy();
139957
140880
  init_helpers();
140881
+ init_hook_wake();
139958
140882
  init_invocations();
139959
140883
  init_wait_until();
139960
140884
  generateResumeId = monotonicFactory();
139961
140885
  __name2(computeResumePayloadDigest, "computeResumePayloadDigest");
139962
- HOOK_WAKE_RETRY_DELAYS_MS = [
139963
- 25,
139964
- 100
139965
- ];
139966
- __name2(isRetryableWakeError, "isRetryableWakeError");
139967
- __name2(publishHookWakeWithRetry, "publishHookWakeWithRetry");
139968
140886
  __name2(resumeContextFromRun, "resumeContextFromRun");
139969
140887
  __name2(resolveHookResumeInfo, "resolveHookResumeInfo");
139970
140888
  __name2(resolveHookEncryptionKey, "resolveHookEncryptionKey");
@@ -139973,7 +140891,10 @@ var init_resume_hook = __esm2({
139973
140891
  __name2(asLazyMetadataHook, "asLazyMetadataHook");
139974
140892
  __name2(getHookByToken, "getHookByToken");
139975
140893
  __name2(resumeHook, "resumeHook");
140894
+ MAX_FORCE_CLAIM_REDIRECTS = 3;
140895
+ notFoundReasons = /* @__PURE__ */ new WeakMap();
139976
140896
  __name2(resumeHookImpl, "resumeHookImpl");
140897
+ __name2(resumeHookAttempt, "resumeHookAttempt");
139977
140898
  __name2(resumeWebhook, "resumeWebhook");
139978
140899
  }
139979
140900
  });
@@ -142412,7 +143333,7 @@ __name2(start, "start");
142412
143333
  value: "start",
142413
143334
  configurable: true
142414
143335
  });
142415
- })(start, "step//@workflow/core/runtime/start@5.0.0-beta.55//start");
143336
+ })(start, "step//@workflow/core/runtime/start@5.0.0-beta.56//start");
142416
143337
  async function wakeUpRun(world, runId, options) {
142417
143338
  try {
142418
143339
  const run22 = await world.runs.get(runId, {
@@ -142887,7 +143808,7 @@ var Run = class _Run {
142887
143808
  value: "wakeUp",
142888
143809
  configurable: true
142889
143810
  });
142890
- })(Run.prototype["wakeUp"], "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#wakeUp");
143811
+ })(Run.prototype["wakeUp"], "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#wakeUp");
142891
143812
  (function(__wf_fn, __wf_id) {
142892
143813
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142893
143814
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142896,7 +143817,7 @@ var Run = class _Run {
142896
143817
  value: "cancel",
142897
143818
  configurable: true
142898
143819
  });
142899
- })(Run.prototype["cancel"], "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#cancel");
143820
+ })(Run.prototype["cancel"], "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#cancel");
142900
143821
  (function(__wf_fn, __wf_id) {
142901
143822
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142902
143823
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142905,7 +143826,7 @@ var Run = class _Run {
142905
143826
  value: "getReadable",
142906
143827
  configurable: true
142907
143828
  });
142908
- })(Run.prototype["getReadable"], "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#getReadable");
143829
+ })(Run.prototype["getReadable"], "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#getReadable");
142909
143830
  (function(__wf_fn, __wf_id) {
142910
143831
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142911
143832
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142914,7 +143835,7 @@ var Run = class _Run {
142914
143835
  value: "getWritable",
142915
143836
  configurable: true
142916
143837
  });
142917
- })(Run.prototype["getWritable"], "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#getWritable");
143838
+ })(Run.prototype["getWritable"], "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#getWritable");
142918
143839
  (function(__wf_fn, __wf_id) {
142919
143840
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142920
143841
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142923,7 +143844,7 @@ var Run = class _Run {
142923
143844
  value: "exists",
142924
143845
  configurable: true
142925
143846
  });
142926
- })(Object.getOwnPropertyDescriptor(Run.prototype, "exists").get, "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#exists");
143847
+ })(Object.getOwnPropertyDescriptor(Run.prototype, "exists").get, "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#exists");
142927
143848
  (function(__wf_fn, __wf_id) {
142928
143849
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142929
143850
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142932,7 +143853,7 @@ var Run = class _Run {
142932
143853
  value: "status",
142933
143854
  configurable: true
142934
143855
  });
142935
- })(Object.getOwnPropertyDescriptor(Run.prototype, "status").get, "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#status");
143856
+ })(Object.getOwnPropertyDescriptor(Run.prototype, "status").get, "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#status");
142936
143857
  (function(__wf_fn, __wf_id) {
142937
143858
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142938
143859
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142941,7 +143862,7 @@ var Run = class _Run {
142941
143862
  value: "returnValue",
142942
143863
  configurable: true
142943
143864
  });
142944
- })(Object.getOwnPropertyDescriptor(Run.prototype, "returnValue").get, "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#returnValue");
143865
+ })(Object.getOwnPropertyDescriptor(Run.prototype, "returnValue").get, "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#returnValue");
142945
143866
  (function(__wf_fn, __wf_id) {
142946
143867
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142947
143868
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142950,7 +143871,7 @@ var Run = class _Run {
142950
143871
  value: "workflowName",
142951
143872
  configurable: true
142952
143873
  });
142953
- })(Object.getOwnPropertyDescriptor(Run.prototype, "workflowName").get, "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#workflowName");
143874
+ })(Object.getOwnPropertyDescriptor(Run.prototype, "workflowName").get, "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#workflowName");
142954
143875
  (function(__wf_fn, __wf_id) {
142955
143876
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142956
143877
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142959,7 +143880,7 @@ var Run = class _Run {
142959
143880
  value: "createdAt",
142960
143881
  configurable: true
142961
143882
  });
142962
- })(Object.getOwnPropertyDescriptor(Run.prototype, "createdAt").get, "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#createdAt");
143883
+ })(Object.getOwnPropertyDescriptor(Run.prototype, "createdAt").get, "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#createdAt");
142963
143884
  (function(__wf_fn, __wf_id) {
142964
143885
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142965
143886
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142968,7 +143889,7 @@ var Run = class _Run {
142968
143889
  value: "startedAt",
142969
143890
  configurable: true
142970
143891
  });
142971
- })(Object.getOwnPropertyDescriptor(Run.prototype, "startedAt").get, "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#startedAt");
143892
+ })(Object.getOwnPropertyDescriptor(Run.prototype, "startedAt").get, "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#startedAt");
142972
143893
  (function(__wf_fn, __wf_id) {
142973
143894
  var __wf_sym = /* @__PURE__ */ Symbol.for("@workflow/core//registeredSteps"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142974
143895
  __wf_reg.set(__wf_id, __wf_fn);
@@ -142977,7 +143898,7 @@ var Run = class _Run {
142977
143898
  value: "completedAt",
142978
143899
  configurable: true
142979
143900
  });
142980
- })(Object.getOwnPropertyDescriptor(Run.prototype, "completedAt").get, "step//@workflow/core/runtime/run@5.0.0-beta.55//Run#completedAt");
143901
+ })(Object.getOwnPropertyDescriptor(Run.prototype, "completedAt").get, "step//@workflow/core/runtime/run@5.0.0-beta.56//Run#completedAt");
142981
143902
  (function(__wf_cls, __wf_id) {
142982
143903
  var __wf_sym = /* @__PURE__ */ Symbol.for("workflow-class-registry"), __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = /* @__PURE__ */ new Map());
142983
143904
  __wf_reg.set(__wf_id, __wf_cls);
@@ -142987,7 +143908,7 @@ var Run = class _Run {
142987
143908
  enumerable: false,
142988
143909
  configurable: false
142989
143910
  });
142990
- })(Run, "class//@workflow/core/runtime/run@5.0.0-beta.55//Run");
143911
+ })(Run, "class//@workflow/core/runtime/run@5.0.0-beta.56//Run");
142991
143912
  async function fetch2(...args) {
142992
143913
  return globalThis.fetch(...args);
142993
143914
  }
@@ -143827,6 +144748,60 @@ __name(guardDeploymentAffinity, "guardDeploymentAffinity");
143827
144748
  init_helpers3();
143828
144749
  init_invocations2();
143829
144750
  init_lifecycle_hooks();
144751
+
144752
+ // ../core/dist/runtime/open-hook-wait-state.js
144753
+ function openHookAndWaitState(events) {
144754
+ const hooks = /* @__PURE__ */ new Set();
144755
+ const waits = /* @__PURE__ */ new Map();
144756
+ for (const event of events) {
144757
+ switch (event.eventType) {
144758
+ case "hook_created":
144759
+ hooks.add(event.correlationId);
144760
+ break;
144761
+ case "hook_disposed":
144762
+ hooks.delete(event.correlationId);
144763
+ break;
144764
+ case "wait_created":
144765
+ waits.set(event.correlationId, resumeAtMs(event.eventData?.resumeAt));
144766
+ break;
144767
+ case "wait_completed":
144768
+ waits.delete(event.correlationId);
144769
+ break;
144770
+ }
144771
+ }
144772
+ let earliestOpenWaitResumeAtMs;
144773
+ for (const at of waits.values()) {
144774
+ if (earliestOpenWaitResumeAtMs === void 0 || at < earliestOpenWaitResumeAtMs) {
144775
+ earliestOpenWaitResumeAtMs = at;
144776
+ }
144777
+ }
144778
+ return {
144779
+ openHook: hooks.size > 0,
144780
+ openWait: waits.size > 0,
144781
+ earliestOpenWaitResumeAtMs
144782
+ };
144783
+ }
144784
+ __name(openHookAndWaitState, "openHookAndWaitState");
144785
+ function resumeAtMs(resumeAt) {
144786
+ let ms3;
144787
+ if (resumeAt instanceof Date) {
144788
+ ms3 = resumeAt.getTime();
144789
+ } else if (typeof resumeAt === "number") {
144790
+ ms3 = resumeAt;
144791
+ } else if (typeof resumeAt === "string") {
144792
+ ms3 = new Date(resumeAt).getTime();
144793
+ } else {
144794
+ return -Infinity;
144795
+ }
144796
+ return Number.isFinite(ms3) ? ms3 : -Infinity;
144797
+ }
144798
+ __name(resumeAtMs, "resumeAtMs");
144799
+ function hasOpenWaitDueBy(state, deadlineMs) {
144800
+ return state.earliestOpenWaitResumeAtMs !== void 0 && state.earliestOpenWaitResumeAtMs <= deadlineMs;
144801
+ }
144802
+ __name(hasOpenWaitDueBy, "hasOpenWaitDueBy");
144803
+
144804
+ // ../core/dist/runtime.js
143830
144805
  init_replay_budget();
143831
144806
  init_replay_recovery_reporter();
143832
144807
  init_resume_latency();
@@ -143890,9 +144865,10 @@ init_util5();
143890
144865
  init_compute_instance();
143891
144866
  init_constants3();
143892
144867
  init_helpers3();
144868
+ init_hook_wake2();
143893
144869
  init_replay_recovery_reporter();
143894
144870
  init_unserializable_step();
143895
- async function createHookEvent({ runId, hookEvent, queueItem, requestId, sinceCursor, createEvent }) {
144871
+ async function createHookEvent({ runId, hookEvent, queueItem, requestId, sinceCursor, createEvent, world }) {
143896
144872
  try {
143897
144873
  const result2 = await createEvent(hookEvent, {
143898
144874
  requestId,
@@ -143904,6 +144880,15 @@ async function createHookEvent({ runId, hookEvent, queueItem, requestId, sinceCu
143904
144880
  hasAwaitedHookCreation: false
143905
144881
  };
143906
144882
  }
144883
+ if (result2.hook?.claimedFrom) {
144884
+ const outcome = await publishForceClaimVictimWake(world, runId, result2.hook);
144885
+ runtimeLogger2.info("Hook token force-claimed from another run", {
144886
+ workflowRunId: runId,
144887
+ hookId: queueItem.correlationId,
144888
+ victimRunId: result2.hook.claimedFrom.runId,
144889
+ victimWake: outcome
144890
+ });
144891
+ }
143907
144892
  return {
143908
144893
  hasHookConflict: false,
143909
144894
  hasAwaitedHookCreation: queueItem.hasConflictAwaiter === true
@@ -143940,6 +144925,7 @@ async function createHookEvent({ runId, hookEvent, queueItem, requestId, sinceCu
143940
144925
  __name(createHookEvent, "createHookEvent");
143941
144926
  async function handleSuspension({ suspension, world, run: run3, span, requestId, eventLog, runReadyBarrier, replayRecoveryReporter, stepDispatch, ownerMessageId, allowDeferredBatchWork }) {
143942
144927
  const runId = run3.runId;
144928
+ await republishOwedForceClaimVictimWake(world, runId, eventLog?.events);
143943
144929
  const ensureRunReady = /* @__PURE__ */ __name(async () => {
143944
144930
  if (runReadyBarrier) {
143945
144931
  try {
@@ -144070,7 +145056,7 @@ async function handleSuspension({ suspension, world, run: run3, span, requestId,
144070
145056
  if (hookItemsByToken.size > 0) {
144071
145057
  const hookPhaseStart = Date.now();
144072
145058
  await ensureRunReady();
144073
- await settlePhase([...hookItemsByToken.values()].map(async (items) => {
145059
+ const processHookGroup = /* @__PURE__ */ __name(async (items) => {
144074
145060
  for (const queueItem of items) {
144075
145061
  let creationConflicted = false;
144076
145062
  if (!queueItem.hasCreatedEvent) {
@@ -144087,7 +145073,8 @@ async function handleSuspension({ suspension, world, run: run3, span, requestId,
144087
145073
  tokenRetentionUntil: queueItem.tokenRetentionUntil,
144088
145074
  metadata: hookMetadata,
144089
145075
  isWebhook: queueItem.isWebhook ?? false,
144090
- ...queueItem.isSystem && { isSystem: true }
145076
+ ...queueItem.isSystem && { isSystem: true },
145077
+ ...queueItem.force && { force: true }
144091
145078
  }
144092
145079
  };
144093
145080
  const result2 = await createHookEvent({
@@ -144096,7 +145083,8 @@ async function handleSuspension({ suspension, world, run: run3, span, requestId,
144096
145083
  queueItem,
144097
145084
  requestId,
144098
145085
  sinceCursor: hookDeltaCursor,
144099
- createEvent: createGuarded
145086
+ createEvent: createGuarded,
145087
+ world
144100
145088
  });
144101
145089
  if (result2.hasHookConflict) {
144102
145090
  hookConflictCorrelationIds.push(queueItem.correlationId);
@@ -144110,7 +145098,13 @@ async function handleSuspension({ suspension, world, run: run3, span, requestId,
144110
145098
  await disposeHook(queueItem);
144111
145099
  }
144112
145100
  }
144113
- }));
145101
+ }, "processHookGroup");
145102
+ const groups = [...hookItemsByToken.values()];
145103
+ const holdsForcedCreation = /* @__PURE__ */ __name((items) => items.some((item) => item.force === true && !item.hasCreatedEvent), "holdsForcedCreation");
145104
+ for (const items of groups.filter(holdsForcedCreation)) {
145105
+ await settlePhase([processHookGroup(items)]);
145106
+ }
145107
+ await settlePhase(groups.filter((items) => !holdsForcedCreation(items)).map(processHookGroup));
144114
145108
  hookCreationMs = Date.now() - hookPhaseStart;
144115
145109
  }
144116
145110
  const hooksNeedingAbort = allHookItems.filter((item) => item.abortRequested && !item.disposed);
@@ -144704,13 +145698,14 @@ async function handleSuspension({ suspension, world, run: run3, span, requestId,
144704
145698
  const now2 = Date.now();
144705
145699
  let soonestWait;
144706
145700
  for (const queueItem of waitItems) {
144707
- const resumeAtMs = queueItem.resumeAt.getTime();
144708
- const delayMs = Math.max(1e3, resumeAtMs - now2);
145701
+ const resumeAtMs2 = queueItem.resumeAt.getTime();
145702
+ const delayMs = Math.max(1e3, resumeAtMs2 - now2);
144709
145703
  const timeoutSeconds = Math.ceil(delayMs / 1e3);
144710
145704
  if (!soonestWait || timeoutSeconds < soonestWait.seconds) {
144711
145705
  soonestWait = {
144712
145706
  seconds: timeoutSeconds,
144713
- correlationId: queueItem.correlationId
145707
+ correlationId: queueItem.correlationId,
145708
+ resumeAtMs: resumeAtMs2
144714
145709
  };
144715
145710
  }
144716
145711
  }
@@ -145705,6 +146700,17 @@ function createCreateHook(ctx) {
145705
146700
  if (options.experimental_minRetention !== void 0 && ctx.worldCapabilities?.hookRetention?.active !== true) {
145706
146701
  throw new FatalError2("The configured World does not support `experimental_minRetention` for Hooks.");
145707
146702
  }
146703
+ if (options.experimental_force === true) {
146704
+ if (options.token === void 0 || options.token === null) {
146705
+ throw new Error("`createHook()` was called with `experimental_force: true` but no `token`. Force-claiming only applies to an explicit token another run may hold.");
146706
+ }
146707
+ if (options.isWebhook === true) {
146708
+ throw new Error("Webhook hooks do not support `experimental_force`. Use a non-webhook `createHook()` with an explicit token.");
146709
+ }
146710
+ if (ctx.worldCapabilities?.hookForceClaim !== true) {
146711
+ throw new FatalError2("The configured World does not support `experimental_force` for Hooks.");
146712
+ }
146713
+ }
145708
146714
  const correlationId = `hook_${ctx.generateUlid()}`;
145709
146715
  const token = options.token ?? ctx.generateNanoid();
145710
146716
  const tokenRetentionUntil = options.experimental_minRetention === void 0 ? void 0 : parseDurationToDate2(options.experimental_minRetention);
@@ -145714,6 +146720,7 @@ function createCreateHook(ctx) {
145714
146720
  correlationId,
145715
146721
  token,
145716
146722
  tokenRetentionUntil,
146723
+ ...options.experimental_force === true && { force: true },
145717
146724
  metadata: options.metadata,
145718
146725
  isWebhook
145719
146726
  });
@@ -145725,6 +146732,7 @@ function createCreateHook(ctx) {
145725
146732
  let hasDisposedEvent = false;
145726
146733
  let hasConflict = false;
145727
146734
  let conflictErrorRef = null;
146735
+ let forceClaimedErrorRef = null;
145728
146736
  let conflictRunRef = null;
145729
146737
  let registrationDelivered = Promise.resolve();
145730
146738
  function deliverRegistration(deliveredAt, settle) {
@@ -145785,10 +146793,11 @@ function createCreateHook(ctx) {
145785
146793
  if (event.eventType === "hook_conflict") {
145786
146794
  ctx.invocationsQueue.delete(correlationId);
145787
146795
  const conflictEvent = event;
145788
- const conflictError = new HookConflictError2(conflictEvent.eventData.token, conflictEvent.eventData.conflictingRunId);
146796
+ const forced = options.experimental_force === true && conflictEvent.eventData.forceRefusedReason === void 0;
146797
+ const conflictError = forced ? new FatalError2(`createHook({ experimental_force: true }) for token "${conflictEvent.eventData.token}" was answered with a hook_conflict: the configured World does not support force-claiming hook tokens${conflictEvent.eventData.conflictingRunId ? ` (run "${conflictEvent.eventData.conflictingRunId}" holds it)` : ""}.`) : new HookConflictError2(conflictEvent.eventData.token, conflictEvent.eventData.conflictingRunId);
145789
146798
  hasConflict = true;
145790
146799
  conflictErrorRef = conflictError;
145791
- conflictRunRef = createConflictingRun(ctx, conflictEvent.eventData.conflictingRunId);
146800
+ conflictRunRef = forced ? null : createConflictingRun(ctx, conflictEvent.eventData.conflictingRunId);
145792
146801
  const pendingPromises = promises.slice();
145793
146802
  promises.length = 0;
145794
146803
  const pendingGetConflictPromises = getConflictPromises.slice();
@@ -145880,6 +146889,28 @@ function createCreateHook(ctx) {
145880
146889
  if (event.eventType === "hook_disposed") {
145881
146890
  ctx.invocationsQueue.delete(correlationId);
145882
146891
  hasDisposedEvent = true;
146892
+ const claimedBy = event.eventData?.forceClaimedBy;
146893
+ if (claimedBy) {
146894
+ const error121 = new HookForceClaimedError2(token, claimedBy.runId, claimedBy.hookId);
146895
+ forceClaimedErrorRef = error121;
146896
+ const pendingPromises = promises.slice();
146897
+ promises.length = 0;
146898
+ const pendingGetConflictPromises = getConflictPromises.slice();
146899
+ getConflictPromises.length = 0;
146900
+ ctx.promiseQueue = ctx.promiseQueue.then(() => {
146901
+ for (const resolver2 of pendingPromises) {
146902
+ resolver2.reject(error121);
146903
+ }
146904
+ for (const resolver2 of pendingGetConflictPromises) {
146905
+ resolver2.resolve(null);
146906
+ }
146907
+ });
146908
+ webhookLogger2.debug("Hook force-claimed by another run", {
146909
+ correlationId,
146910
+ token,
146911
+ claimedByRunId: claimedBy.runId
146912
+ });
146913
+ }
145883
146914
  return EventConsumerResult2.Finished;
145884
146915
  }
145885
146916
  ctx.promiseQueue = ctx.promiseQueue.then(() => {
@@ -145902,6 +146933,13 @@ function createCreateHook(ctx) {
145902
146933
  return nextDelivery.claim();
145903
146934
  }
145904
146935
  }
146936
+ if (forceClaimedErrorRef) {
146937
+ const error121 = forceClaimedErrorRef;
146938
+ ctx.promiseQueue = ctx.promiseQueue.then(() => {
146939
+ resolvers.reject(error121);
146940
+ });
146941
+ return resolvers.promise;
146942
+ }
145905
146943
  if (eventLogEmpty) {
145906
146944
  scheduleWorkflowSuspension(ctx);
145907
146945
  }
@@ -146928,28 +147966,6 @@ function countMissingIds(ids, present) {
146928
147966
  return missing;
146929
147967
  }
146930
147968
  __name(countMissingIds, "countMissingIds");
146931
- function openHookAndWaitState(events) {
146932
- const hooks = /* @__PURE__ */ new Set();
146933
- const waits = /* @__PURE__ */ new Set();
146934
- for (const event of events) {
146935
- switch (event.eventType) {
146936
- case "hook_created":
146937
- hooks.add(event.correlationId);
146938
- break;
146939
- case "hook_disposed":
146940
- hooks.delete(event.correlationId);
146941
- break;
146942
- case "wait_created":
146943
- waits.add(event.correlationId);
146944
- break;
146945
- case "wait_completed":
146946
- waits.delete(event.correlationId);
146947
- break;
146948
- }
146949
- }
146950
- return { openHook: hooks.size > 0, openWait: waits.size > 0 };
146951
- }
146952
- __name(openHookAndWaitState, "openHookAndWaitState");
146953
147969
  function getRetentionDecision({ suspension, serializationBlockerCount, hookContinuation = false, invocationContinuation = false }) {
146954
147970
  if (!isVmRetentionEnabled()) {
146955
147971
  return { retain: false, reason: "disabled" };
@@ -147115,6 +148131,8 @@ function workflowEntrypoint(workflowCode2, options) {
147115
148131
  });
147116
148132
  const invocationStartTime = Date.now();
147117
148133
  const noInlineReplayAfterMs = await getMaxInlineDurationMs(world, invocationStartTime);
148134
+ const openWaitDueDeadlineMs = invocationStartTime + noInlineReplayAfterMs + getOpenWaitClockSkewMs();
148135
+ let eventLogFromInlineDelta = false;
147118
148136
  let loopIteration = 0;
147119
148137
  const replayRecoveryReporter = replayDivergence ? new ReplayRecoveryReporter(replayDivergence.count) : ReplayRecoveryReporter.inert();
147120
148138
  let compiledWorkflowScripts;
@@ -147993,6 +149011,7 @@ function workflowEntrypoint(workflowCode2, options) {
147993
149011
  return;
147994
149012
  }
147995
149013
  if (eventLog.type !== "ready") {
149014
+ eventLogFromInlineDelta = false;
147996
149015
  const page = await loadWorkflowRunEvents(runId, eventLog.type === "loadAfter" ? eventLog.cursor : void 0);
147997
149016
  if (eventLog.type === "loadAfter") {
147998
149017
  appendEventLog(eventLog, page);
@@ -148040,6 +149059,7 @@ function workflowEntrypoint(workflowCode2, options) {
148040
149059
  const completedWaitIdsAfterWrites = new Set(eventLog.events.filter((e) => e.eventType === "wait_completed").map((e) => e.correlationId));
148041
149060
  const missingWaitCompletions = waitsToComplete.filter((waitEvent) => !completedWaitIdsAfterWrites.has(waitEvent.correlationId));
148042
149061
  if (missingWaitCompletions.length > 0) {
149062
+ eventLogFromInlineDelta = false;
148043
149063
  if (eventLog.cursor) {
148044
149064
  const page = await loadWorkflowRunEvents(runId, eventLog.cursor);
148045
149065
  const completedWaitIdsAfterCursor = new Set(page.events.filter((e) => e.eventType === "wait_completed").map((e) => e.correlationId));
@@ -148340,6 +149360,18 @@ function workflowEntrypoint(workflowCode2, options) {
148340
149360
  continue;
148341
149361
  }
148342
149362
  const pendingSteps = suspensionResult.pendingSteps;
149363
+ if (eventLogFromInlineDelta && suspensionResult.waitTimeout !== void 0 && pendingSteps.length === 0 && !suspensionResult.hasAwaitedHookCreation && openHookWait.value.openWait) {
149364
+ runtimeLogger2.debug("Re-reading the event log before parking on a wait over an inline delta", {
149365
+ workflowRunId: runId,
149366
+ loopIteration,
149367
+ waitCorrelationId: suspensionResult.waitTimeout.correlationId
149368
+ });
149369
+ span?.setAttributes({
149370
+ "workflow.wait_park_reread": true
149371
+ });
149372
+ eventLog = nextEventLogLoad(eventLog);
149373
+ continue;
149374
+ }
148343
149375
  const lazyInlineSteps = suspensionResult.lazyInlineSteps;
148344
149376
  const inlineCorrelationIds = new Set(lazyInlineSteps.map((s) => s.correlationId));
148345
149377
  const traceCarrier = await nextTraceCarrier();
@@ -148487,7 +149519,13 @@ function workflowEntrypoint(workflowCode2, options) {
148487
149519
  return;
148488
149520
  }
148489
149521
  const openHookWaitState = openHookWait.value;
148490
- const requestInlineDelta = typeof eventLog.cursor === "string" && err.stepCount === 1 && err.waitCount === 0 && pendingSteps.length === 1 && lazyInlineSteps.length === 1 && ownedRecoverySteps.length === 0 && !suspensionResult.waitTimeout && !openHookWaitState.openWait;
149522
+ const waitDueThisInvocation = hasOpenWaitDueBy(openHookWaitState, openWaitDueDeadlineMs) || suspensionResult.waitTimeout !== void 0 && !(suspensionResult.waitTimeout.resumeAtMs > openWaitDueDeadlineMs);
149523
+ const requestInlineDelta = typeof eventLog.cursor === "string" && err.stepCount === 1 && pendingSteps.length === 1 && lazyInlineSteps.length === 1 && ownedRecoverySteps.length === 0 && !waitDueThisInvocation;
149524
+ if (requestInlineDelta && (openHookWaitState.openWait || suspensionResult.waitTimeout !== void 0)) {
149525
+ span?.setAttributes({
149526
+ "workflow.inline_delta_over_pending_wait": true
149527
+ });
149528
+ }
148491
149529
  const suppressOptimisticStart = openHookWaitState.openHook || err.hookCount > 0 || suspensionResult.hasHookEvents;
148492
149530
  const forceOptimisticStart = turbo && !suspensionResult.hasAttributeEvents && !suspensionResult.waitTimeout && !suspensionResult.hasHookEvents && !suspensionResult.hasAwaitedHookCreation && !openHookWaitState.openHook && !openHookWaitState.openWait;
148493
149531
  const latencyTracking = computeStepLatencyTracking({
@@ -148698,6 +149736,7 @@ function workflowEntrypoint(workflowCode2, options) {
148698
149736
  const only = stepResults[0];
148699
149737
  if (only.type === "completed" && only.inlineDelta) {
148700
149738
  appendEventLog(eventLog, only.inlineDelta);
149739
+ eventLogFromInlineDelta = !only.inlineDelta.hasMore;
148701
149740
  eventLog = only.inlineDelta.hasMore ? nextEventLogLoad(eventLog) : { ...eventLog, type: "ready" };
148702
149741
  continue;
148703
149742
  }
@@ -149447,6 +150486,7 @@ var ERROR_SLUGS = {
149447
150486
  FETCH_IN_WORKFLOW_FUNCTION: "fetch-in-workflow",
149448
150487
  TIMEOUT_FUNCTIONS_IN_WORKFLOW: "timeout-in-workflow",
149449
150488
  HOOK_CONFLICT: "hook-conflict",
150489
+ HOOK_FORCE_CLAIMED: "hook-force-claimed",
149450
150490
  CORRUPTED_EVENT_LOG: "corrupted-event-log",
149451
150491
  REPLAY_DIVERGENCE: "replay-divergence",
149452
150492
  STEP_NOT_REGISTERED: "step-not-registered",
@@ -149560,6 +150600,30 @@ var HookConflictError = class extends WorkflowError {
149560
150600
  return isError(value) && value.name === "HookConflictError";
149561
150601
  }
149562
150602
  };
150603
+ var HookForceClaimedError = class extends WorkflowError {
150604
+ static {
150605
+ __name(this, "HookForceClaimedError");
150606
+ }
150607
+ token;
150608
+ /** The run that took the token. */
150609
+ claimedByRunId;
150610
+ /** The hook (in \`claimedByRunId\`) the token now belongs to. */
150611
+ claimedByHookId;
150612
+ constructor(token, claimedByRunId, claimedByHookId) {
150613
+ super(\`Hook token "\${token}" was force-claimed by another workflow (run "\${claimedByRunId}")\`, {
150614
+ slug: ERROR_SLUGS.HOOK_FORCE_CLAIMED
150615
+ });
150616
+ this.name = "HookForceClaimedError";
150617
+ this.token = token;
150618
+ this.claimedByRunId = claimedByRunId;
150619
+ if (claimedByHookId !== void 0) {
150620
+ this.claimedByHookId = claimedByHookId;
150621
+ }
150622
+ }
150623
+ static is(value) {
150624
+ return isError(value) && value.name === "HookForceClaimedError";
150625
+ }
150626
+ };
149563
150627
  var StreamError = class extends WorkflowWorldError {
149564
150628
  static {
149565
150629
  __name(this, "StreamError");
@@ -149616,6 +150680,7 @@ var RetryableError = class extends Error {
149616
150680
  var FATAL_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//FatalError");
149617
150681
  var RETRYABLE_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//RetryableError");
149618
150682
  var HOOK_CONFLICT_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//HookConflictError");
150683
+ var HOOK_FORCE_CLAIMED_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//HookForceClaimedError");
149619
150684
  var RUNTIME_DECRYPTION_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//RuntimeDecryptionError");
149620
150685
  var STREAM_ERROR_KEY = /* @__PURE__ */ Symbol.for("@workflow/errors//StreamError");
149621
150686
  if (typeof globalThis !== "undefined") {
@@ -149643,6 +150708,14 @@ if (typeof globalThis !== "undefined") {
149643
150708
  configurable: false
149644
150709
  });
149645
150710
  }
150711
+ if (!Object.hasOwn(globalThis, HOOK_FORCE_CLAIMED_ERROR_KEY)) {
150712
+ Object.defineProperty(globalThis, HOOK_FORCE_CLAIMED_ERROR_KEY, {
150713
+ value: HookForceClaimedError,
150714
+ writable: false,
150715
+ enumerable: false,
150716
+ configurable: false
150717
+ });
150718
+ }
149646
150719
  if (!Object.hasOwn(globalThis, RUNTIME_DECRYPTION_ERROR_KEY)) {
149647
150720
  Object.defineProperty(globalThis, RUNTIME_DECRYPTION_ERROR_KEY, {
149648
150721
  value: RuntimeDecryptionError,
@@ -149747,11 +150820,11 @@ var Run = class _Run {
149747
150820
  return this.getWritable();
149748
150821
  }
149749
150822
  };
149750
- Run.prototype["wakeUp"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#wakeUp");
149751
- Run.prototype["cancel"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#cancel");
149752
- Run.prototype["getReadable"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#getReadable");
149753
- Run.prototype["getWritable"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#getWritable");
149754
- var __step_Run$exists = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#exists");
150823
+ Run.prototype["wakeUp"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#wakeUp");
150824
+ Run.prototype["cancel"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#cancel");
150825
+ Run.prototype["getReadable"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#getReadable");
150826
+ Run.prototype["getWritable"] = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#getWritable");
150827
+ var __step_Run$exists = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#exists");
149755
150828
  Object.defineProperty(Run.prototype, "exists", {
149756
150829
  get() {
149757
150830
  return __step_Run$exists.call(this);
@@ -149759,7 +150832,7 @@ Object.defineProperty(Run.prototype, "exists", {
149759
150832
  configurable: true,
149760
150833
  enumerable: false
149761
150834
  });
149762
- var __step_Run$status = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#status");
150835
+ var __step_Run$status = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#status");
149763
150836
  Object.defineProperty(Run.prototype, "status", {
149764
150837
  get() {
149765
150838
  return __step_Run$status.call(this);
@@ -149767,7 +150840,7 @@ Object.defineProperty(Run.prototype, "status", {
149767
150840
  configurable: true,
149768
150841
  enumerable: false
149769
150842
  });
149770
- var __step_Run$returnValue = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#returnValue");
150843
+ var __step_Run$returnValue = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#returnValue");
149771
150844
  Object.defineProperty(Run.prototype, "returnValue", {
149772
150845
  get() {
149773
150846
  return __step_Run$returnValue.call(this);
@@ -149775,7 +150848,7 @@ Object.defineProperty(Run.prototype, "returnValue", {
149775
150848
  configurable: true,
149776
150849
  enumerable: false
149777
150850
  });
149778
- var __step_Run$workflowName = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#workflowName");
150851
+ var __step_Run$workflowName = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#workflowName");
149779
150852
  Object.defineProperty(Run.prototype, "workflowName", {
149780
150853
  get() {
149781
150854
  return __step_Run$workflowName.call(this);
@@ -149783,7 +150856,7 @@ Object.defineProperty(Run.prototype, "workflowName", {
149783
150856
  configurable: true,
149784
150857
  enumerable: false
149785
150858
  });
149786
- var __step_Run$createdAt = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#createdAt");
150859
+ var __step_Run$createdAt = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#createdAt");
149787
150860
  Object.defineProperty(Run.prototype, "createdAt", {
149788
150861
  get() {
149789
150862
  return __step_Run$createdAt.call(this);
@@ -149791,7 +150864,7 @@ Object.defineProperty(Run.prototype, "createdAt", {
149791
150864
  configurable: true,
149792
150865
  enumerable: false
149793
150866
  });
149794
- var __step_Run$startedAt = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#startedAt");
150867
+ var __step_Run$startedAt = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#startedAt");
149795
150868
  Object.defineProperty(Run.prototype, "startedAt", {
149796
150869
  get() {
149797
150870
  return __step_Run$startedAt.call(this);
@@ -149799,7 +150872,7 @@ Object.defineProperty(Run.prototype, "startedAt", {
149799
150872
  configurable: true,
149800
150873
  enumerable: false
149801
150874
  });
149802
- var __step_Run$completedAt = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.55//Run#completedAt");
150875
+ var __step_Run$completedAt = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/run@5.0.0-beta.56//Run#completedAt");
149803
150876
  Object.defineProperty(Run.prototype, "completedAt", {
149804
150877
  get() {
149805
150878
  return __step_Run$completedAt.call(this);
@@ -149816,7 +150889,7 @@ Object.defineProperty(Run.prototype, "completedAt", {
149816
150889
  enumerable: false,
149817
150890
  configurable: false
149818
150891
  });
149819
- })(Run, "class//@workflow/core/runtime/run@5.0.0-beta.55//Run");
150892
+ })(Run, "class//@workflow/core/runtime/run@5.0.0-beta.56//Run");
149820
150893
 
149821
150894
  // ../core/dist/workflow/create-hook.js
149822
150895
  aliasSerializationClass(RUN_CLASS_ID, Run);
@@ -157412,7 +158485,7 @@ globalThis.__private_workflows.set("workflow//./workflows/inline-execution//hook
157412
158485
  var latestNoOpWarning = globalSingleton("@workflow/core//latestNoOpWarning", 1, () => ({
157413
158486
  warned: false
157414
158487
  }));
157415
- var start = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/start@5.0.0-beta.55//start");
158488
+ var start = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//@workflow/core/runtime/start@5.0.0-beta.56//start");
157416
158489
 
157417
158490
  // workflows/lineage.ts
157418
158491
  var noop = globalThis[/* @__PURE__ */ Symbol.for("WORKFLOW_USE_STEP")]("step//./workflows/lineage//noop");