hobots 1.9.1 → 1.9.2

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.
package/dist/index.d.cts CHANGED
@@ -146,6 +146,13 @@ interface TasksOptions {
146
146
  * uma run sem reporte por ~60s. Default: 5_000 (mínimo 1_000, máximo 15_000)
147
147
  */
148
148
  cancelCheckIntervalMs?: number;
149
+ /**
150
+ * Tempo de graça após ctx.signal disparar (run cancelada no app) antes do
151
+ * agente desistir de esperar o handler e liberar o slot de concorrência. O
152
+ * handler não é morto — se ele nunca checar ctx.signal, pode continuar
153
+ * rodando em segundo plano; só o agente para de esperar por ele. Default: 10_000
154
+ */
155
+ cancelGraceMs?: number;
149
156
  /**
150
157
  * Instala handlers de SIGINT/SIGTERM/uncaughtException que reportam as runs
151
158
  * ativas como falha (AgentTerminated) antes do processo morrer. Nunca interfere
package/dist/index.d.ts CHANGED
@@ -146,6 +146,13 @@ interface TasksOptions {
146
146
  * uma run sem reporte por ~60s. Default: 5_000 (mínimo 1_000, máximo 15_000)
147
147
  */
148
148
  cancelCheckIntervalMs?: number;
149
+ /**
150
+ * Tempo de graça após ctx.signal disparar (run cancelada no app) antes do
151
+ * agente desistir de esperar o handler e liberar o slot de concorrência. O
152
+ * handler não é morto — se ele nunca checar ctx.signal, pode continuar
153
+ * rodando em segundo plano; só o agente para de esperar por ele. Default: 10_000
154
+ */
155
+ cancelGraceMs?: number;
149
156
  /**
150
157
  * Instala handlers de SIGINT/SIGTERM/uncaughtException que reportam as runs
151
158
  * ativas como falha (AgentTerminated) antes do processo morrer. Nunca interfere
package/dist/index.js CHANGED
@@ -103,7 +103,7 @@ function scrubSecrets(text) {
103
103
 
104
104
  // src/utils.ts
105
105
  var SDK_NAME = "hobots";
106
- var SDK_VERSION = true ? "1.9.0" : "0.0.0-dev";
106
+ var SDK_VERSION = true ? "1.9.1" : "0.0.0-dev";
107
107
  function isNode() {
108
108
  return typeof process !== "undefined" && typeof process.versions?.node === "string";
109
109
  }
@@ -1501,6 +1501,7 @@ var MIN_POLL_INTERVAL_MS = 1e3;
1501
1501
  var MAX_CONCURRENCY = 5;
1502
1502
  var POLL_RETRY_DELAYS_MS = [1e3, 4e3, 16e3];
1503
1503
  var AUTH_FAILURE_INTERVAL_MS = 3e4;
1504
+ var DEFAULT_CANCEL_GRACE_MS = 1e4;
1504
1505
  function hostname() {
1505
1506
  if (!isNode()) return void 0;
1506
1507
  return process.env.HOSTNAME || process.env.COMPUTERNAME || void 0;
@@ -1519,6 +1520,7 @@ var TaskAgent = class {
1519
1520
  this.taskTimeoutMs = options.taskTimeoutMs;
1520
1521
  this.logFlushIntervalMs = options.logFlushIntervalMs;
1521
1522
  this.cancelCheckIntervalMs = options.cancelCheckIntervalMs;
1523
+ this.cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;
1522
1524
  this.logger.debug("agente configurado", { baseUrl: config.tasksBaseUrl });
1523
1525
  }
1524
1526
  logger;
@@ -1530,6 +1532,7 @@ var TaskAgent = class {
1530
1532
  taskTimeoutMs;
1531
1533
  logFlushIntervalMs;
1532
1534
  cancelCheckIntervalMs;
1535
+ cancelGraceMs;
1533
1536
  state = "idle";
1534
1537
  timer;
1535
1538
  pollAbort;
@@ -1714,14 +1717,26 @@ var TaskAgent = class {
1714
1717
  }
1715
1718
  async invokeWithTimeout(handler, run, reporter) {
1716
1719
  const invocation = Promise.resolve().then(() => handler(run.params, reporter.context));
1717
- if (!this.taskTimeoutMs || this.taskTimeoutMs <= 0) {
1720
+ const races = [];
1721
+ if (this.taskTimeoutMs && this.taskTimeoutMs > 0) {
1722
+ races.push(sleep(this.taskTimeoutMs).then(() => TIMEOUT_SENTINEL));
1723
+ }
1724
+ if (this.cancelGraceMs > 0) {
1725
+ races.push(waitForCancelGrace(reporter.context.signal, this.cancelGraceMs).then(() => CANCEL_SENTINEL));
1726
+ }
1727
+ if (races.length === 0) {
1718
1728
  await invocation;
1719
1729
  return;
1720
1730
  }
1731
+ const outcome = await Promise.race([invocation, ...races]);
1732
+ if (outcome !== TIMEOUT_SENTINEL && outcome !== CANCEL_SENTINEL) return;
1733
+ if (outcome === CANCEL_SENTINEL) {
1734
+ this.logger.warn(
1735
+ `run ${run.id} cancelada \u2014 handler n\xE3o respondeu a ctx.signal em ${this.cancelGraceMs}ms, liberando o slot (o handler pode continuar rodando em segundo plano)`
1736
+ );
1737
+ return;
1738
+ }
1721
1739
  const timeoutMs = this.taskTimeoutMs;
1722
- const timeout = sleep(timeoutMs).then(() => TIMEOUT_SENTINEL);
1723
- const outcome = await Promise.race([invocation, timeout]);
1724
- if (outcome !== TIMEOUT_SENTINEL) return;
1725
1740
  reporter.abort();
1726
1741
  invocation.then(() => this.logger.warn(`run ${run.id} terminou depois do timeout de ${timeoutMs}ms`)).catch(() => this.logger.warn(`run ${run.id} falhou depois do timeout de ${timeoutMs}ms (erro descartado)`));
1727
1742
  throw Object.assign(new Error(`timeout ap\xF3s ${timeoutMs}ms (o handler pode continuar em background)`), {
@@ -1730,6 +1745,16 @@ var TaskAgent = class {
1730
1745
  }
1731
1746
  };
1732
1747
  var TIMEOUT_SENTINEL = /* @__PURE__ */ Symbol("task-timeout");
1748
+ var CANCEL_SENTINEL = /* @__PURE__ */ Symbol("task-canceled");
1749
+ function waitForCancelGrace(signal, graceMs) {
1750
+ return new Promise((resolve) => {
1751
+ if (signal.aborted) {
1752
+ setTimeout(resolve, graceMs);
1753
+ return;
1754
+ }
1755
+ signal.addEventListener("abort", () => setTimeout(resolve, graceMs), { once: true });
1756
+ });
1757
+ }
1733
1758
  var MAX_ERROR_MESSAGE_CHARS = 1e4;
1734
1759
  var MAX_ERROR_STACK_CHARS = 2e4;
1735
1760
  function toRunError(error) {