@automagik/omni 2.260906.2 → 2.260906.4

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.js CHANGED
@@ -12345,6 +12345,19 @@ var init_types3 = __esm(() => {
12345
12345
  "flow.data_exchange"
12346
12346
  ];
12347
12347
  });
12348
+ // ../core/src/events/causality.ts
12349
+ import { AsyncLocalStorage } from "async_hooks";
12350
+ function runWithEventCausality(context, fn) {
12351
+ return storage.run(context, fn);
12352
+ }
12353
+ function currentEventCausality() {
12354
+ return storage.getStore();
12355
+ }
12356
+ var storage;
12357
+ var init_causality = __esm(() => {
12358
+ storage = new AsyncLocalStorage;
12359
+ });
12360
+
12348
12361
  // ../core/src/events/envelope.ts
12349
12362
  function classifyEnvelope(metadata) {
12350
12363
  if (metadata === null || metadata === undefined)
@@ -30370,6 +30383,38 @@ var init_logger = __esm(() => {
30370
30383
  rootLogger = createLogger("system");
30371
30384
  });
30372
30385
 
30386
+ // ../core/src/events/factory.ts
30387
+ function createOmniEvent(type, payload, metadata, serviceName) {
30388
+ const eventId = crypto.randomUUID();
30389
+ const timestamp = Date.now();
30390
+ const tenantId = resolvePublishTenantId(metadata?.tenantId, metadata?.instanceId);
30391
+ const ambient = currentEventCausality();
30392
+ const causationId = metadata?.causationId ?? ambient?.causationId;
30393
+ return {
30394
+ id: eventId,
30395
+ type,
30396
+ payload,
30397
+ timestamp,
30398
+ metadata: {
30399
+ correlationId: metadata?.correlationId ?? ambient?.correlationId ?? eventId,
30400
+ ...causationId ? { causationId } : {},
30401
+ instanceId: metadata?.instanceId,
30402
+ channelType: metadata?.channelType,
30403
+ personId: metadata?.personId,
30404
+ platformIdentityId: metadata?.platformIdentityId,
30405
+ traceId: metadata?.traceId ?? eventId,
30406
+ source: metadata?.source ?? serviceName,
30407
+ ingestMode: metadata?.ingestMode,
30408
+ timings: metadata?.timings,
30409
+ ...tenantId ? { envelopeVersion: CURRENT_ENVELOPE_VERSION, tenantId } : {}
30410
+ }
30411
+ };
30412
+ }
30413
+ var init_factory = __esm(() => {
30414
+ init_causality();
30415
+ init_envelope();
30416
+ });
30417
+
30373
30418
  // ../core/src/events/nats/streams.ts
30374
30419
  function getStreamForEventType(eventType) {
30375
30420
  const prefix = eventType.split(".")[0] ?? "";
@@ -30739,7 +30784,24 @@ var init_registry = __esm(() => {
30739
30784
  component: exports_external.string(),
30740
30785
  reason: exports_external.string(),
30741
30786
  severity: exports_external.enum(["warning", "critical"])
30742
- }), { description: "System health degradation detected" })
30787
+ }), { description: "System health degradation detected" }),
30788
+ connectorStalled: createEventSchema("system.connector.stalled", exports_external.object({
30789
+ sourceId: exports_external.string(),
30790
+ sourceName: exports_external.string(),
30791
+ expectedIntervalSeconds: exports_external.number(),
30792
+ lastReceivedAt: exports_external.number().nullable(),
30793
+ lastHeartbeatAt: exports_external.number().nullable(),
30794
+ silentForSeconds: exports_external.number(),
30795
+ stalledAt: exports_external.number()
30796
+ }), { description: "A supervised connector went silent beyond its declared cadence" }),
30797
+ connectorRecovered: createEventSchema("system.connector.recovered", exports_external.object({
30798
+ sourceId: exports_external.string(),
30799
+ sourceName: exports_external.string(),
30800
+ expectedIntervalSeconds: exports_external.number(),
30801
+ stalledForSeconds: exports_external.number(),
30802
+ recoveredBy: exports_external.enum(["event", "heartbeat", "rearmed"]),
30803
+ recoveredAt: exports_external.number()
30804
+ }), { description: "A stalled connector signalled again (event, heartbeat, or re-declared cadence)" })
30743
30805
  };
30744
30806
  CustomEventSchemas = {
30745
30807
  chatUnreadUpdated: createEventSchema("custom.chat.unread-updated", exports_external.object({
@@ -31159,27 +31221,8 @@ class NatsEventBus {
31159
31221
  }
31160
31222
  async publishInternal(type, payload, metadata) {
31161
31223
  await this.ensureConnected();
31162
- const eventId = crypto.randomUUID();
31163
- const timestamp = Date.now();
31164
- const tenantId = resolvePublishTenantId(metadata?.tenantId, metadata?.instanceId);
31165
- const event = {
31166
- id: eventId,
31167
- type,
31168
- payload,
31169
- timestamp,
31170
- metadata: {
31171
- correlationId: metadata?.correlationId ?? eventId,
31172
- instanceId: metadata?.instanceId,
31173
- channelType: metadata?.channelType,
31174
- personId: metadata?.personId,
31175
- platformIdentityId: metadata?.platformIdentityId,
31176
- traceId: metadata?.traceId ?? eventId,
31177
- source: metadata?.source ?? this.config.serviceName,
31178
- ingestMode: metadata?.ingestMode,
31179
- timings: metadata?.timings,
31180
- ...tenantId ? { envelopeVersion: CURRENT_ENVELOPE_VERSION, tenantId } : {}
31181
- }
31182
- };
31224
+ const event = createOmniEvent(type, payload, metadata, this.config.serviceName);
31225
+ const eventId = event.id;
31183
31226
  let subject;
31184
31227
  if (metadata?.channelType && metadata?.instanceId) {
31185
31228
  subject = buildSubject(type, metadata.channelType, metadata.instanceId);
@@ -31455,7 +31498,7 @@ async function connectEventBus(config2) {
31455
31498
  var import_api2, import_nats, log4, DEFAULTS, ENSURE_CONNECTED_WAIT_MS = 5000;
31456
31499
  var init_client = __esm(() => {
31457
31500
  init_logger();
31458
- init_envelope();
31501
+ init_factory();
31459
31502
  init_consumer();
31460
31503
  init_registry();
31461
31504
  init_streams();
@@ -39448,6 +39491,7 @@ function estimateCompletion(progress) {
39448
39491
  // ../core/src/events/index.ts
39449
39492
  var init_events = __esm(() => {
39450
39493
  init_types3();
39494
+ init_causality();
39451
39495
  init_envelope();
39452
39496
  init_nats();
39453
39497
  init_dead_letter();
@@ -45127,13 +45171,7 @@ function resolveFollowUpValue(trimmed, followUp) {
45127
45171
  return;
45128
45172
  }
45129
45173
  }
45130
- function resolveTemplatePath(path, context) {
45131
- const trimmed = path.trim();
45132
- if (!trimmed)
45133
- return;
45134
- const parts = trimmed.split(".");
45135
- const root = parts[0] ?? "";
45136
- const rest = parts.slice(1).join(".");
45174
+ function resolveSpecialValue(trimmed, root, rest, context) {
45137
45175
  if (context.debounce) {
45138
45176
  const debounceValue = resolveDebounceValue(trimmed, rest, context.debounce);
45139
45177
  if (debounceValue !== undefined)
@@ -45144,6 +45182,21 @@ function resolveTemplatePath(path, context) {
45144
45182
  if (followUpValue !== undefined)
45145
45183
  return followUpValue;
45146
45184
  }
45185
+ if (root === "event" && context.event) {
45186
+ return rest ? getNestedValue(context.event, rest) : context.event;
45187
+ }
45188
+ return;
45189
+ }
45190
+ function resolveTemplatePath(path, context) {
45191
+ const trimmed = path.trim();
45192
+ if (!trimmed)
45193
+ return;
45194
+ const parts = trimmed.split(".");
45195
+ const root = parts[0] ?? "";
45196
+ const rest = parts.slice(1).join(".");
45197
+ const specialValue = resolveSpecialValue(trimmed, root, rest, context);
45198
+ if (specialValue !== undefined)
45199
+ return specialValue;
45147
45200
  if (root === "payload")
45148
45201
  return rest ? getNestedValue(context.payload, rest) : context.payload;
45149
45202
  if (root === "env")
@@ -45225,7 +45278,9 @@ function createTemplateContext(payload, options = {}) {
45225
45278
  variables: options.variables ?? {},
45226
45279
  env: process.env,
45227
45280
  debounce: options.debounce,
45228
- followUp
45281
+ followUp,
45282
+ event: options.event,
45283
+ automation: options.automation
45229
45284
  };
45230
45285
  }
45231
45286
  var TEMPLATE_REGEX;
@@ -45797,8 +45852,12 @@ var init_egress = __esm(() => {
45797
45852
  });
45798
45853
 
45799
45854
  // ../core/src/automations/actions.ts
45800
- function buildWebhookHeaders(config2, context) {
45855
+ function buildWebhookHeaders(config2, context, actionIndex) {
45801
45856
  const headers = { "Content-Type": "application/json" };
45857
+ if (context.event) {
45858
+ headers["X-Omni-Event-Id"] = context.event.id;
45859
+ headers["X-Omni-Delivery-Id"] = `${context.event.id}:${context.automation?.id ?? "manual"}:${actionIndex}`;
45860
+ }
45802
45861
  if (config2.headers) {
45803
45862
  for (const [key, value] of Object.entries(config2.headers)) {
45804
45863
  headers[key] = substituteTemplate(value, context);
@@ -45806,16 +45865,28 @@ function buildWebhookHeaders(config2, context) {
45806
45865
  }
45807
45866
  return headers;
45808
45867
  }
45868
+ function buildDefaultWebhookBody(config2, context) {
45869
+ if (context.event && config2.includeEnvelope !== false) {
45870
+ return JSON.stringify({
45871
+ id: context.event.id,
45872
+ type: context.event.type,
45873
+ payload: context.payload,
45874
+ metadata: context.event.metadata,
45875
+ timestamp: context.event.timestamp
45876
+ });
45877
+ }
45878
+ return JSON.stringify(context.payload);
45879
+ }
45809
45880
  async function parseWebhookResponse(response) {
45810
45881
  const contentType = response.headers.get("content-type") ?? "";
45811
45882
  return contentType.includes("application/json") ? response.json() : response.text();
45812
45883
  }
45813
- async function executeWebhookAction(config2, context, _deps, trustedTenantId) {
45884
+ async function executeWebhookAction(config2, context, _deps, trustedTenantId, actionIndex = 0) {
45814
45885
  try {
45815
45886
  const url = substituteTemplate(config2.url, context);
45816
45887
  const method = config2.method ?? "POST";
45817
- const headers = buildWebhookHeaders(config2, context);
45818
- const body = config2.bodyTemplate ? substituteTemplate(config2.bodyTemplate, context) : JSON.stringify(context.payload);
45888
+ const headers = buildWebhookHeaders(config2, context, actionIndex);
45889
+ const body = config2.bodyTemplate ? substituteTemplate(config2.bodyTemplate, context) : buildDefaultWebhookBody(config2, context);
45819
45890
  logger2.debug(`Webhook ${method} ${url}`, { method, url, waitForResponse: config2.waitForResponse });
45820
45891
  const response = await brokeredFetch(url, {
45821
45892
  method,
@@ -45905,8 +45976,10 @@ async function executeEmitEventAction(config2, context, deps, trustedTenantId) {
45905
45976
  }
45906
45977
  }
45907
45978
  logger2.debug("Emitting event", { eventType });
45979
+ const correlationId = context.event?.metadata.correlationId ?? context.payload.correlationId ?? undefined;
45908
45980
  const result = await deps.eventBus.publishGeneric(eventType, payload, {
45909
- correlationId: context.payload.correlationId ?? undefined,
45981
+ correlationId,
45982
+ causationId: context.event?.id,
45910
45983
  source: "automation",
45911
45984
  ...trustedTenantId ? { tenantId: trustedTenantId } : {}
45912
45985
  });
@@ -45999,7 +46072,12 @@ function extractAgentCallContext(config2, context) {
45999
46072
  threadId,
46000
46073
  senderId,
46001
46074
  senderName,
46002
- messages: messagesResult
46075
+ messages: messagesResult,
46076
+ event: context.event ? {
46077
+ id: context.event.id,
46078
+ type: context.event.type,
46079
+ correlationId: context.event.metadata.correlationId
46080
+ } : undefined
46003
46081
  }
46004
46082
  };
46005
46083
  }
@@ -46040,12 +46118,12 @@ async function executeCallAgentAction(config2, context, deps, trustedTenantId) {
46040
46118
  return { success: false, error: errorMessage };
46041
46119
  }
46042
46120
  }
46043
- async function executeAction(action, context, deps, trustedTenantId) {
46121
+ async function executeAction(action, context, deps, trustedTenantId, actionIndex = 0) {
46044
46122
  const start = Date.now();
46045
46123
  let result;
46046
46124
  switch (action.type) {
46047
46125
  case "webhook":
46048
- result = await executeWebhookAction(action.config, context, deps, trustedTenantId);
46126
+ result = await executeWebhookAction(action.config, context, deps, trustedTenantId, actionIndex);
46049
46127
  break;
46050
46128
  case "send_message":
46051
46129
  result = await executeSendMessageAction(action.config, context, deps, trustedTenantId);
@@ -46074,12 +46152,12 @@ async function executeAction(action, context, deps, trustedTenantId) {
46074
46152
  async function executeActions(actions, context, deps, trustedTenantId) {
46075
46153
  const results = [];
46076
46154
  const variables = { ...context.variables };
46077
- for (const action of actions) {
46155
+ for (const [actionIndex, action] of actions.entries()) {
46078
46156
  const actionContext = {
46079
46157
  ...context,
46080
46158
  variables
46081
46159
  };
46082
- const result = await executeAction(action, actionContext, deps, trustedTenantId);
46160
+ const result = await executeAction(action, actionContext, deps, trustedTenantId, actionIndex);
46083
46161
  results.push(result);
46084
46162
  if (action.type === "webhook" && action.config.responseAs && result.status === "success" && result.result) {
46085
46163
  variables[action.config.responseAs] = result.result;
@@ -46509,13 +46587,18 @@ class AutomationEngine {
46509
46587
  type: content?.type ?? "unknown",
46510
46588
  text: content?.text,
46511
46589
  timestamp: event.timestamp,
46512
- payload
46590
+ payload,
46591
+ eventId: event.id,
46592
+ correlationId: event.metadata.correlationId
46513
46593
  };
46514
46594
  manager.addMessage(key, message2, from, instanceId, stamp);
46515
46595
  }
46516
46596
  async handleImmediate(automation, event) {
46517
46597
  const instanceId = event.metadata.instanceId ?? "global";
46518
- const context = createTemplateContext(event.payload);
46598
+ const context = createTemplateContext(event.payload, {
46599
+ event: { id: event.id, type: event.type, timestamp: event.timestamp, metadata: event.metadata },
46600
+ automation: { id: automation.id }
46601
+ });
46519
46602
  await this.queueExecution(automation, event, context, instanceId);
46520
46603
  }
46521
46604
  setupDebounceManager(automation) {
@@ -46525,28 +46608,35 @@ class AutomationEngine {
46525
46608
  logger4.warn("Debounce callback fired with empty messages array", { key });
46526
46609
  return;
46527
46610
  }
46528
- const context = createTemplateContext(lastMessage.payload, {
46529
- debounce: {
46530
- messages: messages2.map((m2) => ({
46531
- type: m2.type,
46532
- text: m2.text,
46533
- timestamp: m2.timestamp
46534
- })),
46535
- from,
46536
- instanceId
46537
- }
46538
- });
46539
46611
  const syntheticEvent = {
46540
46612
  id: generateId(),
46541
46613
  type: automation.triggerEventType,
46542
46614
  payload: lastMessage.payload,
46543
46615
  metadata: {
46544
- correlationId: generateId(),
46616
+ correlationId: lastMessage.correlationId ?? generateId(),
46545
46617
  instanceId,
46546
46618
  ...stamp ? { envelopeVersion: stamp.envelopeVersion, tenantId: stamp.tenantId } : {}
46547
46619
  },
46548
46620
  timestamp: Date.now()
46549
46621
  };
46622
+ const context = createTemplateContext(lastMessage.payload, {
46623
+ debounce: {
46624
+ messages: messages2.map((m2) => ({
46625
+ type: m2.type,
46626
+ text: m2.text,
46627
+ timestamp: m2.timestamp
46628
+ })),
46629
+ from,
46630
+ instanceId
46631
+ },
46632
+ event: {
46633
+ id: lastMessage.eventId ?? syntheticEvent.id,
46634
+ type: syntheticEvent.type,
46635
+ timestamp: syntheticEvent.timestamp,
46636
+ metadata: syntheticEvent.metadata
46637
+ },
46638
+ automation: { id: automation.id }
46639
+ });
46550
46640
  await this.queueExecution(automation, syntheticEvent, context, instanceId);
46551
46641
  };
46552
46642
  const manager = new DebounceManager(automation.debounce, callback);
@@ -46634,7 +46724,10 @@ class AutomationEngine {
46634
46724
  await this.logExecution(result2, trustedTenantId);
46635
46725
  return result2;
46636
46726
  }
46637
- const actionsExecuted = await executeActions(automation.actions, context, this.deps, trustedTenantId);
46727
+ const actionsExecuted = await runWithEventCausality({
46728
+ correlationId: context.event?.metadata.correlationId ?? event.metadata.correlationId,
46729
+ causationId: context.event?.id ?? event.id
46730
+ }, () => executeActions(automation.actions, context, this.deps, trustedTenantId));
46638
46731
  const allSucceeded = actionsExecuted.every((a) => a.status === "success");
46639
46732
  const status = allSucceeded ? "success" : "failed";
46640
46733
  const result = {
@@ -46730,6 +46823,7 @@ function createAutomationEngine(config2) {
46730
46823
  }
46731
46824
  var logger4, QueueFullError;
46732
46825
  var init_engine = __esm(() => {
46826
+ init_causality();
46733
46827
  init_envelope();
46734
46828
  init_ids();
46735
46829
  init_logger();
@@ -47118,6 +47212,113 @@ var init_automations = __esm(() => {
47118
47212
  init_sweeper();
47119
47213
  });
47120
47214
 
47215
+ // ../core/src/connectors/liveness.ts
47216
+ function latestConnectorSignal(row) {
47217
+ let best = { at: row.createdAt, kind: "rearmed" };
47218
+ const candidates = [
47219
+ { at: row.livenessArmedAt, kind: "rearmed" },
47220
+ { at: row.lastReceivedAt, kind: "event" },
47221
+ { at: row.lastHeartbeatAt, kind: "heartbeat" }
47222
+ ];
47223
+ for (const candidate of candidates) {
47224
+ if (candidate.at && candidate.at.getTime() >= best.at.getTime()) {
47225
+ best = { at: candidate.at, kind: candidate.kind };
47226
+ }
47227
+ }
47228
+ return best;
47229
+ }
47230
+ async function sweepConnectorLiveness(deps) {
47231
+ const clock = deps.now ?? (() => new Date);
47232
+ const now = clock();
47233
+ const rows = await deps.repo.findSupervised();
47234
+ const stats = { scanned: rows.length, stalled: 0, recovered: 0, errors: 0 };
47235
+ for (const row of rows) {
47236
+ try {
47237
+ await processRow2(row, now, deps, stats);
47238
+ } catch (err2) {
47239
+ stats.errors += 1;
47240
+ deps.logger.error("connector liveness: failed to process source", {
47241
+ sourceId: row.id,
47242
+ sourceName: row.name,
47243
+ error: err2 instanceof Error ? err2.message : String(err2)
47244
+ });
47245
+ }
47246
+ }
47247
+ if (stats.stalled > 0 || stats.recovered > 0) {
47248
+ deps.logger.info("connector liveness sweep tick", { ...stats });
47249
+ }
47250
+ return stats;
47251
+ }
47252
+ async function processRow2(row, now, deps, stats) {
47253
+ const signal = latestConnectorSignal(row);
47254
+ const silentMs = Math.max(0, now.getTime() - signal.at.getTime());
47255
+ const overdue = silentMs > row.expectedIntervalSeconds * 1000;
47256
+ const silentForSeconds = Math.round(silentMs / 1000);
47257
+ if (overdue) {
47258
+ if (row.livenessStatus === "stalled")
47259
+ return;
47260
+ if (!await deps.repo.markStalled(row.id, now))
47261
+ return;
47262
+ const payload = {
47263
+ sourceId: row.id,
47264
+ sourceName: row.name,
47265
+ expectedIntervalSeconds: row.expectedIntervalSeconds,
47266
+ lastReceivedAt: row.lastReceivedAt?.getTime() ?? null,
47267
+ lastHeartbeatAt: row.lastHeartbeatAt?.getTime() ?? null,
47268
+ silentForSeconds,
47269
+ stalledAt: now.getTime()
47270
+ };
47271
+ const eventId = await publishTransition(deps, "system.connector.stalled", { ...payload }, row);
47272
+ stats.stalled += 1;
47273
+ await runHook(deps.logger, "onStalled", row, () => deps.onStalled?.(row, payload, eventId));
47274
+ return;
47275
+ }
47276
+ if (row.livenessStatus === "stalled") {
47277
+ if (!await deps.repo.markRecovered(row.id, now))
47278
+ return;
47279
+ const stalledSince = row.stalledAt ?? signal.at;
47280
+ const payload = {
47281
+ sourceId: row.id,
47282
+ sourceName: row.name,
47283
+ expectedIntervalSeconds: row.expectedIntervalSeconds,
47284
+ stalledForSeconds: Math.max(0, Math.round((now.getTime() - stalledSince.getTime()) / 1000)),
47285
+ recoveredBy: signal.kind,
47286
+ recoveredAt: now.getTime()
47287
+ };
47288
+ await publishTransition(deps, "system.connector.recovered", { ...payload }, row);
47289
+ stats.recovered += 1;
47290
+ await runHook(deps.logger, "onRecovered", row, () => deps.onRecovered?.(row, payload));
47291
+ }
47292
+ }
47293
+ async function publishTransition(deps, type, payload, row) {
47294
+ if (!deps.eventBus) {
47295
+ deps.logger.warn("connector liveness: no event bus, transition persisted without event", {
47296
+ type,
47297
+ sourceName: row.name
47298
+ });
47299
+ return null;
47300
+ }
47301
+ const result = await deps.eventBus.publishGeneric(type, payload, {
47302
+ source: "connector-liveness",
47303
+ ...row.tenantId ? { tenantId: row.tenantId } : {}
47304
+ });
47305
+ return result.id;
47306
+ }
47307
+ async function runHook(logger5, name, row, hook) {
47308
+ try {
47309
+ await hook();
47310
+ } catch (err2) {
47311
+ logger5.error(`connector liveness: ${name} hook failed`, {
47312
+ sourceId: row.id,
47313
+ sourceName: row.name,
47314
+ error: err2 instanceof Error ? err2.message : String(err2)
47315
+ });
47316
+ }
47317
+ }
47318
+
47319
+ // ../core/src/connectors/index.ts
47320
+ var init_connectors = () => {};
47321
+
47121
47322
  // ../core/src/sessions/reset.ts
47122
47323
  class InMemorySessionActivityStore {
47123
47324
  activities = new Map;
@@ -74531,7 +74732,7 @@ function isProviderSchemaSupported(schema) {
74531
74732
  function getSupportedProviderSchemas() {
74532
74733
  return ["agno", "webhook", "openclaw", "claude-code", "ag-ui", "a2a", "nats-genie"];
74533
74734
  }
74534
- var init_factory = __esm(() => {
74735
+ var init_factory2 = __esm(() => {
74535
74736
  init_a2a_client();
74536
74737
  init_ag_ui_client();
74537
74738
  init_agno_client();
@@ -74551,7 +74752,7 @@ var init_providers = __esm(() => {
74551
74752
  init_ag_ui_provider();
74552
74753
  init_a2a_provider();
74553
74754
  init_nats_genie_provider();
74554
- init_factory();
74755
+ init_factory2();
74555
74756
  });
74556
74757
 
74557
74758
  // ../core/src/cache/types.ts
@@ -75220,6 +75421,7 @@ __export(exports_src, {
75220
75421
  tenantLabelBucketCount: () => tenantLabelBucketCount,
75221
75422
  tenantEventsProcessed: () => tenantEventsProcessed,
75222
75423
  sweepFollowUps: () => sweepFollowUps,
75424
+ sweepConnectorLiveness: () => sweepConnectorLiveness,
75223
75425
  substituteTemplateObject: () => substituteTemplateObject,
75224
75426
  substituteTemplate: () => substituteTemplate,
75225
75427
  stampsEqual: () => stampsEqual,
@@ -75234,6 +75436,7 @@ __export(exports_src, {
75234
75436
  scheduledJobRuns: () => scheduledJobRuns,
75235
75437
  scheduledJobNextRun: () => scheduledJobNextRun,
75236
75438
  scheduledJobDuration: () => scheduledJobDuration,
75439
+ runWithEventCausality: () => runWithEventCausality,
75237
75440
  rootLogger: () => rootLogger,
75238
75441
  resolveResetConfig: () => resolveResetConfig,
75239
75442
  resolvePublishTenantId: () => resolvePublishTenantId,
@@ -75282,6 +75485,7 @@ __export(exports_src, {
75282
75485
  natsConnectionStatus: () => natsConnectionStatus,
75283
75486
  matchesPattern: () => matchesPattern,
75284
75487
  markdownToWhatsApp: () => markdownToWhatsApp,
75488
+ latestConnectorSignal: () => latestConnectorSignal,
75285
75489
  jsonEquals: () => jsonEquals,
75286
75490
  isValidUuid: () => isValidUuid,
75287
75491
  isValidJsonSchema: () => isValidJsonSchema,
@@ -75345,6 +75549,7 @@ __export(exports_src, {
75345
75549
  dbQueries: () => dbQueries,
75346
75550
  dbPoolSize: () => dbPoolSize,
75347
75551
  dbErrors: () => dbErrors,
75552
+ currentEventCausality: () => currentEventCausality,
75348
75553
  createValidatedSubscription: () => createValidatedSubscription,
75349
75554
  createTemplateContext: () => createTemplateContext,
75350
75555
  createSubscription: () => createSubscription,
@@ -75607,6 +75812,7 @@ var init_src2 = __esm(() => {
75607
75812
  init_observability();
75608
75813
  init_secrets();
75609
75814
  init_automations();
75815
+ init_connectors();
75610
75816
  init_reset();
75611
75817
  init_providers();
75612
75818
  init_cache();
@@ -80968,6 +81174,9 @@ __export(exports_schema, {
80968
81174
  conversations: () => conversations,
80969
81175
  contentTypes: () => contentTypes,
80970
81176
  consumerOffsets: () => consumerOffsets,
81177
+ connectorWindowSemanticsValues: () => connectorWindowSemanticsValues,
81178
+ connectorMutationPolicies: () => connectorMutationPolicies,
81179
+ connectorLivenessStatuses: () => connectorLivenessStatuses,
80971
81180
  conditionOperators: () => conditionOperators,
80972
81181
  closeContactOutcomes: () => closeContactOutcomes,
80973
81182
  closeContactLogs: () => closeContactLogs,
@@ -81016,7 +81225,7 @@ __export(exports_schema, {
81016
81225
  accessRules: () => accessRules,
81017
81226
  accessModes: () => accessModes
81018
81227
  });
81019
- var channelTypes, agentTypes, agentSystems, agentEntityTypes, debounceMode, splitDelayMode, supersedeMode, replyFilterMode, agentSessionStrategies, ruleTypes, accessModes, settingValueTypes, apiKeyStatuses, apiKeyProfiles, eventTypes, contentTypes, chatTypes, messageSources, messageTypes, messageStatuses, deliveryStatuses, scheduledMessageDeliveryModes, scheduledMessageStatuses, jobStatuses, providerSchemas, agentProviders, agents, agentRoutes, agentSessions, apiKeys, apiKeyAuditLogs, apiKeysRelations, apiKeyAuditLogsRelations, instances, whatsappTemplates, whatsappFlowKeys, scheduledMessages, persons, platformIdentities, conversations, chats, chatParticipants, omniGroups, messages2, omniEvents, handoffLogs, closeContactOutcomes, closeContactLogs, accessRules, globalSettings, settingChangeHistory, batchJobs, syncJobTypes, syncJobs, mediaContent, chatIdMappings, pluginStorage, agentProvidersRelations, agentsRelations, instancesRelations, syncJobsRelations, personsRelations, platformIdentitiesRelations, conversationsRelations, chatsRelations, chatParticipantsRelations, messagesRelations, omniEventsRelations, accessRulesRelations, globalSettingsRelations, settingChangeHistoryRelations, batchJobsRelations, mediaContentRelations, chatIdMappingsRelations, deadLetterStatuses, deadLetterEvents, payloadStorageConfig, payloadStages, eventPayloads, webhookSignatureAlgorithms, webhookSources, eventSchemas, conditionOperators, actionTypes, automationDebounceModes, automations2, automationLogStatuses, automationLogs, consumerOffsets, automationsRelations, automationLogsRelations, triggerLogs, triggerLogsRelations, agentRoutesRelations, agentTaskStatuses, agentTasks, agentTasksRelations, turnStatuses, turnActions, turns, turnsRelations, followUpDisarmReasons, chatFollowUpState, chatFollowUpStateRelations, processedEvents, genieHosts, tenantStatuses, principalTypes, principalStatuses, tenantRoles, membershipStatuses, credentialClasses, authCredentialStatuses, platformApiKeyStatuses, tenants, principals, tenantMemberships, tenantRolePolicies, platformApiKeys, tenantKeyLineage, authCredentials, tenantAuditLogs, platformAuditLogs, platformProviderCatalog, tenantProviderConfig, platformSettings, tenantSettings, platformSettingChangeHistory, tenantSettingChangeHistory, platformPluginStorage, tenantPluginStorage, platformPayloadStorageConfig, tenantPayloadStorageOverrides, tenantMigrationLedger, tenantMigrationLedgerHistory;
81228
+ var channelTypes, agentTypes, agentSystems, agentEntityTypes, debounceMode, splitDelayMode, supersedeMode, replyFilterMode, agentSessionStrategies, ruleTypes, accessModes, settingValueTypes, apiKeyStatuses, apiKeyProfiles, eventTypes, contentTypes, chatTypes, messageSources, messageTypes, messageStatuses, deliveryStatuses, scheduledMessageDeliveryModes, scheduledMessageStatuses, jobStatuses, providerSchemas, agentProviders, agents, agentRoutes, agentSessions, apiKeys, apiKeyAuditLogs, apiKeysRelations, apiKeyAuditLogsRelations, instances, whatsappTemplates, whatsappFlowKeys, scheduledMessages, persons, platformIdentities, conversations, chats, chatParticipants, omniGroups, messages2, omniEvents, handoffLogs, closeContactOutcomes, closeContactLogs, accessRules, globalSettings, settingChangeHistory, batchJobs, syncJobTypes, syncJobs, mediaContent, chatIdMappings, pluginStorage, agentProvidersRelations, agentsRelations, instancesRelations, syncJobsRelations, personsRelations, platformIdentitiesRelations, conversationsRelations, chatsRelations, chatParticipantsRelations, messagesRelations, omniEventsRelations, accessRulesRelations, globalSettingsRelations, settingChangeHistoryRelations, batchJobsRelations, mediaContentRelations, chatIdMappingsRelations, deadLetterStatuses, deadLetterEvents, payloadStorageConfig, payloadStages, eventPayloads, webhookSignatureAlgorithms, connectorLivenessStatuses, connectorWindowSemanticsValues, connectorMutationPolicies, webhookSources, eventSchemas, conditionOperators, actionTypes, automationDebounceModes, automations2, automationLogStatuses, automationLogs, consumerOffsets, automationsRelations, automationLogsRelations, triggerLogs, triggerLogsRelations, agentRoutesRelations, agentTaskStatuses, agentTasks, agentTasksRelations, turnStatuses, turnActions, turns, turnsRelations, followUpDisarmReasons, chatFollowUpState, chatFollowUpStateRelations, processedEvents, genieHosts, tenantStatuses, principalTypes, principalStatuses, tenantRoles, membershipStatuses, credentialClasses, authCredentialStatuses, platformApiKeyStatuses, tenants, principals, tenantMemberships, tenantRolePolicies, platformApiKeys, tenantKeyLineage, authCredentials, tenantAuditLogs, platformAuditLogs, platformProviderCatalog, tenantProviderConfig, platformSettings, tenantSettings, platformSettingChangeHistory, tenantSettingChangeHistory, platformPluginStorage, tenantPluginStorage, platformPayloadStorageConfig, tenantPayloadStorageOverrides, tenantMigrationLedger, tenantMigrationLedgerHistory;
81020
81229
  var init_schema2 = __esm(() => {
81021
81230
  init_events();
81022
81231
  init_types5();
@@ -81748,9 +81957,11 @@ var init_schema2 = __esm(() => {
81748
81957
  agentRequest: jsonb("agent_request").$type(),
81749
81958
  agentResponse: jsonb("agent_response").$type(),
81750
81959
  metadata: jsonb("metadata").$type(),
81960
+ causationId: uuid("causation_id"),
81751
81961
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
81752
81962
  tenantId: uuid("tenant_id").references(() => tenants.id, { onDelete: "restrict" })
81753
81963
  }, (table3) => ({
81964
+ causationIdx: index("omni_events_causation_idx").on(table3.causationId),
81754
81965
  tenantIdx: index("omni_events_tenant_idx").on(table3.tenantId),
81755
81966
  tenantIdUq: uniqueIndex("omni_events_tenant_id_uq").on(table3.tenantId, table3.id),
81756
81967
  externalIdIdx: index("omni_events_external_id_idx").on(table3.externalId),
@@ -82229,6 +82440,9 @@ var init_schema2 = __esm(() => {
82229
82440
  eventStageIdx: index("event_payloads_event_stage_idx").on(table3.eventId, table3.stage)
82230
82441
  }));
82231
82442
  webhookSignatureAlgorithms = ["hmac-sha256", "hmac-sha1", "token-match"];
82443
+ connectorLivenessStatuses = ["healthy", "stalled"];
82444
+ connectorWindowSemanticsValues = ["future_only", "includes_in_progress"];
82445
+ connectorMutationPolicies = ["same_id", "new_id"];
82232
82446
  webhookSources = pgTable("webhook_sources", {
82233
82447
  id: uuid("id").primaryKey().defaultRandom(),
82234
82448
  name: varchar("name", { length: 100 }).notNull().unique(),
@@ -82240,6 +82454,14 @@ var init_schema2 = __esm(() => {
82240
82454
  enabled: boolean2("enabled").notNull().default(true),
82241
82455
  lastReceivedAt: timestamp("last_received_at", { withTimezone: true }),
82242
82456
  totalReceived: integer("total_received").notNull().default(0),
82457
+ expectedIntervalSeconds: integer("expected_interval_seconds"),
82458
+ lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),
82459
+ heartbeatCount: integer("heartbeat_count").notNull().default(0),
82460
+ livenessStatus: varchar("liveness_status", { length: 20 }).$type(),
82461
+ livenessArmedAt: timestamp("liveness_armed_at", { withTimezone: true }),
82462
+ stalledAt: timestamp("stalled_at", { withTimezone: true }),
82463
+ windowSemantics: varchar("window_semantics", { length: 40 }).$type(),
82464
+ mutationPolicy: varchar("mutation_policy", { length: 20 }).$type(),
82243
82465
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
82244
82466
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
82245
82467
  tenantId: uuid("tenant_id").references(() => tenants.id, { onDelete: "restrict" })
@@ -82247,7 +82469,8 @@ var init_schema2 = __esm(() => {
82247
82469
  tenantIdx: index("webhook_sources_tenant_idx").on(table3.tenantId),
82248
82470
  tenantNameUq: uniqueIndex("webhook_sources_tenant_name_uq").on(table3.tenantId, table3.name).where(sql`${table3.tenantId} IS NOT NULL`),
82249
82471
  nameIdx: uniqueIndex("webhook_sources_name_idx").on(table3.name),
82250
- enabledIdx: index("webhook_sources_enabled_idx").on(table3.enabled)
82472
+ enabledIdx: index("webhook_sources_enabled_idx").on(table3.enabled),
82473
+ supervisedIdx: index("webhook_sources_supervised_idx").on(table3.enabled).where(sql`${table3.expectedIntervalSeconds} IS NOT NULL`)
82251
82474
  }));
82252
82475
  eventSchemas = pgTable("event_schemas", {
82253
82476
  id: uuid("id").primaryKey().defaultRandom(),
@@ -135302,6 +135525,13 @@ function createOmniClient(config) {
135302
135525
  const { data, error, response } = await client.POST("/events/trigger", { body });
135303
135526
  throwIfError(response, error);
135304
135527
  return data ?? { eventId: "", eventType: body.eventType };
135528
+ },
135529
+ async heartbeat(source) {
135530
+ const { data, error, response } = await client.POST("/webhooks/{source}/heartbeat", {
135531
+ params: { path: { source } }
135532
+ });
135533
+ throwIfError(response, error);
135534
+ return data;
135305
135535
  }
135306
135536
  },
135307
135537
  payloads: {
@@ -136109,7 +136339,7 @@ import { fileURLToPath } from "url";
136109
136339
  // package.json
136110
136340
  var package_default = {
136111
136341
  name: "@automagik/omni",
136112
- version: "2.260906.2",
136342
+ version: "2.260906.4",
136113
136343
  repository: {
136114
136344
  type: "git",
136115
136345
  url: "git+https://github.com/automagik-dev/omni.git",
@@ -142734,6 +142964,46 @@ ${title}:`);
142734
142964
  const items = Object.entries(record).sort(([, a], [, b2]) => b2 - a).map(([key, count]) => ({ [keyName]: key, count }));
142735
142965
  list(items);
142736
142966
  }
142967
+ async function fetchTrace(id) {
142968
+ const config = loadConfig();
142969
+ const baseUrl = config.apiUrl ?? "http://localhost:8882";
142970
+ const resp = await fetch(`${baseUrl}/api/v2/events/${encodeURIComponent(id)}/trace`, {
142971
+ headers: { "x-api-key": config.apiKey ?? "" }
142972
+ });
142973
+ if (!resp.ok) {
142974
+ throw new Error(`API returned ${resp.status}: ${await resp.text()}`);
142975
+ }
142976
+ return (await resp.json()).data;
142977
+ }
142978
+ function formatTraceLine(event, marker) {
142979
+ const time = new Date(event.receivedAt).toISOString().replace("T", " ").slice(0, 19);
142980
+ const corr = event.metadata?.correlationId?.slice(0, 8) ?? "--------";
142981
+ const text = event.textContent ? ` "${event.textContent.slice(0, 40)}"` : "";
142982
+ return `${marker}${event.eventType} ${event.id.slice(0, 8)} ${time} corr=${corr}${text}`;
142983
+ }
142984
+ function displayTrace(data2) {
142985
+ if (getOutputFormat() === "json") {
142986
+ data(data2);
142987
+ return;
142988
+ }
142989
+ let indent = 0;
142990
+ for (const ancestor of data2.ancestors) {
142991
+ const label = indent === 0 && !ancestor.causationId ? " (root)" : "";
142992
+ raw(`${" ".repeat(indent)}${formatTraceLine(ancestor, indent === 0 ? "\u25CF " : "\u2514\u2500 ")}${label}`);
142993
+ indent++;
142994
+ }
142995
+ const focusIsRoot = data2.ancestors.length === 0 && !data2.event.causationId;
142996
+ raw(`${" ".repeat(indent)}${formatTraceLine(data2.event, indent === 0 ? "\u25CF " : "\u2514\u2500 ")}${focusIsRoot ? " (root)" : ""} \u2190 trace target`);
142997
+ for (const { event, depth } of data2.descendants) {
142998
+ raw(`${" ".repeat(indent + depth)}${formatTraceLine(event, "\u2514\u2500 ")}`);
142999
+ }
143000
+ if (data2.descendants.length === 0) {
143001
+ dim("(no descendants \u2014 nothing was caused by this event)");
143002
+ }
143003
+ if (data2.truncated) {
143004
+ warn("Trace truncated (depth/node cap reached)");
143005
+ }
143006
+ }
142737
143007
  async function schemaApiRequest(path, init = {}) {
142738
143008
  const config = loadConfig();
142739
143009
  const baseUrl = config.apiUrl ?? "http://localhost:8882";
@@ -142980,6 +143250,15 @@ function createEventsCommand() {
142980
143250
  error(`Failed to get event: ${message}`);
142981
143251
  }
142982
143252
  });
143253
+ events.command("trace <id>").description("Walk the causality chain around an event: root \u2192 parents \u2192 this event \u2192 everything it caused").action(async (id) => {
143254
+ try {
143255
+ const data2 = await fetchTrace(id);
143256
+ displayTrace(data2);
143257
+ } catch (err) {
143258
+ const message = err instanceof Error ? err.message : "Unknown error";
143259
+ error(`Failed to trace event: ${message}`);
143260
+ }
143261
+ });
142983
143262
  events.command("search <query>").description("Search events by content").option("--since <time>", "Events since (e.g., 24h, 7d)").option("--limit <n>", "Limit results", (v) => Number.parseInt(v, 10), 50).action(async (query, options) => {
142984
143263
  const client = getClient();
142985
143264
  try {
@@ -150609,6 +150888,53 @@ function assertPairedSignatureOnCreate(signatureConfig, signatureSecret) {
150609
150888
  }
150610
150889
  throw new Error(`--signature-algorithm and --signature-header require a signature secret (${SECRET_SOURCE_FLAGS})`);
150611
150890
  }
150891
+ var WINDOW_SEMANTICS = ["future_only", "includes_in_progress"];
150892
+ var MUTATION_POLICIES = ["same_id", "new_id"];
150893
+ function parseExpectedInterval(raw2) {
150894
+ if (raw2 === undefined)
150895
+ return;
150896
+ const seconds = Number(raw2);
150897
+ if (!Number.isInteger(seconds) || seconds < 1) {
150898
+ error("--expected-interval must be a positive integer number of seconds");
150899
+ }
150900
+ return seconds;
150901
+ }
150902
+ function parseWindowSemantics(raw2) {
150903
+ if (raw2 === undefined)
150904
+ return;
150905
+ const value = WINDOW_SEMANTICS.find((v4) => v4 === raw2);
150906
+ if (!value) {
150907
+ error(`Invalid --window-semantics; expected one of: ${WINDOW_SEMANTICS.join(", ")}`);
150908
+ }
150909
+ return value;
150910
+ }
150911
+ function parseMutationPolicy(raw2) {
150912
+ if (raw2 === undefined)
150913
+ return;
150914
+ const value = MUTATION_POLICIES.find((v4) => v4 === raw2);
150915
+ if (!value) {
150916
+ error(`Invalid --mutation-policy; expected one of: ${MUTATION_POLICIES.join(", ")}`);
150917
+ }
150918
+ return value;
150919
+ }
150920
+ function buildLifecycleUpdates(options3) {
150921
+ if (options3.clearCadence && options3.expectedInterval !== undefined) {
150922
+ error("Use only one of --expected-interval and --clear-cadence");
150923
+ }
150924
+ const updates = {};
150925
+ if (options3.clearCadence)
150926
+ updates.expectedIntervalSeconds = null;
150927
+ const expectedIntervalSeconds = parseExpectedInterval(options3.expectedInterval);
150928
+ if (expectedIntervalSeconds !== undefined)
150929
+ updates.expectedIntervalSeconds = expectedIntervalSeconds;
150930
+ const windowSemantics = parseWindowSemantics(options3.windowSemantics);
150931
+ if (windowSemantics !== undefined)
150932
+ updates.windowSemantics = windowSemantics;
150933
+ const mutationPolicy = parseMutationPolicy(options3.mutationPolicy);
150934
+ if (mutationPolicy !== undefined)
150935
+ updates.mutationPolicy = mutationPolicy;
150936
+ return updates;
150937
+ }
150612
150938
  function createWebhooksCommand() {
150613
150939
  const webhooks = new Command("webhooks").description("Manage webhook sources");
150614
150940
  webhooks.command("list").description("List webhook sources").option("--enabled", "Show only enabled sources").option("--disabled", "Show only disabled sources").action(async (options3) => {
@@ -150626,6 +150952,8 @@ function createWebhooksCommand() {
150626
150952
  id: w.id,
150627
150953
  name: w.name,
150628
150954
  enabled: w.enabled ? "yes" : "no",
150955
+ health: w.livenessStatus ?? "-",
150956
+ cadence: w.expectedIntervalSeconds != null ? `${w.expectedIntervalSeconds}s` : "-",
150629
150957
  createdAt: w.createdAt
150630
150958
  }));
150631
150959
  list(items, { emptyMessage: "No webhook sources found." });
@@ -150645,7 +150973,7 @@ function createWebhooksCommand() {
150645
150973
  error(`Failed to get webhook source: ${message2}`);
150646
150974
  }
150647
150975
  });
150648
- webhooks.command("create").description("Create a webhook source").requiredOption("--name <name>", "Webhook source name").option("--description <desc>", "Description").option("--disabled", "Create in disabled state").option("--headers <json>", `Expected headers as JSON (e.g., '{"X-Secret": true}')`).option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").action(async (options3) => {
150976
+ webhooks.command("create").description("Create a webhook source").requiredOption("--name <name>", "Webhook source name").option("--description <desc>", "Description").option("--disabled", "Create in disabled state").option("--headers <json>", `Expected headers as JSON (e.g., '{"X-Secret": true}')`).option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").option("--expected-interval <seconds>", "Declared cadence: >=1 event or heartbeat per N seconds. Arms liveness supervision (#961)").option("--window-semantics <value>", `Declared window semantics: ${WINDOW_SEMANTICS.join(" or ")}`).option("--mutation-policy <value>", `Declared mutation re-emit policy: ${MUTATION_POLICIES.join(" or ")}`).action(async (options3) => {
150649
150977
  const client = getClient();
150650
150978
  try {
150651
150979
  let expectedHeaders;
@@ -150665,7 +150993,10 @@ function createWebhooksCommand() {
150665
150993
  enabled: !options3.disabled,
150666
150994
  expectedHeaders,
150667
150995
  signatureConfig,
150668
- signatureSecret
150996
+ signatureSecret,
150997
+ expectedIntervalSeconds: parseExpectedInterval(options3.expectedInterval),
150998
+ windowSemantics: parseWindowSemantics(options3.windowSemantics),
150999
+ mutationPolicy: parseMutationPolicy(options3.mutationPolicy)
150669
151000
  });
150670
151001
  const details = {
150671
151002
  id: source.id,
@@ -150681,11 +151012,11 @@ function createWebhooksCommand() {
150681
151012
  error(`Failed to create webhook source: ${message2}`);
150682
151013
  }
150683
151014
  });
150684
- webhooks.command("update <id>").description("Update a webhook source").option("--name <name>", "New name").option("--description <desc>", "New description").option("--enable", "Enable the webhook").option("--disable", "Disable the webhook").option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").option("--clear-signature", "Remove the signature config and stored secret").action(async (id, options3) => {
151015
+ webhooks.command("update <id>").description("Update a webhook source").option("--name <name>", "New name").option("--description <desc>", "New description").option("--enable", "Enable the webhook").option("--disable", "Disable the webhook").option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").option("--clear-signature", "Remove the signature config and stored secret").option("--expected-interval <seconds>", "Declared cadence: >=1 event or heartbeat per N seconds. (Re)arms liveness supervision (#961)").option("--clear-cadence", "Remove the declared cadence (disarms liveness supervision)").option("--window-semantics <value>", `Declared window semantics: ${WINDOW_SEMANTICS.join(" or ")}`).option("--mutation-policy <value>", `Declared mutation re-emit policy: ${MUTATION_POLICIES.join(" or ")}`).action(async (id, options3) => {
150685
151016
  const resolvedId = await resolveWebhookId(id);
150686
151017
  const client = getClient();
150687
151018
  try {
150688
- const updates = {};
151019
+ const updates = buildLifecycleUpdates(options3);
150689
151020
  if (options3.name)
150690
151021
  updates.name = options3.name;
150691
151022
  if (options3.description)
@@ -150751,6 +151082,20 @@ function createWebhooksCommand() {
150751
151082
  error(`Failed to trigger event: ${message2}`);
150752
151083
  }
150753
151084
  });
151085
+ webhooks.command("heartbeat <source>").description('Record a connector heartbeat ("ran, zero events found") \u2014 resets the liveness window (#961)').action(async (source) => {
151086
+ const client = getClient();
151087
+ try {
151088
+ const result = await client.webhooks.heartbeat(source);
151089
+ success(`Heartbeat recorded for '${result.source}'`, {
151090
+ heartbeatAt: result.heartbeatAt,
151091
+ livenessStatus: result.livenessStatus ?? "unsupervised",
151092
+ expectedIntervalSeconds: result.expectedIntervalSeconds
151093
+ });
151094
+ } catch (err2) {
151095
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151096
+ error(`Failed to record heartbeat: ${message2}`);
151097
+ }
151098
+ });
150754
151099
  return webhooks;
150755
151100
  }
150756
151101