@whittlelabs/sifter 0.36.0 → 0.37.1

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