@cmmd-center/forge 0.13.87 → 0.13.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/dist/bin.cjs +165 -21
  2. package/dist/bin.mjs +165 -21
  3. package/dist/client/assets/{CanvasPanel-Dyho_JDo.js → CanvasPanel-D9NzolmF.js} +3 -3
  4. package/dist/client/assets/{CompositeItem-DeDhuBZB.js → CompositeItem-DU4kdAw6.js} +2 -2
  5. package/dist/client/assets/{DiffPanel-LdZieNJf.js → DiffPanel-Dx1AY0mg.js} +3 -3
  6. package/dist/client/assets/{DiffPanel.logic-CU1JloJt.js → DiffPanel.logic-BiyBXIja.js} +2 -2
  7. package/dist/client/assets/{DiffWorkerPoolProvider-BMlqcnLd.js → DiffWorkerPoolProvider-jDqu6eJj.js} +12 -12
  8. package/dist/client/assets/{PullRequestCodePanel-DUSNwNQ9.js → PullRequestCodePanel-CqrN4lB_.js} +3 -3
  9. package/dist/client/assets/{Virtualizer-DZvTW6Dy.js → Virtualizer-D-5QCW79.js} +2 -2
  10. package/dist/client/assets/WorkerPoolContext-BxfSg7K_.js +2540 -0
  11. package/dist/client/assets/{canvasStateStore-DJ_OzrrC.js → canvasStateStore-CBQkAlnK.js} +2 -2
  12. package/dist/client/assets/{cmmdAuthRedirect-Bqx90tp9.js → cmmdAuthRedirect-CCwq2L-I.js} +2 -2
  13. package/dist/client/assets/index-BrOo-A0J.js +153 -0
  14. package/dist/client/assets/index-BvseDV5h.css +1 -0
  15. package/dist/client/assets/{popover-D20eY2oM.js → popover-DJpPSHsy.js} +2 -2
  16. package/dist/client/assets/{src-CoTxmdk3.js → src-CF8xm6xH.js} +2 -2
  17. package/dist/client/assets/{toggle-AqC0QUc1.js → toggle-Bv2GfLGu.js} +2 -2
  18. package/dist/client/index.html +11 -11
  19. package/dist/forge-build.json +1 -1
  20. package/package.json +1 -1
  21. package/dist/client/assets/WorkerPoolContext-Cqf93ctM.js +0 -2537
  22. package/dist/client/assets/index-C-3BZaMd.css +0 -1
  23. package/dist/client/assets/index-CUQ9jU7z.js +0 -153
package/dist/bin.mjs CHANGED
@@ -22352,6 +22352,16 @@ const TerminalFontSize = Int$1.check(isGreaterThanOrEqualTo(8)).check(isLessThan
22352
22352
  const FontFamilyPreference = String$1.check(isMaxLength(200));
22353
22353
  const AutoSettleAfterDays = NullOr(Int$1.check(isGreaterThanOrEqualTo(1)).check(isLessThanOrEqualTo(90)));
22354
22354
  const GlassOpacity = Int$1.check(isGreaterThanOrEqualTo(40)).check(isLessThanOrEqualTo(100));
22355
+ /**
22356
+ * Interface contrast, in percent of the shipped look. 100 disables mixing;
22357
+ * above it the `--contrast-*` axis in `apps/web/src/index.css` raises text
22358
+ * and border contrast from the semantic roles, below it softens them.
22359
+ * Upstream's axis and bounds, adopted by the token union.
22360
+ */
22361
+ const AppearanceContrast = Int$1.check(isBetween({
22362
+ minimum: 50,
22363
+ maximum: 200
22364
+ }));
22355
22365
  const DEFAULT_CLIENT_SETTINGS = decodeSync(Struct({
22356
22366
  theme: ThemePreference.pipe(withDecodingDefault(succeed("system"))),
22357
22367
  themeId: ThemeId,
@@ -22367,6 +22377,7 @@ const DEFAULT_CLIENT_SETTINGS = decodeSync(Struct({
22367
22377
  fontSmoothing: Boolean$1.pipe(withDecodingDefault(succeed(true))),
22368
22378
  sidebarAutoSettleAfterDays: AutoSettleAfterDays.pipe(withDecodingDefault(succeed(3))),
22369
22379
  glassOpacity: GlassOpacity.pipe(withDecodingDefault(succeed(80))),
22380
+ appearanceContrast: AppearanceContrast.pipe(withDecodingDefault(succeed(100))),
22370
22381
  confirmThreadArchive: Boolean$1.pipe(withDecodingDefault(succeed(true))),
22371
22382
  confirmThreadDelete: Boolean$1.pipe(withDecodingDefault(succeed(true))),
22372
22383
  showInteractionModeToggle: Boolean$1.pipe(withDecodingDefault(succeed(false))),
@@ -65994,7 +66005,7 @@ function normalizeNumberish(value) {
65994
66005
  }
65995
66006
  //#endregion
65996
66007
  //#region package.json
65997
- var version$1 = "0.13.87";
66008
+ var version$1 = "0.13.89";
65998
66009
  //#endregion
65999
66010
  //#region src/sentry.ts
66000
66011
  const SERVER_APP_NAME = "forge-server";
@@ -241337,6 +241348,22 @@ function resolveSpriteTaskHoldConfig(env = process.env) {
241337
241348
  function spriteTaskHoldName(namePrefix, turnId) {
241338
241349
  return `${namePrefix}${turnId}`;
241339
241350
  }
241351
+ /**
241352
+ * Thrown by the production transport when the guest API answers a non-2xx
241353
+ * status. Carries the status so a caller can tell a 409 (already held, and
241354
+ * generally benign) apart from a real transport failure.
241355
+ */
241356
+ var SpriteTaskHoldTransportError = class extends Error {
241357
+ status;
241358
+ constructor(status) {
241359
+ super(`sprite task API responded ${status}`);
241360
+ this.name = "SpriteTaskHoldTransportError";
241361
+ this.status = status;
241362
+ }
241363
+ };
241364
+ function transportStatus(cause) {
241365
+ return cause instanceof SpriteTaskHoldTransportError ? cause.status : void 0;
241366
+ }
241340
241367
  const noTimer = () => {};
241341
241368
  /**
241342
241369
  * Resolve with `promise`, but stop waiting after `timeoutMs`. Rejections pass
@@ -241370,12 +241397,25 @@ var SpriteTaskHoldManager = class {
241370
241397
  #transport;
241371
241398
  #timer;
241372
241399
  #onError;
241400
+ #onEvent;
241373
241401
  #holds = /* @__PURE__ */ new Map();
241374
241402
  constructor(options) {
241375
241403
  this.#config = options.config;
241376
241404
  this.#transport = options.transport;
241377
241405
  this.#timer = options.timer ?? DEFAULT_TIMER;
241378
241406
  this.#onError = options.onError ?? (() => {});
241407
+ this.#onEvent = options.onEvent ?? (() => {});
241408
+ }
241409
+ /** Compose and report one acquire/refresh/release outcome, if a caller wants to log it. */
241410
+ #emitEvent(action, holdKey, hold, outcome, status) {
241411
+ this.#onEvent({
241412
+ action,
241413
+ holdKey,
241414
+ holdName: hold.name,
241415
+ threadId: hold.threadId,
241416
+ outcome,
241417
+ ...status !== void 0 ? { status } : {}
241418
+ });
241379
241419
  }
241380
241420
  /** Turn ids that currently hold the sprite awake. Test/diagnostic surface. */
241381
241421
  activeTurnIds() {
@@ -241384,10 +241424,19 @@ var SpriteTaskHoldManager = class {
241384
241424
  /**
241385
241425
  * `maxRefreshes` bounds a hold that has no terminal event of its own, such as a launch
241386
241426
  * whose turn may never start. When the budget is spent the hold releases itself.
241427
+ *
241428
+ * Resolves to whether this hold has ever landed a PUT, never throwing (a
241429
+ * hold problem must not fail a turn). A caller handing off from a
241430
+ * predecessor hold (forge#3589) must not release it on anything less than
241431
+ * "succeeded": a failed initial PUT still installs the refresher below and
241432
+ * keeps retrying every `SPRITE_TASK_HOLD_REFRESH_MS`, so the predecessor is
241433
+ * the only thing covering the sprite until one of those retries lands. A
241434
+ * duplicate call against a live, unreleased hold reports that hold's own
241435
+ * confirmed state rather than a blanket "succeeded", for the same reason.
241387
241436
  */
241388
241437
  async acquire(turnId, threadId = null, options = {}) {
241389
- const existing = this.#holds.get(turnId);
241390
- if (existing !== void 0 && !existing.released) return;
241438
+ const existing = this.#liveHold(turnId);
241439
+ if (existing !== void 0) return existing.confirmed ? "succeeded" : "failed";
241391
241440
  let refreshesLeft = options.maxRefreshes ?? Number.POSITIVE_INFINITY;
241392
241441
  const name = spriteTaskHoldName(this.#config.namePrefix, turnId);
241393
241442
  const hold = {
@@ -241399,24 +241448,54 @@ var SpriteTaskHoldManager = class {
241399
241448
  phase: "idle",
241400
241449
  releaseAttempt: null,
241401
241450
  deletion: null,
241402
- uncertainPut: false
241451
+ uncertainPut: false,
241452
+ confirmed: false
241403
241453
  };
241404
241454
  this.#holds.set(turnId, hold);
241405
241455
  const put = this.#transport.put(name, 300);
241406
241456
  const tracked = put.then(() => "succeeded", () => "failed");
241407
241457
  hold.inFlight.add(tracked);
241458
+ let outcome = "succeeded";
241408
241459
  try {
241409
241460
  await put;
241410
241461
  hold.uncertainPut = false;
241462
+ hold.confirmed = true;
241463
+ this.#emitEvent("acquire", turnId, hold, "success");
241411
241464
  } catch (cause) {
241465
+ outcome = "failed";
241412
241466
  hold.uncertainPut = true;
241413
241467
  hold.inFlight.delete(tracked);
241414
241468
  this.#onError(`failed to acquire sprite task hold for turn ${turnId}`, cause);
241415
- if (hold.released || this.#holds.get(turnId) !== hold) return;
241469
+ this.#emitEvent("acquire", turnId, hold, "failure", transportStatus(cause));
241470
+ if (this.#isStale(turnId, hold)) return outcome;
241416
241471
  }
241417
241472
  hold.inFlight.delete(tracked);
241418
- if (hold.released || this.#holds.get(turnId) !== hold) return;
241419
- hold.refresher = this.#timer.setInterval(() => {
241473
+ if (this.#isStale(turnId, hold)) return outcome;
241474
+ hold.refresher = this.#installRefresher(turnId, hold, name, refreshesLeft);
241475
+ return outcome;
241476
+ }
241477
+ /** The hold registered for `turnId`, if it is still live: not mid-release or concluded. */
241478
+ #liveHold(turnId) {
241479
+ const existing = this.#holds.get(turnId);
241480
+ return existing !== void 0 && !existing.released ? existing : void 0;
241481
+ }
241482
+ /**
241483
+ * Whether `hold` has been superseded since the caller last checked: either
241484
+ * released, or no longer the map's current entry for `turnId` (a fresh
241485
+ * acquire replaced it). Both `acquire`'s post-await fences use this so
241486
+ * neither has to repeat the compound condition inline.
241487
+ */
241488
+ #isStale(turnId, hold) {
241489
+ return hold.released || this.#holds.get(turnId) !== hold;
241490
+ }
241491
+ /**
241492
+ * Install a hold's refresh interval. Extracted out of `acquire` so that
241493
+ * method's own branching stays flat; the ordering and identity-fence rules
241494
+ * around it are unchanged, and this closure owns only the refresh tick.
241495
+ */
241496
+ #installRefresher(turnId, hold, name, initialBudget) {
241497
+ let refreshesLeft = initialBudget;
241498
+ return this.#timer.setInterval(() => {
241420
241499
  if (hold.inFlight.size > 0) return;
241421
241500
  if (refreshesLeft <= 0) {
241422
241501
  if (this.#holds.get(turnId) === hold) this.release(turnId);
@@ -241425,10 +241504,13 @@ var SpriteTaskHoldManager = class {
241425
241504
  refreshesLeft -= 1;
241426
241505
  const refresh = this.#transport.put(name, 300).then(() => {
241427
241506
  hold.uncertainPut = false;
241507
+ hold.confirmed = true;
241508
+ this.#emitEvent("refresh", turnId, hold, "success");
241428
241509
  return "succeeded";
241429
241510
  }, (cause) => {
241430
241511
  hold.uncertainPut = true;
241431
241512
  this.#onError(`failed to refresh sprite task hold for turn ${turnId}`, cause);
241513
+ this.#emitEvent("refresh", turnId, hold, "failure", transportStatus(cause));
241432
241514
  return "failed";
241433
241515
  });
241434
241516
  hold.inFlight.add(refresh);
@@ -241472,6 +241554,7 @@ var SpriteTaskHoldManager = class {
241472
241554
  hold.phase = "joining";
241473
241555
  const attempt = (async () => {
241474
241556
  const outcome = await this.#concludeRelease(hold, turnId, options);
241557
+ this.#emitEvent("release", turnId, hold, outcome);
241475
241558
  if (outcome === "unreleased") {
241476
241559
  hold.phase = "idle";
241477
241560
  this.#removeIfCurrent(turnId, hold);
@@ -241655,7 +241738,7 @@ function makeSpriteTaskHoldTransport(config, fetchImpl = fetch) {
241655
241738
  signal: controller.signal,
241656
241739
  unix: config.socketPath
241657
241740
  });
241658
- if (!response.ok) throw new Error(`sprite task API responded ${response.status}`);
241741
+ if (!response.ok) throw new SpriteTaskHoldTransportError(response.status);
241659
241742
  } finally {
241660
241743
  clearTimeout(deadline);
241661
241744
  }
@@ -241691,13 +241774,14 @@ function makeSpriteTaskHoldTransport(config, fetchImpl = fetch) {
241691
241774
  * Build a manager for this process, or null when the runtime is not a sprite.
241692
241775
  * Callers treat null as "this feature does not apply here".
241693
241776
  */
241694
- function makeSpriteTaskHoldManager(env = process.env, onError) {
241777
+ function makeSpriteTaskHoldManager(env = process.env, onError, onEvent) {
241695
241778
  const config = resolveSpriteTaskHoldConfig(env);
241696
241779
  if (config === null) return null;
241697
241780
  return new SpriteTaskHoldManager({
241698
241781
  config,
241699
241782
  transport: makeSpriteTaskHoldTransport(config),
241700
- ...onError ? { onError } : {}
241783
+ ...onError ? { onError } : {},
241784
+ ...onEvent ? { onEvent } : {}
241701
241785
  });
241702
241786
  }
241703
241787
  //#endregion
@@ -286277,7 +286361,7 @@ const AgentBrowserManagerLive = effect(BrowserManager, gen(function* () {
286277
286361
  const displayPeers = createWebRtcDisplaySessionManager({
286278
286362
  mediaSourceFactory: displayCaptureRuntime.mediaSourceFactory,
286279
286363
  taskHold: displayTaskHoldManager ? {
286280
- acquire: (holdId) => displayTaskHoldManager.acquire(holdId),
286364
+ acquire: (holdId) => displayTaskHoldManager.acquire(holdId).then(() => {}),
286281
286365
  release: (holdId) => displayTaskHoldManager.release(holdId).then(() => {})
286282
286366
  } : void 0
286283
286367
  });
@@ -294615,6 +294699,15 @@ const SpriteTaskHoldReactorLive = effect(SpriteTaskHoldReactor, gen(function* ()
294615
294699
  const providerService = yield* ProviderService;
294616
294700
  const manager = makeSpriteTaskHoldManager(process.env, (message, cause) => {
294617
294701
  runFork(logWarning$1(message, { cause: cause instanceof Error ? cause.message : cause }));
294702
+ }, (event) => {
294703
+ runFork(logInfo("sprite task hold", {
294704
+ action: event.action,
294705
+ holdKey: event.holdKey,
294706
+ holdName: event.holdName,
294707
+ threadId: event.threadId,
294708
+ outcome: event.outcome,
294709
+ ...event.status !== void 0 ? { status: event.status } : {}
294710
+ }));
294618
294711
  });
294619
294712
  /**
294620
294713
  * Run a manager call, or nothing on runtimes that are not sprites. One
@@ -294630,27 +294723,58 @@ const SpriteTaskHoldReactorLive = effect(SpriteTaskHoldReactor, gen(function* ()
294630
294723
  deleteTimeoutMs: SPRITE_TASK_HOLD_RELEASE_DELETE_TIMEOUT_MS
294631
294724
  })));
294632
294725
  const launchCallers = /* @__PURE__ */ new Map();
294726
+ const launchGeneration = /* @__PURE__ */ new Map();
294727
+ const turnLaunchGeneration = /* @__PURE__ */ new Map();
294728
+ /**
294729
+ * Belt-and-braces cleanup for a turn reaching a terminal state
294730
+ * (turn.completed or turn.aborted): a launch hold whose hand-off never
294731
+ * confirmed (the turn hold's PUT kept failing) is still up, refreshing on
294732
+ * its own budget, and the turn ending either way means the launch phase is
294733
+ * over. `manager.release(String(turnId))` above can itself wait on the
294734
+ * guest API, so by the time this runs the NEXT stage's own dispatch may
294735
+ * already have acquired a fresh launch hold for the same thread; only
294736
+ * release it here when no such newer launch has started since this turn's
294737
+ * own turn.started was processed (CodeRabbit review, forge#3592). A no-op
294738
+ * when this turn's own generation is stale, or when the hold was already
294739
+ * released.
294740
+ */
294741
+ const releaseLingeringLaunchHold = async (turnId, threadId) => {
294742
+ const capturedGeneration = turnLaunchGeneration.get(turnId);
294743
+ turnLaunchGeneration.delete(turnId);
294744
+ if (manager === null || capturedGeneration === void 0) return;
294745
+ if (launchGeneration.get(threadId) !== capturedGeneration) return;
294746
+ await manager.release(launchHoldId(threadId));
294747
+ };
294633
294748
  const start = fn$1("start")(function* () {
294634
294749
  if (manager === null) return;
294635
294750
  yield* forkScoped(runForEach(providerService.streamEvents, (event) => {
294636
294751
  if (event.type === "turn.started") {
294637
294752
  const turnId = event.turnId;
294638
294753
  if (turnId === void 0) return void_$1;
294754
+ const threadId = String(event.threadId);
294755
+ const generation = launchGeneration.get(threadId) ?? 0;
294639
294756
  return promise(async () => {
294640
- await manager.acquire(String(turnId), String(event.threadId));
294641
- launchCallers.delete(String(event.threadId));
294642
- await manager.release(launchHoldId(String(event.threadId)));
294757
+ const outcome = await manager.acquire(String(turnId), threadId);
294758
+ launchCallers.delete(threadId);
294759
+ turnLaunchGeneration.set(String(turnId), generation);
294760
+ if (outcome === "succeeded") await manager.release(launchHoldId(threadId));
294643
294761
  });
294644
294762
  }
294645
294763
  if (event.type === "turn.completed") {
294646
294764
  const turnId = event.turnId;
294647
294765
  if (turnId === void 0) return void_$1;
294648
- return promise(() => manager.release(String(turnId)).then(() => {}));
294766
+ return promise(async () => {
294767
+ await manager.release(String(turnId));
294768
+ await releaseLingeringLaunchHold(String(turnId), String(event.threadId));
294769
+ });
294649
294770
  }
294650
294771
  if (event.type === "turn.aborted") {
294651
294772
  const turnId = event.turnId;
294652
294773
  if (turnId === void 0) return void_$1;
294653
- return promise(() => manager.release(String(turnId)).then(() => {}));
294774
+ return promise(async () => {
294775
+ await manager.release(String(turnId));
294776
+ await releaseLingeringLaunchHold(String(turnId), String(event.threadId));
294777
+ });
294654
294778
  }
294655
294779
  if (event.type === "session.exited") {
294656
294780
  launchCallers.delete(String(event.threadId));
@@ -294662,7 +294786,9 @@ const SpriteTaskHoldReactorLive = effect(SpriteTaskHoldReactor, gen(function* ()
294662
294786
  /** Takes the launch hold for one more caller. The budget bounds a launch that never starts a turn. */
294663
294787
  const holdLaunch = (threadId) => promise(async () => {
294664
294788
  if (manager === null) return;
294665
- launchCallers.set(threadId, (launchCallers.get(threadId) ?? 0) + 1);
294789
+ const callers = launchCallers.get(threadId) ?? 0;
294790
+ launchCallers.set(threadId, callers + 1);
294791
+ if (callers === 0) launchGeneration.set(threadId, (launchGeneration.get(threadId) ?? 0) + 1);
294666
294792
  await manager.acquire(launchHoldId(threadId), threadId, { maxRefreshes: LAUNCH_HOLD_MAX_REFRESHES });
294667
294793
  });
294668
294794
  /** Lets one caller go. The hold itself is released when the last caller has let go. */
@@ -298802,7 +298928,7 @@ function resolveBuildCommitFromEnv(env) {
298802
298928
  * environment descriptor down instead of reporting an honest "unknown".
298803
298929
  */
298804
298930
  function readBakedBuildCommit() {
298805
- return "14fc66b40c2ee6a6c8a1574d5c8d9f58e2da093c";
298931
+ return "394b2a7cd449282f2845ec5fa3bd9093f20b8bb8";
298806
298932
  }
298807
298933
  async function resolveServerBuildCommit(input) {
298808
298934
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -316156,6 +316282,7 @@ const PipelineRunCoordinatorLive = effect(PipelineRunCoordinator, gen(function*
316156
316282
  const policy = yield* PipelineStagePolicy;
316157
316283
  const secretStore = yield* ServerSecretStore;
316158
316284
  const orchestration = yield* OrchestrationEngineService;
316285
+ const spriteTaskHoldReactor = yield* serviceOption(SpriteTaskHoldReactor);
316159
316286
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
316160
316287
  const projectionTurns = yield* ProjectionTurnRepository;
316161
316288
  const changeInspector = yield* PipelineChangeInspector;
@@ -316883,6 +317010,23 @@ const PipelineRunCoordinatorLive = effect(PipelineRunCoordinator, gen(function*
316883
317010
  });
316884
317011
  yield* drainReports();
316885
317012
  });
317013
+ /**
317014
+ * Holds the sprite awake around a freshly dispatched thread.turn.start, the
317015
+ * same way bootstrapLaunch.ts's withLaunchHold does for a turn started from
317016
+ * the Forge web composer or a bootstrap launch (forge#3589). A turn kicked
317017
+ * from a CMMD card reaches the engine through dispatchStage /
317018
+ * recoverMissingProviderStart below, never through that HTTP dispatch
317019
+ * route, so nothing else covers the window between this dispatch and the
317020
+ * provider's own turn.started.
317021
+ *
317022
+ * Releases only on error: a successful dispatch hands the hold off to the
317023
+ * turn's own, in SpriteTaskHoldReactor's turn.started handler.
317024
+ */
317025
+ const withPipelineLaunchHold = (threadId, dispatch) => {
317026
+ if (isNone(spriteTaskHoldReactor)) return dispatch;
317027
+ const reactor = spriteTaskHoldReactor.value;
317028
+ return uninterruptible(reactor.holdLaunch(threadId)).pipe(andThen(yieldNow), andThen(dispatch), onError$1(() => reactor.releaseLaunch(threadId)));
317029
+ };
316886
317030
  const dispatchStage = (binding, snapshot, authority) => gen(function* () {
316887
317031
  const thread = (yield* orchestration.getReadModel()).threads.find((candidate) => candidate.id === ThreadId.make(binding.threadId) && candidate.deletedAt === null);
316888
317032
  if (thread === void 0) {
@@ -316958,7 +317102,7 @@ const PipelineRunCoordinatorLive = effect(PipelineRunCoordinator, gen(function*
316958
317102
  messageId: `${commandId}:message`,
316959
317103
  createdAt: startedAt
316960
317104
  });
316961
- yield* orchestration.dispatch(command).pipe(tapError(() => store.resetDispatch(binding.runId, commandId)));
317105
+ yield* withPipelineLaunchHold(binding.threadId, orchestration.dispatch(command)).pipe(tapError(() => store.resetDispatch(binding.runId, commandId)));
316962
317106
  yield* store.markDispatched(binding.runId, commandId);
316963
317107
  });
316964
317108
  const resumeRun = (runId) => gen(function* () {
@@ -317598,7 +317742,7 @@ const PipelineRunCoordinatorLive = effect(PipelineRunCoordinator, gen(function*
317598
317742
  });
317599
317743
  if (workerCoordination.kind !== "single" && workerCoordination.kind !== "ready") return;
317600
317744
  const commandId = `${originalCommandId}:provider-start-recovery`;
317601
- yield* orchestration.dispatch(buildStageTurnCommand({
317745
+ yield* withPipelineLaunchHold(input.binding.threadId, orchestration.dispatch(buildStageTurnCommand({
317602
317746
  binding: input.binding,
317603
317747
  attempt: input.binding.attempt,
317604
317748
  snapshot: input.snapshot,
@@ -317609,7 +317753,7 @@ const PipelineRunCoordinatorLive = effect(PipelineRunCoordinator, gen(function*
317609
317753
  commandId,
317610
317754
  messageId: `${commandId}:message`,
317611
317755
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
317612
- }));
317756
+ })));
317613
317757
  });
317614
317758
  }
317615
317759
  function reconcileActiveThread(input) {