@whittlelabs/sifter 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +3 -1
  2. package/bin.js +230 -35
  3. package/bin.js.map +2 -2
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -122,7 +122,9 @@ A running Sifter deals with Sift or `claude` going away on its own, and says wha
122
122
  - **The pairing is revoked** (removed in Sift, or replaced by a newer pairing): it exits with code 3 and shows the two ways to pair again. A review it was running goes back to the queue for another Sifter.
123
123
  - **It is decommissioned from Sift's settings page**: it finishes the review in hand and exits with code 0.
124
124
 
125
- Ctrl-C stops it at any time, including while it waits.
125
+ Ctrl-C stops it at any time, including while it waits. A review in progress goes back to the queue for the next Sifter that checks in, and one that had already finished is submitted first; it quits within 20 seconds, or at once on a second Ctrl-C. Closing the terminal and `kill` do the same. If it stops without that chance (a crash, the machine asleep, the network gone), it stops checking in, and Jobs puts its reviews back in the queue once two minutes pass without a check-in (whittlelabs ADR 0129).
126
+
127
+ Each run is logged to `~/.sifter/sifter.log` as well as the terminal (`~/.sifter/<profile>/sifter.log` for a named profile), rotated to `sifter.log.1` past 5 MB. A hosted Sifter logs to stdout only.
126
128
 
127
129
  ## Check
128
130
 
package/bin.js CHANGED
@@ -22263,8 +22263,20 @@ var require_client2 = __commonJS({
22263
22263
  * callers should be rare. Returns the dispatched-job context the
22264
22264
  * subscriber needs to execute and submit.
22265
22265
  */
22266
- async acceptDispatch(jobId) {
22267
- return this.request("POST", `/api/jobs/${jobId}/attendance`);
22266
+ async acceptDispatch(jobId, options) {
22267
+ return this.request("POST", `/api/jobs/${jobId}/attendance`, options?.shiftId ? { shiftId: options.shiftId } : void 0);
22268
+ }
22269
+ // ── Shifts (whittlelabs ADR 0129) ────────────────────────────────
22270
+ /**
22271
+ * Start a shift for this process to claim on. A Jobs that predates shifts
22272
+ * answers 404; the subscribe loop then claims without one.
22273
+ */
22274
+ async startShift() {
22275
+ return this.request("POST", "/api/my/shifts");
22276
+ }
22277
+ /** End a shift, releasing every claim still taken on it back to the queue. */
22278
+ async endShift(shiftId) {
22279
+ await this.request("DELETE", `/api/my/shifts/${shiftId}`);
22268
22280
  }
22269
22281
  async submit(jobId, options) {
22270
22282
  return this.request("PUT", `/api/jobs/${jobId}/attendance`, options);
@@ -22286,12 +22298,15 @@ var require_client2 = __commonJS({
22286
22298
  params.set("offset", String(options.offset));
22287
22299
  if (options?.sort)
22288
22300
  params.set("sort", options.sort);
22301
+ if (options?.shift)
22302
+ params.set("shift", options.shift);
22289
22303
  const qs = params.toString();
22290
- const { jobs, total, directives } = await this.request("GET", `/api/my/jobs${qs ? `?${qs}` : ""}`);
22304
+ const { jobs, total, directives, shift } = await this.request("GET", `/api/my/jobs${qs ? `?${qs}` : ""}`);
22291
22305
  return {
22292
22306
  data: jobs,
22293
22307
  pagination: { total, limit: options?.limit ?? 25, offset: options?.offset ?? 0 },
22294
- directives: directives ?? null
22308
+ directives: directives ?? null,
22309
+ shift: shift ?? null
22295
22310
  };
22296
22311
  }
22297
22312
  // ── HTTP plumbing ────────────────────────────────────────────────
@@ -22382,6 +22397,17 @@ var require_subscribe = __commonJS({
22382
22397
  const rest = seconds % 60;
22383
22398
  return rest === 0 ? `${minutes}m` : `${minutes}m ${rest}s`;
22384
22399
  }
22400
+ function statusOf(err) {
22401
+ const code = err?.statusCode;
22402
+ return typeof code === "number" ? code : void 0;
22403
+ }
22404
+ var HandBack = class {
22405
+ reason;
22406
+ constructor(reason) {
22407
+ this.reason = reason;
22408
+ }
22409
+ };
22410
+ var RELEASED = Symbol("released");
22385
22411
  function newOutcome() {
22386
22412
  let resolve;
22387
22413
  let reject;
@@ -22422,7 +22448,10 @@ var require_subscribe = __commonJS({
22422
22448
  let pollTimer = null;
22423
22449
  let inflight = 0;
22424
22450
  let resolvedPoolIds = [];
22425
- const controllers = /* @__PURE__ */ new Set();
22451
+ const controllers = /* @__PURE__ */ new Map();
22452
+ let shift = null;
22453
+ let shiftsSupported = true;
22454
+ let draining = null;
22426
22455
  const holds = /* @__PURE__ */ new Set();
22427
22456
  let fatalError = null;
22428
22457
  let outcome = newOutcome();
@@ -22551,13 +22580,74 @@ var require_subscribe = __commonJS({
22551
22580
  }
22552
22581
  if (stopped || mySession !== session)
22553
22582
  return;
22554
- logger.info("subscribe loop started", { pools: resolvedPoolIds, maxConcurrency });
22583
+ try {
22584
+ const opened = await untilAvailable(() => startShift(), mySession);
22585
+ if (opened === STOPPED)
22586
+ return;
22587
+ } catch (err) {
22588
+ if (mySession === session) {
22589
+ stopped = true;
22590
+ outcome.reject(err);
22591
+ }
22592
+ throw err;
22593
+ }
22594
+ if (stopped || mySession !== session)
22595
+ return;
22596
+ logger.info("subscribe loop started", {
22597
+ pools: resolvedPoolIds,
22598
+ maxConcurrency,
22599
+ ...shift ? { shiftId: shift.id } : {}
22600
+ });
22555
22601
  schedulePoll(pollIntervalMs);
22556
22602
  }
22603
+ async function startShift() {
22604
+ if (!shiftsSupported)
22605
+ return;
22606
+ try {
22607
+ shift = await opts.jobsClient.startShift();
22608
+ } catch (err) {
22609
+ if (isFatal(err) || isUnavailable(err) || isRateLimited(err))
22610
+ throw err;
22611
+ if (statusOf(err) === 404) {
22612
+ shiftsSupported = false;
22613
+ logger.debug?.("Jobs predates shifts; claims are held until each job's deadline");
22614
+ return;
22615
+ }
22616
+ logger.warn(`Could not start a shift (${describeOutage(err)}). Claims taken meanwhile are held until each job's deadline; the next poll tries again.`);
22617
+ }
22618
+ }
22619
+ async function endShift() {
22620
+ const ending = shift;
22621
+ shift = null;
22622
+ if (!ending)
22623
+ return;
22624
+ try {
22625
+ await opts.jobsClient.endShift(ending.id);
22626
+ } catch (err) {
22627
+ logger.debug?.("could not end the shift; it will lapse", {
22628
+ shiftId: ending.id,
22629
+ error: err instanceof Error ? err.message : String(err)
22630
+ });
22631
+ }
22632
+ }
22633
+ async function shiftReleased(ended) {
22634
+ const mine = [...controllers].filter(([, held]) => held.shiftId === ended.id);
22635
+ logger.warn(`Jobs ended this Sifter's shift (${ended.endReason ?? "ended"}) and released its claims` + (mine.length > 0 ? `; ${mine.length} job(s) running on it were handed back to the queue and stop here` : "") + ". Starting a new shift.", { shiftId: ended.id });
22636
+ for (const [controller] of mine)
22637
+ controller.abort(RELEASED);
22638
+ shift = null;
22639
+ try {
22640
+ await startShift();
22641
+ } catch (err) {
22642
+ if (isFatal(err))
22643
+ throw err;
22644
+ }
22645
+ }
22557
22646
  async function finish() {
22558
22647
  while (inflight > 0) {
22559
22648
  await sleep(100);
22560
22649
  }
22650
+ await endShift();
22561
22651
  logger.info("subscribe loop stopped");
22562
22652
  if (fatalError !== null)
22563
22653
  outcome.reject(fatalError.error);
@@ -22572,45 +22662,61 @@ var require_subscribe = __commonJS({
22572
22662
  }
22573
22663
  wakeStartup?.();
22574
22664
  }
22575
- async function stop() {
22665
+ async function stop(options = {}) {
22666
+ if (options.handBack !== void 0) {
22667
+ for (const [controller] of controllers)
22668
+ controller.abort(new HandBack(options.handBack));
22669
+ }
22670
+ if (draining)
22671
+ return draining;
22576
22672
  if (stopped)
22577
22673
  return;
22578
22674
  halt();
22579
- await finish();
22675
+ draining = finish().finally(() => {
22676
+ draining = null;
22677
+ });
22678
+ return draining;
22580
22679
  }
22581
22680
  async function fail(err) {
22582
22681
  if (fatalError === null)
22583
22682
  fatalError = { error: err };
22584
- for (const controller of controllers)
22683
+ for (const [controller] of controllers)
22585
22684
  controller.abort(err);
22685
+ if (draining)
22686
+ return draining;
22586
22687
  if (stopped)
22587
22688
  return;
22588
22689
  halt();
22589
- await finish();
22690
+ draining = finish().finally(() => {
22691
+ draining = null;
22692
+ });
22693
+ return draining;
22590
22694
  }
22591
22695
  function schedulePoll(delayMs) {
22592
22696
  const mySession = session;
22697
+ const checkInMs = shift ? shift.lapseAfterSeconds * 1e3 / 4 : Infinity;
22593
22698
  pollTimer = setTimeout(() => {
22594
22699
  void poll(mySession);
22595
- }, delayMs);
22700
+ }, Math.min(delayMs, checkInMs));
22596
22701
  }
22597
22702
  async function poll(mySession) {
22598
22703
  if (stopped || mySession !== session)
22599
22704
  return;
22600
22705
  let nextPollMs = pollIntervalMs;
22601
- if (inflight >= maxConcurrency) {
22602
- schedulePoll(pollIntervalMs);
22603
- return;
22604
- }
22605
22706
  try {
22606
22707
  const paused = holds.size > 0;
22607
22708
  const free = maxConcurrency - inflight;
22608
- const { data, directives } = await opts.jobsClient.listDispatchableJobs({
22709
+ if (shift === null && shiftsSupported)
22710
+ await startShift();
22711
+ const { data, directives, shift: standing } = await opts.jobsClient.listDispatchableJobs({
22609
22712
  pools: resolvedPoolIds,
22610
- limit: paused ? 1 : accepts ? Math.max(free, DECLINE_WINDOW) : free,
22611
- sort: "priority:desc,queued_at:asc"
22713
+ limit: paused || free <= 0 ? 1 : accepts ? Math.max(free, DECLINE_WINDOW) : free,
22714
+ sort: "priority:desc,queued_at:asc",
22715
+ ...shift ? { shift: shift.id } : {}
22612
22716
  });
22613
22717
  noteReachable();
22718
+ if (shift && standing?.id === shift.id && standing.endedAt !== null)
22719
+ await shiftReleased(standing);
22614
22720
  if (directives && Object.keys(directives).length > 0 && opts.onDirectives) {
22615
22721
  try {
22616
22722
  await opts.onDirectives(directives);
@@ -22658,8 +22764,9 @@ var require_subscribe = __commonJS({
22658
22764
  }
22659
22765
  async function runOnce(available) {
22660
22766
  let dispatch;
22767
+ const shiftId = shift?.id ?? null;
22661
22768
  try {
22662
- dispatch = await opts.jobsClient.acceptDispatch(available.id);
22769
+ dispatch = await opts.jobsClient.acceptDispatch(available.id, shiftId ? { shiftId } : void 0);
22663
22770
  } catch (err) {
22664
22771
  if (isFatal(err)) {
22665
22772
  void fail(err);
@@ -22676,8 +22783,13 @@ var require_subscribe = __commonJS({
22676
22783
  });
22677
22784
  return;
22678
22785
  }
22786
+ logger.info(`Claimed job ${dispatch.job.id} (attempt ${dispatch.job.attempt})`, {
22787
+ jobId: dispatch.job.id,
22788
+ pool: dispatch.pool.name,
22789
+ ...shiftId ? { shiftId } : {}
22790
+ });
22679
22791
  const controller = new AbortController();
22680
- controllers.add(controller);
22792
+ controllers.set(controller, { shiftId });
22681
22793
  if (fatalError !== null)
22682
22794
  controller.abort(fatalError.error);
22683
22795
  let quietTimer = null;
@@ -22717,9 +22829,20 @@ var require_subscribe = __commonJS({
22717
22829
  controllers.delete(controller);
22718
22830
  }
22719
22831
  if (fatalError !== null) {
22720
- logger.debug?.("not submitting a result after a fatal error", { jobId: dispatch.job.id });
22832
+ logger.error(shiftId ? `Dropping job ${dispatch.job.id}'s result after a fatal error. Its claim goes back to the queue when this Sifter's shift lapses.` : `Dropping job ${dispatch.job.id}'s result after a fatal error. Its claim is held until the job's deadline.`, { jobId: dispatch.job.id, status: result.status });
22721
22833
  return;
22722
22834
  }
22835
+ if (controller.signal.reason === RELEASED) {
22836
+ logger.warn(`Job ${dispatch.job.id} was handed back to the queue while it ran; its result is not submitted.`, {
22837
+ jobId: dispatch.job.id
22838
+ });
22839
+ return;
22840
+ }
22841
+ const handBack = controller.signal.reason instanceof HandBack ? controller.signal.reason.reason : null;
22842
+ if (handBack !== null && result.status !== "completed") {
22843
+ result = { status: "abandoned", reason: handBack };
22844
+ logger.warn(`Handing job ${dispatch.job.id} back to the queue: ${handBack}`, { jobId: dispatch.job.id });
22845
+ }
22723
22846
  try {
22724
22847
  for (const output of result.outputs ?? []) {
22725
22848
  await opts.jobsClient.createOutput(dispatch.job.id, {
@@ -22741,7 +22864,12 @@ var require_subscribe = __commonJS({
22741
22864
  jobId: dispatch.job.id,
22742
22865
  error: err instanceof Error ? err.message : String(err)
22743
22866
  });
22867
+ return;
22744
22868
  }
22869
+ logger.info(`Submitted job ${dispatch.job.id} as ${result.status}`, {
22870
+ jobId: dispatch.job.id,
22871
+ ...result.reason !== void 0 ? { reason: result.reason } : {}
22872
+ });
22745
22873
  }
22746
22874
  return {
22747
22875
  start,
@@ -26382,9 +26510,12 @@ var require_logger = __commonJS({
26382
26510
  "../../packages/shuttle/dist/logging/logger.js"(exports2) {
26383
26511
  "use strict";
26384
26512
  Object.defineProperty(exports2, "__esModule", { value: true });
26513
+ exports2.LOG_FILE_MAX_BYTES = void 0;
26385
26514
  exports2.createLogger = createLogger;
26386
26515
  exports2.setGlobalLogger = setGlobalLogger;
26387
26516
  exports2.getLogger = getLogger;
26517
+ var node_fs_1 = require("node:fs");
26518
+ var node_path_1 = require("node:path");
26388
26519
  var LEVEL_ORDER = {
26389
26520
  debug: 0,
26390
26521
  info: 1,
@@ -26412,13 +26543,42 @@ var require_logger = __commonJS({
26412
26543
  };
26413
26544
  return JSON.stringify(entry);
26414
26545
  }
26546
+ exports2.LOG_FILE_MAX_BYTES = 5 * 1024 * 1024;
26547
+ function fileSink(path) {
26548
+ let size = 0;
26549
+ let broken = false;
26550
+ try {
26551
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
26552
+ size = (0, node_fs_1.statSync)(path).size;
26553
+ } catch {
26554
+ size = 0;
26555
+ }
26556
+ return (line) => {
26557
+ if (broken)
26558
+ return;
26559
+ try {
26560
+ if (size > exports2.LOG_FILE_MAX_BYTES) {
26561
+ (0, node_fs_1.renameSync)(path, `${path}.1`);
26562
+ size = 0;
26563
+ }
26564
+ const entry = `${line}
26565
+ `;
26566
+ (0, node_fs_1.appendFileSync)(path, entry, { mode: 384 });
26567
+ size += Buffer.byteLength(entry);
26568
+ } catch {
26569
+ broken = true;
26570
+ }
26571
+ };
26572
+ }
26415
26573
  function createLogger(options) {
26416
26574
  const threshold = LEVEL_ORDER[options.level];
26417
26575
  const format = options.format === "json" ? formatJson : formatPretty;
26576
+ const toFile = options.file ? fileSink(options.file) : null;
26418
26577
  function emit(level, message, context) {
26419
26578
  if (LEVEL_ORDER[level] < threshold)
26420
26579
  return;
26421
26580
  const line = format(level, message, context);
26581
+ toFile?.(line);
26422
26582
  if (level === "error") {
26423
26583
  console.error(line);
26424
26584
  } else if (level === "warn") {
@@ -27714,6 +27874,7 @@ var require_shuttle = __commonJS({
27714
27874
  exports2.mergeExecutorDefaults = mergeExecutorDefaults;
27715
27875
  exports2.resolveAuth = resolveAuth;
27716
27876
  var os_1 = require("os");
27877
+ var path_1 = require("path");
27717
27878
  var jobs_1 = require_dist4();
27718
27879
  var self_update_1 = require_self_update();
27719
27880
  var decommission_1 = require_decommission();
@@ -27854,13 +28015,18 @@ var require_shuttle = __commonJS({
27854
28015
  return this.spendTracker;
27855
28016
  }
27856
28017
  async start() {
28018
+ const paths = (0, apply_1.resolvePaths)(this.brand, this.profile);
28019
+ const logFile = this.config.credentials === void 0 ? (0, path_1.join)(paths.configDir, `${this.brand.product.id}.log`) : void 0;
27857
28020
  (0, logger_1.setGlobalLogger)((0, logger_1.createLogger)({
27858
28021
  level: this.config.logging.level,
27859
- format: this.config.logging.format
28022
+ format: this.config.logging.format,
28023
+ ...logFile ? { file: logFile } : {}
27860
28024
  }));
27861
28025
  const logger = (0, logger_1.getLogger)();
27862
- logger.info(`${this.brand.product.title} starting...`);
27863
- const paths = (0, apply_1.resolvePaths)(this.brand, this.profile);
28026
+ logger.info(`${this.brand.product.title} starting...`, {
28027
+ version: this.brand.product.version,
28028
+ ...logFile ? { log: logFile } : {}
28029
+ });
27864
28030
  this.broker = (0, credentials_1.createCredentialBroker)(this.config.credentials, this.credentialDeps);
27865
28031
  const subscribeLogger = {
27866
28032
  debug: (msg, meta) => logger.debug(msg, meta),
@@ -27998,7 +28164,7 @@ var require_shuttle = __commonJS({
27998
28164
  const runOn = async (executor) => {
27999
28165
  await observerChain.notify({ kind: "executor.dispatch.started", jobId, attendanceId, executor: executor.capability.id, strategy }, onObserverError);
28000
28166
  const dispatchStartedAt = Date.now();
28001
- const outcome = await (0, run_dispatch_1.runDispatch)(dispatch, executor, {
28167
+ const ran = await (0, run_dispatch_1.runDispatch)(dispatch, executor, {
28002
28168
  credentials: this.broker,
28003
28169
  signal,
28004
28170
  reportProgress: (channel, frames) => {
@@ -28006,6 +28172,8 @@ var require_shuttle = __commonJS({
28006
28172
  });
28007
28173
  }
28008
28174
  });
28175
+ subscribeLogger.info(`Job ${jobId} ran on ${executor.capability.id} for ${formatDuration(Date.now() - dispatchStartedAt)}: ` + (ran.kind === "completed" ? "finished" : ran.kind === "gate-failed" ? "its output failed a gate" : "the executor failed"), { jobId, ...signal.aborted ? { aborted: true } : {} });
28176
+ const outcome = ran;
28009
28177
  if (outcome.kind === "executor-failed") {
28010
28178
  const err = outcome.error;
28011
28179
  const reason = `executor failed: ${err instanceof Error ? err.message : String(err)}`;
@@ -28165,14 +28333,21 @@ var require_shuttle = __commonJS({
28165
28333
  logger.info(`${this.brand.product.title} stopping...`);
28166
28334
  this.stopping.abort();
28167
28335
  if (this.subscription) {
28168
- await this.subscription.stop();
28336
+ await this.subscription.stop({
28337
+ handBack: `The ${this.brand.product.title} was stopped before it finished this job`
28338
+ });
28169
28339
  }
28170
28340
  logger.info(`${this.brand.product.title} stopped`);
28171
28341
  this.lifetime.resolve();
28172
28342
  }
28173
28343
  /** Forward the current subscribe session's fatal, if one ends it, to the run's outcome. */
28174
28344
  watchSession() {
28175
- this.subscription?.done().catch((err) => this.lifetime.reject(err));
28345
+ this.subscription?.done().catch((err) => {
28346
+ (0, logger_1.getLogger)().error(`${this.brand.product.title} stopped on a fatal error`, {
28347
+ error: err instanceof Error ? `${err.name}: ${err.message}` : String(err)
28348
+ });
28349
+ this.lifetime.reject(err);
28350
+ });
28176
28351
  }
28177
28352
  /**
28178
28353
  * Note that an executor cannot run jobs on this machine, and keep checking
@@ -28336,29 +28511,49 @@ var require_shutdown = __commonJS({
28336
28511
  "../../packages/shuttle/dist/lifecycle/shutdown.js"(exports2) {
28337
28512
  "use strict";
28338
28513
  Object.defineProperty(exports2, "__esModule", { value: true });
28514
+ exports2.SHUTDOWN_DEADLINE_MS = void 0;
28339
28515
  exports2.setupShutdownHandler = setupShutdownHandler;
28516
+ exports2.createShutdownHandler = createShutdownHandler;
28340
28517
  var logger_1 = require_logger();
28518
+ exports2.SHUTDOWN_DEADLINE_MS = 2e4;
28341
28519
  function setupShutdownHandler(onShutdown) {
28520
+ const handler = createShutdownHandler(onShutdown);
28521
+ process.on("SIGINT", () => handler("SIGINT"));
28522
+ process.on("SIGTERM", () => handler("SIGTERM"));
28523
+ process.on("SIGHUP", () => handler("SIGHUP"));
28524
+ }
28525
+ function createShutdownHandler(onShutdown, options = {}) {
28526
+ const deadlineMs = options.deadlineMs ?? exports2.SHUTDOWN_DEADLINE_MS;
28527
+ const exit = options.exit ?? ((code) => process.exit(code));
28342
28528
  let shuttingDown = false;
28343
28529
  const handler = (signal) => {
28344
28530
  const logger = (0, logger_1.getLogger)();
28345
28531
  if (shuttingDown) {
28346
- logger.warn("Forced shutdown", { signal });
28347
- process.exit(1);
28532
+ logger.warn("Forced shutdown: exiting now. Jobs releases any claim still held once this shift lapses.", {
28533
+ signal
28534
+ });
28535
+ exit(1);
28536
+ return;
28348
28537
  }
28349
28538
  shuttingDown = true;
28350
- logger.info("Shutting down...", { signal });
28539
+ logger.info("Shutting down: handing any job in progress back to the queue...", { signal });
28540
+ const deadline = setTimeout(() => {
28541
+ logger.warn(`Shutdown did not finish within ${Math.round(deadlineMs / 1e3)}s; exiting. Jobs releases any claim still held once this shift lapses.`);
28542
+ exit(1);
28543
+ }, deadlineMs);
28544
+ deadline.unref?.();
28351
28545
  onShutdown().then(() => {
28352
- process.exit(0);
28546
+ clearTimeout(deadline);
28547
+ exit(0);
28353
28548
  }).catch((err) => {
28549
+ clearTimeout(deadline);
28354
28550
  logger.error("Shutdown error", {
28355
28551
  error: err instanceof Error ? err.message : String(err)
28356
28552
  });
28357
- process.exit(1);
28553
+ exit(1);
28358
28554
  });
28359
28555
  };
28360
- process.on("SIGINT", () => handler("SIGINT"));
28361
- process.on("SIGTERM", () => handler("SIGTERM"));
28556
+ return handler;
28362
28557
  }
28363
28558
  }
28364
28559
  });
@@ -29192,7 +29387,7 @@ var import_path = require("path");
29192
29387
  var import_promises = require("fs/promises");
29193
29388
  var import_yaml = __toESM(require_dist3());
29194
29389
  var import_shuttle = __toESM(require_dist5());
29195
- var buildVersion = true ? "0.36.0" : pkg.version;
29390
+ var buildVersion = true ? "0.37.0" : pkg.version;
29196
29391
  var AGENT_EXECUTORS = ["claude-code", "codex", "copilot"];
29197
29392
  var sifterBrand = {
29198
29393
  product: {