@muggleai/works 5.27.0 → 5.28.0-staging.250

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 (31) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/{chunk-6BAB7IVG.js → chunk-HBCASFGN.js} +300 -99
  3. package/dist/{chunk-B5BWBZZ5.js → chunk-KGNRBL52.js} +46 -58
  4. package/dist/cli.js +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  7. package/dist/plugin/.codex-mcp.json +1 -1
  8. package/dist/plugin/.codex-plugin/plugin.json +1 -1
  9. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  10. package/dist/plugin/.mcp.json +1 -1
  11. package/dist/plugin/skills/_shared/dev-loop/run.md +1 -1
  12. package/dist/plugin/skills/do/pre-flight.md +1 -1
  13. package/dist/plugin/skills/muggle-test/execute-local.md +8 -6
  14. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +4 -2
  15. package/dist/plugin/skills/muggle-test-prepare/SKILL.md +7 -4
  16. package/dist/plugin/skills/muggle-test-prepare/steps/claim-stack.md +10 -4
  17. package/dist/release-manifest.json +3 -3
  18. package/dist/src-ZOCXV6PT.js +1 -0
  19. package/package.json +2 -2
  20. package/plugin/.claude-plugin/plugin.json +1 -1
  21. package/plugin/.codex-mcp.json +1 -1
  22. package/plugin/.codex-plugin/plugin.json +1 -1
  23. package/plugin/.cursor-plugin/plugin.json +1 -1
  24. package/plugin/.mcp.json +1 -1
  25. package/plugin/skills/_shared/dev-loop/run.md +1 -1
  26. package/plugin/skills/do/pre-flight.md +1 -1
  27. package/plugin/skills/muggle-test/execute-local.md +8 -6
  28. package/plugin/skills/muggle-test-feature-local/SKILL.md +4 -2
  29. package/plugin/skills/muggle-test-prepare/SKILL.md +7 -4
  30. package/plugin/skills/muggle-test-prepare/steps/claim-stack.md +10 -4
  31. package/dist/src-MCJFH7AB.js +0 -1
@@ -85,6 +85,7 @@ __export(src_exports, {
85
85
  SCREENSHOT_FILE_EXTENSION: () => SCREENSHOT_FILE_EXTENSION,
86
86
  StackCheckVerdict: () => StackCheckVerdict,
87
87
  StackClaimVerdict: () => StackClaimVerdict,
88
+ StackLeaseEndReason: () => StackLeaseEndReason,
88
89
  StackMismatchKind: () => StackMismatchKind,
89
90
  StackPortAction: () => StackPortAction,
90
91
  WATCHER_LIFETIME_SECONDS: () => WATCHER_LIFETIME_SECONDS,
@@ -102,6 +103,7 @@ __export(src_exports, {
102
103
  calculateFileChecksum: () => calculateFileChecksum,
103
104
  checkServiceStack: () => checkServiceStack,
104
105
  claimServiceStack: () => claimServiceStack,
106
+ compareQueuePlaces: () => compareQueuePlaces,
105
107
  completeOnboarding: () => completeOnboarding,
106
108
  createApiKeyWithToken: () => createApiKeyWithToken,
107
109
  createChildLogger: () => createChildLogger,
@@ -157,6 +159,7 @@ __export(src_exports, {
157
159
  loadApiKeyData: () => loadApiKeyData,
158
160
  loadCredentials: () => loadCredentials,
159
161
  localQa: () => local_exports2,
162
+ lookUpCheckoutPullRequest: () => lookUpCheckoutPullRequest,
160
163
  markOnboardingCompleted: () => markOnboardingCompleted,
161
164
  maskLlmApiKey: () => maskLlmApiKey,
162
165
  mcp: () => mcp_exports,
@@ -3326,6 +3329,57 @@ function describeStoppedRun(params) {
3326
3329
  return `Run stopped: it exceeded its ${params.timeoutMs}ms time limit.`;
3327
3330
  }
3328
3331
 
3332
+ // packages/mcps/src/mcp/local/services/execution-timeout-constants.ts
3333
+ var DEFAULT_GENERATION_TIMEOUT_MS = 3e5;
3334
+ var DEFAULT_REPLAY_TIMEOUT_MS = 18e4;
3335
+
3336
+ // packages/mcps/src/shared/queue-priority/queue-priority-constants.ts
3337
+ var PR_LOOKUP_TIMEOUT_MS = 1e4;
3338
+ var PR_LOOKUP_CACHE_MS = 2 * 6e4;
3339
+ var PR_LOOKUP_ENV = "MUGGLE_PR_LOOKUP";
3340
+
3341
+ // packages/mcps/src/shared/queue-priority/pull-request-lookup.ts
3342
+ var lookupsByCwd = /* @__PURE__ */ new Map();
3343
+ var lookUpCheckoutPullRequest = async (cwd) => {
3344
+ if (process.env[PR_LOOKUP_ENV] === "off") return null;
3345
+ const cached = lookupsByCwd.get(cwd);
3346
+ if (cached !== void 0 && Date.now() - cached.lookedUpAt < PR_LOOKUP_CACHE_MS) return cached.pullRequest;
3347
+ const pullRequest = parseOpenPullRequest(await runGhPrView(cwd));
3348
+ lookupsByCwd.set(cwd, { pullRequest, lookedUpAt: Date.now() });
3349
+ return pullRequest;
3350
+ };
3351
+ function runGhPrView(cwd) {
3352
+ return new Promise((resolve7) => {
3353
+ execFile(
3354
+ "gh",
3355
+ ["pr", "view", "--json", "number,url,createdAt,state"],
3356
+ { cwd, timeout: PR_LOOKUP_TIMEOUT_MS, windowsHide: true, encoding: "utf-8" },
3357
+ (error, stdout) => {
3358
+ resolve7(error ? null : stdout);
3359
+ }
3360
+ );
3361
+ });
3362
+ }
3363
+ function parseOpenPullRequest(output) {
3364
+ if (output === null) return null;
3365
+ try {
3366
+ const parsed = JSON.parse(output);
3367
+ const createdAt = typeof parsed.createdAt === "string" ? Date.parse(parsed.createdAt) : Number.NaN;
3368
+ if (parsed.state !== "OPEN" || typeof parsed.number !== "number" || Number.isNaN(createdAt)) return null;
3369
+ return { number: parsed.number, url: typeof parsed.url === "string" ? parsed.url : "", createdAt };
3370
+ } catch {
3371
+ return null;
3372
+ }
3373
+ }
3374
+
3375
+ // packages/mcps/src/shared/queue-priority/queue-priority.ts
3376
+ function compareQueuePlaces(left, right) {
3377
+ const leftOpenedAt = left.pullRequest?.createdAt ?? Number.POSITIVE_INFINITY;
3378
+ const rightOpenedAt = right.pullRequest?.createdAt ?? Number.POSITIVE_INFINITY;
3379
+ if (leftOpenedAt !== rightOpenedAt) return leftOpenedAt < rightOpenedAt ? -1 : 1;
3380
+ return left.enqueuedAt - right.enqueuedAt;
3381
+ }
3382
+
3329
3383
  // packages/mcps/src/mcp/local/services/local-execution-lock-constants.ts
3330
3384
  var LOCK_FILE_NAME = "local-execution.lock";
3331
3385
  var MODIFY_LOCK_SUFFIX = ".modify";
@@ -3333,6 +3387,8 @@ var POLL_INTERVAL_MS = 2e3;
3333
3387
  var WAIT_LOG_INTERVAL_MS = 1e4;
3334
3388
  var MODIFY_LOCK_RETRY_MS = 50;
3335
3389
  var MODIFY_LOCK_STALE_MS = 5e3;
3390
+ var WAITER_ABANDON_MS = 5 * POLL_INTERVAL_MS;
3391
+ var HOLD_OVERRUN_GRACE_MS = STOP_REQUEST_GRACE_MS + 3 * 6e4;
3336
3392
 
3337
3393
  // packages/mcps/src/mcp/local/services/local-execution-lock.ts
3338
3394
  function lockFilePath() {
@@ -3385,8 +3441,8 @@ async function withModifyLock(fn) {
3385
3441
  } catch (error) {
3386
3442
  if (error.code !== "EEXIST") throw error;
3387
3443
  try {
3388
- const stat3 = await fs14.stat(modifyPath);
3389
- if (Date.now() - stat3.mtimeMs > MODIFY_LOCK_STALE_MS) {
3444
+ const stat4 = await fs14.stat(modifyPath);
3445
+ if (Date.now() - stat4.mtimeMs > MODIFY_LOCK_STALE_MS) {
3390
3446
  await fs14.unlink(modifyPath).catch(() => void 0);
3391
3447
  continue;
3392
3448
  }
@@ -3405,74 +3461,95 @@ async function withModifyLock(fn) {
3405
3461
  }
3406
3462
  async function tryAcquireOnce(params) {
3407
3463
  return await withModifyLock(async () => {
3464
+ const now = Date.now();
3408
3465
  const existing = await readState();
3409
- if (existing === null) {
3410
- await writeState({
3411
- holderCwd: params.normalizedCwd,
3412
- holderPids: [params.pid],
3413
- acquiredAt: Date.now()
3414
- });
3415
- return { acquired: true };
3416
- }
3417
- const liveHolderPids = existing.holderPids.filter((pid) => pidIsAlive(pid));
3418
- if (liveHolderPids.length === 0) {
3419
- getLogger().warn("Breaking stale local-execution lock", {
3420
- staleCwd: existing.holderCwd,
3421
- stalePids: existing.holderPids
3422
- });
3466
+ const isCaller = (waiter) => waiter.cwd === params.caller.cwd && waiter.pid === params.caller.pid;
3467
+ const previousPlace = existing?.waiters?.find(isCaller);
3468
+ const caller = {
3469
+ ...params.caller,
3470
+ enqueuedAt: Math.min(params.caller.enqueuedAt, previousPlace?.enqueuedAt ?? Number.POSITIVE_INFINITY),
3471
+ lastPolledAt: now
3472
+ };
3473
+ const otherWaiters = (existing?.waiters ?? []).filter(
3474
+ (waiter) => !isCaller(waiter) && pidIsAlive(waiter.pid) && now - waiter.lastPolledAt <= WAITER_ABANDON_MS
3475
+ );
3476
+ const holderPids = existing === null ? [] : findResponsiveHolderPids({ state: existing, now });
3477
+ const isHeldByPeer = holderPids.length > 0 && existing?.holderCwd !== caller.cwd;
3478
+ const isPeerAhead = otherWaiters.some((waiter) => waiter.cwd !== caller.cwd && compareQueuePlaces(waiter, caller) < 0);
3479
+ if (!isHeldByPeer && !isPeerAhead) {
3480
+ const isJoining = existing !== null && holderPids.length > 0;
3423
3481
  await writeState({
3424
- holderCwd: params.normalizedCwd,
3425
- holderPids: [params.pid],
3426
- acquiredAt: Date.now()
3482
+ holderCwd: caller.cwd,
3483
+ holderPids: [...holderPids, caller.pid],
3484
+ acquiredAt: isJoining ? existing.acquiredAt : now,
3485
+ holdDeadlineAt: Math.max(isJoining ? existing.holdDeadlineAt ?? 0 : 0, now + params.holdLimitMs),
3486
+ waiters: otherWaiters
3427
3487
  });
3428
3488
  return { acquired: true };
3429
3489
  }
3430
- if (existing.holderCwd === params.normalizedCwd) {
3431
- const updated = {
3432
- holderCwd: existing.holderCwd,
3433
- holderPids: [...liveHolderPids, params.pid],
3434
- acquiredAt: existing.acquiredAt
3435
- };
3436
- await writeState(updated);
3437
- return { acquired: true };
3438
- }
3439
- return {
3440
- acquired: false,
3441
- blockingState: { ...existing, holderPids: liveHolderPids }
3442
- };
3490
+ const line = [...otherWaiters, caller].sort(compareQueuePlaces);
3491
+ const blockingState = existing === null || holderPids.length === 0 ? { holderCwd: "", holderPids: [], acquiredAt: 0, waiters: line } : { ...existing, holderPids, waiters: line };
3492
+ await writeState(blockingState);
3493
+ return { acquired: false, blockingState, position: line.findIndex(isCaller) + 1 };
3443
3494
  });
3444
3495
  }
3496
+ function findResponsiveHolderPids(params) {
3497
+ const liveHolderPids = params.state.holderPids.filter((pid) => pidIsAlive(pid));
3498
+ if (liveHolderPids.length < params.state.holderPids.length) {
3499
+ getLogger().warn("Dropping exited local-execution lock holders", {
3500
+ holderCwd: params.state.holderCwd,
3501
+ exitedPids: params.state.holderPids.filter((pid) => !liveHolderPids.includes(pid))
3502
+ });
3503
+ }
3504
+ const isStuck = params.state.holdDeadlineAt !== void 0 && params.now > params.state.holdDeadlineAt;
3505
+ if (liveHolderPids.length > 0 && isStuck) {
3506
+ getLogger().warn("Breaking stuck local-execution lock \u2014 its holder outran its run timeout", {
3507
+ stuckCwd: params.state.holderCwd,
3508
+ stuckPids: liveHolderPids,
3509
+ overrunSec: Math.round((params.now - params.state.holdDeadlineAt) / 1e3)
3510
+ });
3511
+ return [];
3512
+ }
3513
+ return liveHolderPids;
3514
+ }
3445
3515
  async function decrementHolder(params) {
3446
3516
  await withModifyLock(async () => {
3447
3517
  const existing = await readState();
3448
3518
  if (existing === null) return;
3449
- if (existing.holderCwd !== params.normalizedCwd) {
3450
- getLogger().warn("Release skipped \u2014 holder cwd no longer matches", {
3519
+ const removalIndex = existing.holderPids.indexOf(params.pid);
3520
+ if (existing.holderCwd !== params.normalizedCwd || removalIndex < 0) {
3521
+ getLogger().warn("Release skipped \u2014 no longer a holder; the lock passed on", {
3451
3522
  ourCwd: params.normalizedCwd,
3452
3523
  holderCwd: existing.holderCwd
3453
3524
  });
3454
3525
  return;
3455
3526
  }
3456
- const removalIndex = existing.holderPids.indexOf(params.pid);
3457
- const remainingPids = removalIndex >= 0 ? [...existing.holderPids.slice(0, removalIndex), ...existing.holderPids.slice(removalIndex + 1)] : existing.holderPids;
3458
- if (remainingPids.length === 0) {
3527
+ const remainingPids = [...existing.holderPids.slice(0, removalIndex), ...existing.holderPids.slice(removalIndex + 1)];
3528
+ if (remainingPids.length > 0) {
3529
+ await writeState({ ...existing, holderPids: remainingPids });
3530
+ return;
3531
+ }
3532
+ if ((existing.waiters ?? []).length === 0) {
3459
3533
  await deleteStateFile();
3460
3534
  return;
3461
3535
  }
3462
- await writeState({
3463
- holderCwd: existing.holderCwd,
3464
- holderPids: remainingPids,
3465
- acquiredAt: existing.acquiredAt
3466
- });
3536
+ await writeState({ holderCwd: "", holderPids: [], acquiredAt: 0, waiters: existing.waiters });
3467
3537
  });
3468
3538
  }
3469
3539
  async function acquireLocalExecutionLock(params) {
3470
3540
  const normalizedCwd = normalizeCwd(params.cwd);
3471
3541
  const pid = process.pid;
3472
3542
  const startWaitAt = Date.now();
3543
+ const caller = {
3544
+ cwd: normalizedCwd,
3545
+ pid,
3546
+ pullRequest: await (params.lookUpPullRequest ?? lookUpCheckoutPullRequest)(normalizedCwd),
3547
+ enqueuedAt: startWaitAt,
3548
+ lastPolledAt: startWaitAt
3549
+ };
3473
3550
  let lastWaitLogAt = 0;
3474
3551
  while (true) {
3475
- const outcome = await tryAcquireOnce({ normalizedCwd, pid });
3552
+ const outcome = await tryAcquireOnce({ caller, holdLimitMs: params.runTimeoutMs + HOLD_OVERRUN_GRACE_MS });
3476
3553
  if (outcome.acquired) {
3477
3554
  return {
3478
3555
  release: () => decrementHolder({ normalizedCwd, pid })
@@ -3485,6 +3562,8 @@ async function acquireLocalExecutionLock(params) {
3485
3562
  ourCwd: normalizedCwd,
3486
3563
  heldBy: outcome.blockingState?.holderCwd,
3487
3564
  heldByPids: outcome.blockingState?.holderPids,
3565
+ position: outcome.position,
3566
+ lineLength: outcome.blockingState?.waiters?.length,
3488
3567
  waitedSec
3489
3568
  });
3490
3569
  lastWaitLogAt = now;
@@ -3733,10 +3812,10 @@ async function scanForRuntimeDirByMtime(params) {
3733
3812
  for (const entry of entries) {
3734
3813
  if (!entry.isDirectory()) continue;
3735
3814
  const fullPath = path16.join(runtimeRoot, entry.name);
3736
- const stat3 = await fs14.stat(fullPath);
3737
- if (stat3.mtimeMs < params.spawnAtMs) continue;
3738
- if (bestCandidate === null || stat3.mtimeMs > bestCandidate.mtimeMs) {
3739
- bestCandidate = { path: fullPath, mtimeMs: stat3.mtimeMs };
3815
+ const stat4 = await fs14.stat(fullPath);
3816
+ if (stat4.mtimeMs < params.spawnAtMs) continue;
3817
+ if (bestCandidate === null || stat4.mtimeMs > bestCandidate.mtimeMs) {
3818
+ bestCandidate = { path: fullPath, mtimeMs: stat4.mtimeMs };
3740
3819
  }
3741
3820
  }
3742
3821
  return bestCandidate?.path ?? null;
@@ -3923,7 +4002,10 @@ ${processInfo.capturedStderr}`
3923
4002
  });
3924
4003
  }
3925
4004
  async function executeTestGeneration(params) {
3926
- const lockHandle = await acquireLocalExecutionLock({ cwd: params.cwd });
4005
+ const lockHandle = await acquireLocalExecutionLock({
4006
+ cwd: params.cwd,
4007
+ runTimeoutMs: params.timeoutMs ?? DEFAULT_GENERATION_TIMEOUT_MS
4008
+ });
3927
4009
  try {
3928
4010
  return await runTestGenerationLocked(params);
3929
4011
  } finally {
@@ -3932,7 +4014,7 @@ async function executeTestGeneration(params) {
3932
4014
  }
3933
4015
  async function runTestGenerationLocked(params) {
3934
4016
  const { testCase, localUrl } = params;
3935
- const timeoutMs = params.timeoutMs ?? 3e5;
4017
+ const timeoutMs = params.timeoutMs ?? DEFAULT_GENERATION_TIMEOUT_MS;
3936
4018
  const userId = getAuthenticatedUserId();
3937
4019
  const authContent = buildStudioAuthContent();
3938
4020
  if (!testCase.url) {
@@ -4119,7 +4201,10 @@ async function runTestGenerationLocked(params) {
4119
4201
  }
4120
4202
  }
4121
4203
  async function executeReplay(params) {
4122
- const lockHandle = await acquireLocalExecutionLock({ cwd: params.cwd });
4204
+ const lockHandle = await acquireLocalExecutionLock({
4205
+ cwd: params.cwd,
4206
+ runTimeoutMs: params.timeoutMs ?? DEFAULT_REPLAY_TIMEOUT_MS
4207
+ });
4123
4208
  try {
4124
4209
  return await runReplayLocked(params);
4125
4210
  } finally {
@@ -4128,7 +4213,7 @@ async function executeReplay(params) {
4128
4213
  }
4129
4214
  async function runReplayLocked(params) {
4130
4215
  const { testScript, actionScript, localUrl } = params;
4131
- const timeoutMs = params.timeoutMs ?? 18e4;
4216
+ const timeoutMs = params.timeoutMs ?? DEFAULT_REPLAY_TIMEOUT_MS;
4132
4217
  const userId = getAuthenticatedUserId();
4133
4218
  const authContent = buildStudioAuthContent();
4134
4219
  if (!testScript.url) {
@@ -5188,6 +5273,13 @@ function toolRequiresAuth(toolName) {
5188
5273
  }
5189
5274
 
5190
5275
  // packages/mcps/src/shared/service-stack/service-stack-types.ts
5276
+ var StackLeaseEndReason = /* @__PURE__ */ ((StackLeaseEndReason2) => {
5277
+ StackLeaseEndReason2["NeverServed"] = "NEVER_SERVED";
5278
+ StackLeaseEndReason2["NoRunAfterServed"] = "NO_RUN_AFTER_SERVED";
5279
+ StackLeaseEndReason2["Idle"] = "IDLE";
5280
+ StackLeaseEndReason2["HolderExited"] = "HOLDER_EXITED";
5281
+ return StackLeaseEndReason2;
5282
+ })(StackLeaseEndReason || {});
5191
5283
  var StackClaimVerdict = /* @__PURE__ */ ((StackClaimVerdict2) => {
5192
5284
  StackClaimVerdict2["Granted"] = "GRANTED";
5193
5285
  StackClaimVerdict2["Busy"] = "BUSY";
@@ -5204,6 +5296,7 @@ var StackCheckVerdict = /* @__PURE__ */ ((StackCheckVerdict2) => {
5204
5296
  StackCheckVerdict2["NoLease"] = "NO_LEASE";
5205
5297
  StackCheckVerdict2["Stale"] = "STALE";
5206
5298
  StackCheckVerdict2["PeerLease"] = "PEER_LEASE";
5299
+ StackCheckVerdict2["Ended"] = "ENDED";
5207
5300
  return StackCheckVerdict2;
5208
5301
  })(StackCheckVerdict || {});
5209
5302
  var StackMismatchKind = /* @__PURE__ */ ((StackMismatchKind2) => {
@@ -5216,7 +5309,9 @@ var StackMismatchKind = /* @__PURE__ */ ((StackMismatchKind2) => {
5216
5309
  // packages/mcps/src/shared/service-stack/service-stack-constants.ts
5217
5310
  var STACK_LEDGER_DIR_NAME = "service-stack";
5218
5311
  var PREPARE_TRACKING_DIR_SEGMENTS = ["prepare", "running"];
5219
- var STARTUP_WINDOW_MS = 15 * 6e4;
5312
+ var SERVE_WINDOW_MS = 8 * 6e4;
5313
+ var FIRST_RUN_WINDOW_MS = 4 * 6e4;
5314
+ var ENDED_LEASE_MEMORY_MS = 30 * 6e4;
5220
5315
  var IDLE_RELEASE_MS = 2 * 6e4;
5221
5316
  var MAX_RUN_MS = 2 * 60 * 6e4;
5222
5317
  var DEAD_HOLDER_GRACE_MS = 6e4;
@@ -5230,6 +5325,12 @@ var OWED_RETRY_AFTER_MS = 5e3;
5230
5325
  var CLAIM_POLL_INTERVAL_MS = 1e3;
5231
5326
  var BUSY_EXIT_CODE = 75;
5232
5327
  var LISTENER_COMMAND_MAX_CHARS = 120;
5328
+ var LEASE_END_REASON_TEXT = {
5329
+ ["NEVER_SERVED" /* NeverServed */]: `its ports did not serve the claimed code within ${SERVE_WINDOW_MS / 6e4} minutes of the grant`,
5330
+ ["NO_RUN_AFTER_SERVED" /* NoRunAfterServed */]: `no run started within ${FIRST_RUN_WINDOW_MS / 6e4} minutes of its services coming up`,
5331
+ ["IDLE" /* Idle */]: `no run started within ${IDLE_RELEASE_MS / 6e4} minutes of the last one`,
5332
+ ["HOLDER_EXITED" /* HolderExited */]: "every MCP server that ran under it exited"
5333
+ };
5233
5334
  var DEFAULT_PORT_BY_PROTOCOL = { "http:": 80, "https:": 443 };
5234
5335
 
5235
5336
  // packages/mcps/src/shared/service-stack/generation-store-constants.ts
@@ -5461,22 +5562,34 @@ function needsConflict(left, right) {
5461
5562
  }
5462
5563
  function sweepStackRecord(params) {
5463
5564
  const { record, now } = params;
5464
- const isLive = (lease) => !hasLeaseEnded({ lease, now, isAlive: params.isAlive });
5465
- const liveLeases = record.leases.filter(isLive);
5466
- const expiredLeases = record.leases.filter((lease) => !isLive(lease));
5565
+ const servedSince = params.servedSince ?? isServedByNothing;
5566
+ const endings = record.leases.map((lease) => ({
5567
+ lease,
5568
+ reason: findLeaseEndReason({ lease, now, isAlive: params.isAlive, servedSince })
5569
+ }));
5570
+ const liveLeases = endings.filter((ending) => ending.reason === null).map((ending) => ending.lease);
5571
+ const endedNow = endings.flatMap(
5572
+ ({ lease, reason }) => reason === null ? [] : [{ cwd: lease.cwd, label: lease.label, endedAt: now, reason }]
5573
+ );
5467
5574
  return {
5468
5575
  leases: liveLeases,
5469
- queue: record.queue.filter((ticket) => now - ticket.lastSeenAt <= TICKET_ABANDON_MS),
5576
+ queue: record.queue.filter((ticket) => now - ticket.lastSeenAt <= TICKET_ABANDON_MS).sort(compareQueuePlaces),
5470
5577
  portFreedAt: markFreedPorts({
5471
5578
  portFreedAt: forgetSettledPorts({ portFreedAt: record.portFreedAt, now }),
5472
- releasedNeeds: expiredLeases.flatMap((lease) => lease.needs),
5579
+ releasedNeeds: endings.filter((ending) => ending.reason !== null).flatMap((ending) => ending.lease.needs),
5473
5580
  remainingLeases: liveLeases,
5474
5581
  now
5582
+ }),
5583
+ endedLeases: rememberEndedLeases({
5584
+ endedLeases: record.endedLeases ?? [],
5585
+ endedNow,
5586
+ liveLeases,
5587
+ now
5475
5588
  })
5476
5589
  };
5477
5590
  }
5478
5591
  function decideStackClaim(params) {
5479
- const swept = sweepStackRecord({ record: params.record, now: params.now, isAlive: params.isAlive });
5592
+ const swept = sweepStackRecord(params);
5480
5593
  const peerLeases = swept.leases.filter((lease) => lease.cwd !== params.cwd);
5481
5594
  const leaseBlockers = peerLeases.flatMap((lease) => leaseBlockersFor({ lease, needs: params.needs, now: params.now }));
5482
5595
  const queueWithCaller = upsertTicket({
@@ -5484,6 +5597,7 @@ function decideStackClaim(params) {
5484
5597
  cwd: params.cwd,
5485
5598
  label: params.label,
5486
5599
  needs: params.needs,
5600
+ pullRequest: params.pullRequest,
5487
5601
  now: params.now
5488
5602
  });
5489
5603
  const position = queueWithCaller.findIndex((ticket) => ticket.cwd === params.cwd) + 1;
@@ -5512,7 +5626,8 @@ function decideStackClaim(params) {
5512
5626
  releasedNeeds: ownLease?.needs ?? [],
5513
5627
  remainingLeases: leases,
5514
5628
  now: params.now
5515
- })
5629
+ }),
5630
+ endedLeases: (swept.endedLeases ?? []).filter((ended) => ended.cwd !== params.cwd)
5516
5631
  },
5517
5632
  blockers: [],
5518
5633
  sharedWith: peerLeases.filter((lease) => lease.needs.some((need) => params.needs.some((own) => own.port === need.port))).map((lease) => lease.cwd),
@@ -5523,7 +5638,7 @@ function decideStackClaim(params) {
5523
5638
  }
5524
5639
  function decideStackRelease(params) {
5525
5640
  const isReleased = params.record.leases.some((lease) => lease.cwd === params.cwd) || params.record.queue.some((ticket) => ticket.cwd === params.cwd);
5526
- const swept = sweepStackRecord({ record: params.record, now: params.now, isAlive: params.isAlive });
5641
+ const swept = sweepStackRecord(params);
5527
5642
  if (!isReleased) return { next: hasSweepDropped({ record: params.record, swept }) ? swept : null, isReleased: false };
5528
5643
  const ownLease = swept.leases.find((lease) => lease.cwd === params.cwd);
5529
5644
  const leases = swept.leases.filter((lease) => lease.cwd !== params.cwd);
@@ -5535,7 +5650,8 @@ function decideStackRelease(params) {
5535
5650
  releasedNeeds: ownLease?.needs ?? [],
5536
5651
  remainingLeases: leases,
5537
5652
  now: params.now
5538
- })
5653
+ }),
5654
+ endedLeases: (swept.endedLeases ?? []).filter((ended) => ended.cwd !== params.cwd)
5539
5655
  };
5540
5656
  return { next, isReleased: true };
5541
5657
  }
@@ -5544,7 +5660,7 @@ function decideStackReap(params) {
5544
5660
  return hasSweepDropped({ record: params.record, swept }) ? swept : null;
5545
5661
  }
5546
5662
  function decideStackRunStart(params) {
5547
- const swept = sweepStackRecord({ record: params.record, now: params.now, isAlive: params.isAlive });
5663
+ const swept = sweepStackRecord(params);
5548
5664
  if (!swept.leases.some((lease) => lease.cwd === params.cwd)) return decideStackReap(params);
5549
5665
  const start = (lease) => {
5550
5666
  const liveHolders = (lease.holderPids ?? []).filter((pid) => params.isAlive(pid));
@@ -5560,7 +5676,7 @@ function decideStackRunStart(params) {
5560
5676
  return { ...swept, leases: swept.leases.map((lease) => lease.cwd === params.cwd ? start(lease) : lease) };
5561
5677
  }
5562
5678
  function decideStackRunEnd(params) {
5563
- const swept = sweepStackRecord({ record: params.record, now: params.now, isAlive: params.isAlive });
5679
+ const swept = sweepStackRecord(params);
5564
5680
  if (!swept.leases.some((lease) => lease.cwd === params.cwd)) return decideStackReap(params);
5565
5681
  const finish = (lease) => {
5566
5682
  const activeRuns = liveRuns({ lease, now: params.now, isAlive: params.isAlive }).filter(
@@ -5585,13 +5701,31 @@ function findVacatedNeeds(params) {
5585
5701
  }
5586
5702
  return vacated;
5587
5703
  }
5588
- function hasLeaseEnded(params) {
5589
- if (liveRuns(params).length > 0) return false;
5704
+ function findLeaseEndReason(params) {
5705
+ if (liveRuns(params).length > 0) return null;
5590
5706
  const holderPids = params.lease.holderPids ?? [];
5591
5707
  const isHolderGone = holderPids.length > 0 && !holderPids.some((pid) => params.isAlive(pid));
5592
- if (isHolderGone && params.now - params.lease.lastSeenAt > DEAD_HOLDER_GRACE_MS) return true;
5708
+ if (isHolderGone && params.now - params.lease.lastSeenAt > DEAD_HOLDER_GRACE_MS) return "HOLDER_EXITED" /* HolderExited */;
5593
5709
  const lastRunEndedAt = params.lease.lastRunEndedAt;
5594
- return lastRunEndedAt === void 0 ? params.now - params.lease.lastSeenAt > STARTUP_WINDOW_MS : params.now - lastRunEndedAt > IDLE_RELEASE_MS;
5710
+ if (lastRunEndedAt !== void 0) return params.now - lastRunEndedAt > IDLE_RELEASE_MS ? "IDLE" /* Idle */ : null;
5711
+ const servedAt = findLeaseServedAt({ lease: params.lease, servedSince: params.servedSince });
5712
+ if (servedAt === null) return params.now - params.lease.lastSeenAt > SERVE_WINDOW_MS ? "NEVER_SERVED" /* NeverServed */ : null;
5713
+ return params.now - Math.max(servedAt, params.lease.lastSeenAt) > FIRST_RUN_WINDOW_MS ? "NO_RUN_AFTER_SERVED" /* NoRunAfterServed */ : null;
5714
+ }
5715
+ function findLeaseServedAt(params) {
5716
+ const servedTimes = params.lease.needs.map((need) => params.servedSince(need));
5717
+ if (servedTimes.some((servedAt) => servedAt === null)) return null;
5718
+ return Math.max(params.lease.grantedAt, ...servedTimes);
5719
+ }
5720
+ function isServedByNothing() {
5721
+ return null;
5722
+ }
5723
+ function rememberEndedLeases(params) {
5724
+ const isSuperseded = (ended) => params.endedNow.some((latest) => latest.cwd === ended.cwd) || params.liveLeases.some((lease) => lease.cwd === ended.cwd);
5725
+ const remembered = params.endedLeases.filter(
5726
+ (ended) => params.now - ended.endedAt <= ENDED_LEASE_MEMORY_MS && !isSuperseded(ended)
5727
+ );
5728
+ return [...remembered, ...params.endedNow];
5595
5729
  }
5596
5730
  function isSameNeed(left, right) {
5597
5731
  return left.port === right.port && left.runDir === right.runDir && left.headSha === right.headSha;
@@ -5627,16 +5761,23 @@ function ticketBlockersFor(params) {
5627
5761
  }));
5628
5762
  }
5629
5763
  function upsertTicket(params) {
5630
- const existing = params.queue.find((ticket) => ticket.cwd === params.cwd);
5631
- if (existing === void 0) {
5632
- return [
5633
- ...params.queue,
5634
- { cwd: params.cwd, label: params.label, needs: params.needs, enqueuedAt: params.now, lastSeenAt: params.now }
5635
- ];
5636
- }
5637
- return params.queue.map(
5638
- (ticket) => ticket.cwd === params.cwd ? { ...ticket, label: params.label ?? ticket.label, needs: params.needs, lastSeenAt: params.now } : ticket
5639
- );
5764
+ const existing = params.queue.find((ticket2) => ticket2.cwd === params.cwd);
5765
+ const ticket = existing === void 0 ? {
5766
+ cwd: params.cwd,
5767
+ label: params.label,
5768
+ needs: params.needs,
5769
+ pullRequest: params.pullRequest ?? void 0,
5770
+ enqueuedAt: params.now,
5771
+ lastSeenAt: params.now
5772
+ } : {
5773
+ ...existing,
5774
+ label: params.label ?? existing.label,
5775
+ needs: params.needs,
5776
+ pullRequest: params.pullRequest ?? existing.pullRequest,
5777
+ lastSeenAt: params.now
5778
+ };
5779
+ const queue = existing === void 0 ? [...params.queue, ticket] : params.queue.map((queued) => queued.cwd === params.cwd ? ticket : queued);
5780
+ return queue.sort(compareQueuePlaces);
5640
5781
  }
5641
5782
  function isOwedTurn(params) {
5642
5783
  if (params.now - params.ticket.lastSeenAt < CLAIM_GRACE_MS) return true;
@@ -5692,9 +5833,10 @@ async function claimServiceStack(params) {
5692
5833
  const runGit = params.runGit ?? runGitCommand;
5693
5834
  const cwd = await resolveCheckoutRoot({ cwd: params.cwd, runGit });
5694
5835
  const needs = await resolveNeeds({ needs: params.needs, runGit });
5836
+ const pullRequest = await (params.lookUpPullRequest ?? lookUpCheckoutPullRequest)(cwd);
5695
5837
  const startedAt = Date.now();
5696
5838
  for (; ; ) {
5697
- const report = await claimOnce({ cwd, label: params.label, needs });
5839
+ const report = await claimOnce({ cwd, label: params.label, needs, pullRequest });
5698
5840
  const hasWaitLeft = params.waitMs !== void 0 && Date.now() - startedAt < params.waitMs;
5699
5841
  if (report.verdict === "GRANTED" /* Granted */ || !hasWaitLeft) return report;
5700
5842
  await sleep3(CLAIM_POLL_INTERVAL_MS);
@@ -5703,7 +5845,13 @@ async function claimServiceStack(params) {
5703
5845
  async function checkServiceStack(params) {
5704
5846
  const cwd = await resolveCheckoutRoot({ cwd: params.cwd, runGit: params.runGit ?? runGitCommand });
5705
5847
  const snapshot = await readGenerationStore({ directory: ledgerDir(), emptyRecord: EMPTY_STACK_RECORD });
5706
- const swept = sweepStackRecord({ record: snapshot.record, now: Date.now(), isAlive: isProcessAlive });
5848
+ const services = await readTrackedServices();
5849
+ const swept = sweepStackRecord({
5850
+ record: snapshot.record,
5851
+ now: Date.now(),
5852
+ isAlive: isProcessAlive,
5853
+ servedSince: servedSinceFrom(services)
5854
+ });
5707
5855
  const ownLease = swept.leases.find((lease) => lease.cwd === cwd) ?? null;
5708
5856
  const base = { cwd, generation: snapshot.generation, mismatches: [], peerLease: null, servedNeeds: [] };
5709
5857
  const isClaimedByCaller = ownLease?.needs.some((need) => need.port === params.port) ?? false;
@@ -5717,10 +5865,17 @@ async function checkServiceStack(params) {
5717
5865
  peerLease
5718
5866
  };
5719
5867
  }
5868
+ const endedLease = ownLease === null ? swept.endedLeases?.find((ended) => ended.cwd === cwd) : void 0;
5869
+ if (endedLease !== void 0) {
5870
+ return {
5871
+ ...base,
5872
+ verdict: "ENDED" /* Ended */,
5873
+ reason: `this checkout's lease ended ${Math.round((Date.now() - endedLease.endedAt) / 1e3)}s ago: ${LEASE_END_REASON_TEXT[endedLease.reason]}`
5874
+ };
5875
+ }
5720
5876
  if (ownLease === null) {
5721
5877
  return { ...base, verdict: "NO_LEASE" /* NoLease */, reason: "this checkout holds no stack lease" };
5722
5878
  }
5723
- const services = await readTrackedServices();
5724
5879
  const readListener = params.readListener ?? readPortListener;
5725
5880
  const mismatches = (await Promise.all(
5726
5881
  ownLease.needs.map((need) => findPortMismatch({ need, services, readListener }))
@@ -5743,48 +5898,68 @@ async function checkServiceStack(params) {
5743
5898
  }
5744
5899
  async function startStackRun(params) {
5745
5900
  const cwd = await resolveCheckoutRoot({ cwd: params.cwd, runGit: params.runGit ?? runGitCommand });
5746
- await updateLedger((record) => ({
5901
+ await updateLedger((record, servedSince) => ({
5747
5902
  next: decideStackRunStart({
5748
5903
  record,
5749
5904
  cwd,
5750
5905
  runId: params.runId,
5751
5906
  holderPid: params.holderPid,
5752
5907
  now: Date.now(),
5753
- isAlive: isProcessAlive
5908
+ isAlive: isProcessAlive,
5909
+ servedSince
5754
5910
  }),
5755
5911
  result: null
5756
5912
  }));
5757
5913
  }
5758
5914
  async function finishStackRun(params) {
5759
5915
  const cwd = await resolveCheckoutRoot({ cwd: params.cwd, runGit: params.runGit ?? runGitCommand });
5760
- await updateLedger((record) => ({
5761
- next: decideStackRunEnd({ record, cwd, runId: params.runId, now: Date.now(), isAlive: isProcessAlive }),
5916
+ await updateLedger((record, servedSince) => ({
5917
+ next: decideStackRunEnd({
5918
+ record,
5919
+ cwd,
5920
+ runId: params.runId,
5921
+ now: Date.now(),
5922
+ isAlive: isProcessAlive,
5923
+ servedSince
5924
+ }),
5762
5925
  result: null
5763
5926
  }));
5764
5927
  }
5765
5928
  async function reapServiceStack() {
5766
- const update = await updateLedger((record) => ({
5767
- next: decideStackReap({ record, now: Date.now(), isAlive: isProcessAlive }),
5929
+ const update = await updateLedger((record, servedSince) => ({
5930
+ next: decideStackReap({ record, now: Date.now(), isAlive: isProcessAlive, servedSince }),
5768
5931
  result: null
5769
5932
  }));
5770
5933
  return update.stoppedServices;
5771
5934
  }
5772
5935
  async function releaseServiceStack(params) {
5773
5936
  const cwd = await resolveCheckoutRoot({ cwd: params.cwd, runGit: params.runGit ?? runGitCommand });
5774
- const update = await updateLedger((record) => {
5775
- const decision = decideStackRelease({ record, cwd, now: Date.now(), isAlive: isProcessAlive });
5937
+ const update = await updateLedger((record, servedSince) => {
5938
+ const decision = decideStackRelease({
5939
+ record,
5940
+ cwd,
5941
+ now: Date.now(),
5942
+ isAlive: isProcessAlive,
5943
+ servedSince
5944
+ });
5776
5945
  return { next: decision.next, result: decision.isReleased };
5777
5946
  });
5778
5947
  return { isReleased: update.result, stoppedServices: update.stoppedServices };
5779
5948
  }
5780
5949
  async function readServiceStackStatus() {
5781
5950
  const snapshot = await readGenerationStore({ directory: ledgerDir(), emptyRecord: EMPTY_STACK_RECORD });
5782
- const swept = sweepStackRecord({ record: snapshot.record, now: Date.now(), isAlive: isProcessAlive });
5951
+ const services = await readTrackedServices();
5952
+ const swept = sweepStackRecord({
5953
+ record: snapshot.record,
5954
+ now: Date.now(),
5955
+ isAlive: isProcessAlive,
5956
+ servedSince: servedSinceFrom(services)
5957
+ });
5783
5958
  return {
5784
5959
  generation: snapshot.generation,
5785
5960
  leases: swept.leases,
5786
5961
  queue: swept.queue,
5787
- services: await readTrackedServices()
5962
+ services
5788
5963
  };
5789
5964
  }
5790
5965
  function portOfUrl(url) {
@@ -5797,14 +5972,16 @@ function portOfUrl(url) {
5797
5972
  }
5798
5973
  }
5799
5974
  async function claimOnce(params) {
5800
- const update = await updateLedger((record) => {
5975
+ const update = await updateLedger((record, servedSince) => {
5801
5976
  const decision2 = decideStackClaim({
5802
5977
  record,
5803
5978
  cwd: params.cwd,
5804
5979
  label: params.label,
5805
5980
  needs: params.needs,
5981
+ pullRequest: params.pullRequest,
5806
5982
  now: Date.now(),
5807
- isAlive: isProcessAlive
5983
+ isAlive: isProcessAlive,
5984
+ servedSince
5808
5985
  });
5809
5986
  return { next: decision2.next, result: decision2 };
5810
5987
  });
@@ -5825,11 +6002,12 @@ async function claimOnce(params) {
5825
6002
  };
5826
6003
  }
5827
6004
  async function updateLedger(decide) {
6005
+ const servedSince = servedSinceFrom(await readTrackedServices());
5828
6006
  const outcome = await updateGenerationStore({
5829
6007
  directory: ledgerDir(),
5830
6008
  emptyRecord: EMPTY_STACK_RECORD,
5831
6009
  decide: (snapshot) => {
5832
- const update = decide(snapshot.record);
6010
+ const update = decide(snapshot.record, servedSince);
5833
6011
  const vacatedNeeds = update.next === null ? [] : findVacatedNeeds({ before: snapshot.record, after: update.next });
5834
6012
  return {
5835
6013
  next: update.next,
@@ -5874,6 +6052,12 @@ async function findPortMismatch(params) {
5874
6052
  if (isTrackedListener) return null;
5875
6053
  return { need: params.need, kind: "UNTRACKED_LISTENER" /* UntrackedListener */, actual: tracked, listener: reading.listener };
5876
6054
  }
6055
+ function servedSinceFrom(services) {
6056
+ return (need) => {
6057
+ const service = trackedServiceOnPort({ services, port: need.port });
6058
+ return service !== null && servesNeed({ service, need }) ? service.startedAt : null;
6059
+ };
6060
+ }
5877
6061
  function servesNeed(params) {
5878
6062
  return params.service.dir === params.need.runDir && params.service.headSha === params.need.headSha;
5879
6063
  }
@@ -5886,7 +6070,9 @@ async function readTrackedServices() {
5886
6070
  const perFile = await Promise.all(
5887
6071
  fileNames.filter((fileName) => fileName.endsWith(".json")).map(async (fileName) => {
5888
6072
  try {
5889
- const parsed = JSON.parse(await fs14.readFile(path16.join(directory, fileName), "utf-8"));
6073
+ const filePath = path16.join(directory, fileName);
6074
+ const [content, stat4] = await Promise.all([fs14.readFile(filePath, "utf-8"), fs14.stat(filePath)]);
6075
+ const parsed = JSON.parse(content);
5890
6076
  return (parsed.services ?? []).filter((service) => typeof service.port === "number" && typeof service.pid === "number").filter((service) => isProcessAlive(service.pid)).map(
5891
6077
  (service) => ({
5892
6078
  name: service.name ?? "",
@@ -5895,7 +6081,8 @@ async function readTrackedServices() {
5895
6081
  pid: service.pid,
5896
6082
  headSha: service.headSha ?? null,
5897
6083
  source: normalizePath(parsed.source ?? ""),
5898
- isExternal: service.external === true
6084
+ isExternal: service.external === true,
6085
+ startedAt: parseStartedAt(service.startedAt) ?? stat4.mtimeMs
5899
6086
  })
5900
6087
  );
5901
6088
  } catch {
@@ -5905,6 +6092,10 @@ async function readTrackedServices() {
5905
6092
  );
5906
6093
  return perFile.flat();
5907
6094
  }
6095
+ function parseStartedAt(startedAt) {
6096
+ const parsed = startedAt === void 0 ? Number.NaN : Date.parse(startedAt);
6097
+ return Number.isNaN(parsed) ? null : parsed;
6098
+ }
5908
6099
  async function resolveNeeds(params) {
5909
6100
  return await Promise.all(
5910
6101
  params.needs.map(async (need) => {
@@ -9588,7 +9779,11 @@ function writeLastHost(cwd, host) {
9588
9779
  function clearLastHost(cwd) {
9589
9780
  clearCwdEntry(LAST_HOST_CACHE, cwd);
9590
9781
  }
9591
- var REFUSING_VERDICTS = /* @__PURE__ */ new Set(["PEER_LEASE" /* PeerLease */, "STALE" /* Stale */]);
9782
+ var REFUSING_VERDICTS = /* @__PURE__ */ new Set([
9783
+ "PEER_LEASE" /* PeerLease */,
9784
+ "STALE" /* Stale */,
9785
+ "ENDED" /* Ended */
9786
+ ]);
9592
9787
  var idleReapTimer = null;
9593
9788
  async function findRunStackRefusal(params) {
9594
9789
  if (!isLoopbackUrl(params.localUrl)) return null;
@@ -9709,11 +9904,17 @@ function formatRunCheckoutRefusal(report, cwd) {
9709
9904
  }
9710
9905
 
9711
9906
  // packages/mcps/src/mcp/tools/local/run-stack-refusal.ts
9907
+ var RE_PREPARE_REMEDY = "Re-run muggle-test-prepare from this checkout: it re-claims the stack and restarts what moved.";
9908
+ var REFUSAL_HEADING_BY_VERDICT = {
9909
+ ["PEER_LEASE" /* PeerLease */]: "## Run refused \u2014 the target port is leased to another checkout",
9910
+ ["STALE" /* Stale */]: "## Run refused \u2014 the service stack changed since this checkout claimed it",
9911
+ ["ENDED" /* Ended */]: "## Run refused \u2014 this checkout's stack lease ended and its services were stopped"
9912
+ };
9712
9913
  function formatRunStackRefusal(report, cwd) {
9713
9914
  const mismatchLines = report.mismatches.map((mismatch) => `- ${describeStackMismatch(mismatch)}`);
9714
- const remedy = report.verdict === "PEER_LEASE" /* PeerLease */ ? `Claim the stack for this checkout \u2014 \`muggle stack claim --cwd "${cwd}" --need <port>=<run-dir>\` \u2014 then wait your turn or re-run prepare once it is granted.` : `Re-run muggle-test-prepare from this checkout: it re-claims the stack and restarts what moved.`;
9915
+ const remedy = report.verdict === "PEER_LEASE" /* PeerLease */ ? `Claim the stack for this checkout \u2014 \`muggle stack claim --cwd "${cwd}" --need <port>=<run-dir>\` \u2014 then wait your turn or re-run prepare once it is granted.` : report.verdict === "ENDED" /* Ended */ ? `${RE_PREPARE_REMEDY} Ask any question before invoking it: a lease ends when its first run does not follow soon after its services come up.` : RE_PREPARE_REMEDY;
9715
9916
  return [
9716
- report.verdict === "PEER_LEASE" /* PeerLease */ ? "## Run refused \u2014 the target port is leased to another checkout" : "## Run refused \u2014 the service stack changed since this checkout claimed it",
9917
+ REFUSAL_HEADING_BY_VERDICT[report.verdict] ?? `## Run refused \u2014 ${report.verdict}`,
9717
9918
  "",
9718
9919
  `**Verdict:** ${report.verdict}`,
9719
9920
  `**Checkout:** ${report.cwd}`,
@@ -11069,4 +11270,4 @@ function applyLlmSourceSelection(selection, dataDirOverride2) {
11069
11270
  return resolveLlmSourceStatus(dataDirOverride2);
11070
11271
  }
11071
11272
 
11072
- export { ACTION_SCRIPT_FILE_NAME, BASE_REMOTE_NAME, BLOCKING_FRESHNESS_VERDICTS, BRANCH_CREATION_REFLOG_PREFIX, BUSY_EXIT_CODE, CLAUDE_CODE_AUTH_STATUS_ARGS, CLAUDE_CODE_CHECK_TIMEOUT_MS, CLAUDE_CODE_EXECUTABLE, CLAUDE_CODE_EXECUTABLE_NOT_FOUND_CODE, CLAUDE_CODE_SESSION_LINK_ENV_VARS, CLAUDE_CODE_VERSION_ARGS, CheckoutFreshnessVerdict, ClaudeCodeReadiness, DEFAULT_LOCAL_LLM_BASE_URL, DEFAULT_PREFERENCES, ELECTRON_APP_EXECUTABLE_PATH_SEGMENTS, ElectronAppPlatform, ElectronAppReleaseStream, EventName, HTML_VIEW_OFFER_LINE, LLM_CREDENTIALS_FILE_MODE, LLM_CREDENTIALS_FILE_NAME, LLM_SOURCE_HEADER, LLM_SOURCE_OPTIONS, LLM_SOURCE_OWNED_ENV_NAMES, LLM_SOURCE_PROMPT, LLM_SOURCE_SCOPE_NOTE, LlmSource, MASKED_API_KEY_PREFIX, MASKED_API_KEY_VISIBLE_CHARS, MAX_LISTED_INHERITED_COMMITS, ONBOARDING_BLANKET_OPTIONS, ONBOARDING_CHOICE_GROUPS, ONBOARDING_MAX_OFFERS, ONBOARDING_PRIMER_BULLETS, ONBOARDING_PRIMER_HEADLINE, ONBOARDING_TOGGLE_GROUPS, ONBOARDING_TOGGLE_PROMPT, ONBOARDING_UNSELECTED_TOGGLE_VALUE, OnboardingBlanketChoice, OnboardingGroupKind, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, ProjectPreferencesReconcileOutcome, RUN_SESSIONS_DIR_NAME, RUN_VIEW_DASHBOARD_LINK_LABEL, RunViewError, RuntimeTarget, SCREENSHOT_DIR_RELATIVE_PATH, SCREENSHOT_FILE_EXTENSION, ServiceName, StackCheckVerdict, StackClaimVerdict, StackMismatchKind, StackPortAction, Surface, WATCHER_LIFETIME_SECONDS, WATCHER_LIFETIME_UNBOUNDED_SECONDS, __export, __require, applyLlmSourceSelection, applyOnboardingAnswers, assertDeviceCodeClientProvisioned, buildElectronAppChecksumsUrl, buildElectronAppDirName, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, buildLlmSourcePlan, buildOnboardingPlan, buildToolInputJsonSchema, calculateFileChecksum, checkServiceStack, claimServiceStack, completeOnboarding, createApiKeyWithToken, createChildLogger, decideStackClaim, decideStackReap, decideStackRelease, decideStackRunEnd, decideStackRunStart, deleteApiKeyData, deleteCredentials, describeLlmSourceStatus, describeStackMismatch, e2e_exports2 as e2e_exports, evaluateCheckoutFreshness, findVacatedNeeds, finishStackRun, formatPreferencesOneLiner, getActiveElectronAppReleaseStream, getActiveRuntimeTarget, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDashboardBaseUrl, getDataDir, getDataDir2, getDisclosureCopy, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppReleaseTagPrefix, getElectronAppSignedFromVersion, getElectronAppVersion, getElectronAppVersionSource, getLlmSourceOption, getLocalQaTools, getLogger, getOnboardingToggleKeys, getPlatformKey, getQaTools, getReleaseSignerIdentityUri, getRunSessionsDir, getValidApiKeyData, getValidCredentials, hasApiKey, hasShownDisclosure, initTelemetry, isElectronAppInstalled, isElectronAppPlatform, isFirstRun, isValidLocalBaseUrl, loadApiKeyData, loadCredentials, local_exports2 as local_exports, markDisclosureShown, markOnboardingCompleted, maskLlmApiKey, mcp_exports, needsConflict, needsOnboarding, openBrowserUrl, parseElectronAppDirName, performLogin, performLogout, pollDeviceCode, portOfUrl, readPreferencesMetadata, readRunView, readServiceStackStatus, reapServiceStack, reconcileProjectPreferences, recordOnboardingSkip, releaseServiceStack, renderRunViewHtml, renderRunViewText, resetConfig, resetLogger, resetPreference, resolveActiveProfile, resolveActiveReleaseStream, resolveActiveReleaseTagPrefix, resolveBlanketPreferences, resolveElectronAppExecutablePath, resolveElectronAppPathOrNull, resolveLlmSourceStatus, resolveOnboardingAnswers, resolvePreferences, resolveRunSessionPath, resolveRuntimeTarget, resolveTogglePreferences, runGitCommand, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, startStackRun, stopVacatedServices, toDisplayRef, toolRequiresAuth, track, validatePreference, verifyFileChecksum, writeLlmEnvBlock, writePreferences, writePreferencesWithMetadata, writeRunViewHtml };
11273
+ export { ACTION_SCRIPT_FILE_NAME, BASE_REMOTE_NAME, BLOCKING_FRESHNESS_VERDICTS, BRANCH_CREATION_REFLOG_PREFIX, BUSY_EXIT_CODE, CLAUDE_CODE_AUTH_STATUS_ARGS, CLAUDE_CODE_CHECK_TIMEOUT_MS, CLAUDE_CODE_EXECUTABLE, CLAUDE_CODE_EXECUTABLE_NOT_FOUND_CODE, CLAUDE_CODE_SESSION_LINK_ENV_VARS, CLAUDE_CODE_VERSION_ARGS, CheckoutFreshnessVerdict, ClaudeCodeReadiness, DEFAULT_LOCAL_LLM_BASE_URL, DEFAULT_PREFERENCES, ELECTRON_APP_EXECUTABLE_PATH_SEGMENTS, ElectronAppPlatform, ElectronAppReleaseStream, EventName, HTML_VIEW_OFFER_LINE, LLM_CREDENTIALS_FILE_MODE, LLM_CREDENTIALS_FILE_NAME, LLM_SOURCE_HEADER, LLM_SOURCE_OPTIONS, LLM_SOURCE_OWNED_ENV_NAMES, LLM_SOURCE_PROMPT, LLM_SOURCE_SCOPE_NOTE, LlmSource, MASKED_API_KEY_PREFIX, MASKED_API_KEY_VISIBLE_CHARS, MAX_LISTED_INHERITED_COMMITS, ONBOARDING_BLANKET_OPTIONS, ONBOARDING_CHOICE_GROUPS, ONBOARDING_MAX_OFFERS, ONBOARDING_PRIMER_BULLETS, ONBOARDING_PRIMER_HEADLINE, ONBOARDING_TOGGLE_GROUPS, ONBOARDING_TOGGLE_PROMPT, ONBOARDING_UNSELECTED_TOGGLE_VALUE, OnboardingBlanketChoice, OnboardingGroupKind, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, ProjectPreferencesReconcileOutcome, RUN_SESSIONS_DIR_NAME, RUN_VIEW_DASHBOARD_LINK_LABEL, RunViewError, RuntimeTarget, SCREENSHOT_DIR_RELATIVE_PATH, SCREENSHOT_FILE_EXTENSION, ServiceName, StackCheckVerdict, StackClaimVerdict, StackLeaseEndReason, StackMismatchKind, StackPortAction, Surface, WATCHER_LIFETIME_SECONDS, WATCHER_LIFETIME_UNBOUNDED_SECONDS, __export, __require, applyLlmSourceSelection, applyOnboardingAnswers, assertDeviceCodeClientProvisioned, buildElectronAppChecksumsUrl, buildElectronAppDirName, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, buildLlmSourcePlan, buildOnboardingPlan, buildToolInputJsonSchema, calculateFileChecksum, checkServiceStack, claimServiceStack, compareQueuePlaces, completeOnboarding, createApiKeyWithToken, createChildLogger, decideStackClaim, decideStackReap, decideStackRelease, decideStackRunEnd, decideStackRunStart, deleteApiKeyData, deleteCredentials, describeLlmSourceStatus, describeStackMismatch, e2e_exports2 as e2e_exports, evaluateCheckoutFreshness, findVacatedNeeds, finishStackRun, formatPreferencesOneLiner, getActiveElectronAppReleaseStream, getActiveRuntimeTarget, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDashboardBaseUrl, getDataDir, getDataDir2, getDisclosureCopy, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppReleaseTagPrefix, getElectronAppSignedFromVersion, getElectronAppVersion, getElectronAppVersionSource, getLlmSourceOption, getLocalQaTools, getLogger, getOnboardingToggleKeys, getPlatformKey, getQaTools, getReleaseSignerIdentityUri, getRunSessionsDir, getValidApiKeyData, getValidCredentials, hasApiKey, hasShownDisclosure, initTelemetry, isElectronAppInstalled, isElectronAppPlatform, isFirstRun, isValidLocalBaseUrl, loadApiKeyData, loadCredentials, local_exports2 as local_exports, lookUpCheckoutPullRequest, markDisclosureShown, markOnboardingCompleted, maskLlmApiKey, mcp_exports, needsConflict, needsOnboarding, openBrowserUrl, parseElectronAppDirName, performLogin, performLogout, pollDeviceCode, portOfUrl, readPreferencesMetadata, readRunView, readServiceStackStatus, reapServiceStack, reconcileProjectPreferences, recordOnboardingSkip, releaseServiceStack, renderRunViewHtml, renderRunViewText, resetConfig, resetLogger, resetPreference, resolveActiveProfile, resolveActiveReleaseStream, resolveActiveReleaseTagPrefix, resolveBlanketPreferences, resolveElectronAppExecutablePath, resolveElectronAppPathOrNull, resolveLlmSourceStatus, resolveOnboardingAnswers, resolvePreferences, resolveRunSessionPath, resolveRuntimeTarget, resolveTogglePreferences, runGitCommand, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, startStackRun, stopVacatedServices, toDisplayRef, toolRequiresAuth, track, validatePreference, verifyFileChecksum, writeLlmEnvBlock, writePreferences, writePreferencesWithMetadata, writeRunViewHtml };