@automagik/omni 2.260906.2 → 2.260906.3
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/db/drizzle/0057_connector_lifecycle_contract.sql +40 -0
- package/db/drizzle/meta/_journal.json +7 -0
- package/dist/commands/webhooks.d.ts.map +1 -1
- package/dist/index.js +223 -8
- package/dist/sdk/client.d.ts +21 -0
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +7 -0
- package/dist/sdk/types.generated.d.ts +376 -10
- package/dist/sdk/types.generated.d.ts.map +1 -1
- package/dist/server/index.js +342 -7
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
-- Connector lifecycle contract (#961) — liveness, heartbeat, declared
|
|
2
|
+
-- window/mutation semantics on webhook_sources.
|
|
3
|
+
--
|
|
4
|
+
-- A source that declares expected_interval_seconds promises "≥1 event or
|
|
5
|
+
-- heartbeat per N seconds". The liveness sweeper compares the most recent
|
|
6
|
+
-- signal — GREATEST(last_received_at, last_heartbeat_at, liveness_armed_at) —
|
|
7
|
+
-- against that window and owns every liveness_status transition, so
|
|
8
|
+
-- system.connector.stalled / system.connector.recovered are emitted exactly
|
|
9
|
+
-- once per transition.
|
|
10
|
+
--
|
|
11
|
+
-- expected_interval_seconds integer declared cadence; NULL = unsupervised
|
|
12
|
+
-- last_heartbeat_at timestamptz "I ran, zero events found" — the
|
|
13
|
+
-- heartbeat verb's compacted trace
|
|
14
|
+
-- (no journal event per heartbeat)
|
|
15
|
+
-- heartbeat_count integer total heartbeats received
|
|
16
|
+
-- liveness_status varchar(20) 'healthy' | 'stalled'; NULL = unsupervised
|
|
17
|
+
-- liveness_armed_at timestamptz when the cadence was (re)declared —
|
|
18
|
+
-- a fresh window before a stall can fire
|
|
19
|
+
-- stalled_at timestamptz when the current stall began
|
|
20
|
+
-- window_semantics varchar(40) 'future_only' | 'includes_in_progress'
|
|
21
|
+
-- mutation_policy varchar(20) 'same_id' | 'new_id' (feeds #958's
|
|
22
|
+
-- idempotency key template choice)
|
|
23
|
+
--
|
|
24
|
+
-- Hand-written following the 0044-0055 precedent (snapshot drift keeps
|
|
25
|
+
-- drizzle-kit generate interactive). Additive + idempotent. No explicit
|
|
26
|
+
-- BEGIN/COMMIT — the boot migrator executes this file on a pooled postgres-js
|
|
27
|
+
-- connection, which rejects raw transaction control.
|
|
28
|
+
|
|
29
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "expected_interval_seconds" integer;
|
|
30
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "last_heartbeat_at" timestamp with time zone;
|
|
31
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "heartbeat_count" integer DEFAULT 0 NOT NULL;
|
|
32
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "liveness_status" varchar(20);
|
|
33
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "liveness_armed_at" timestamp with time zone;
|
|
34
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "stalled_at" timestamp with time zone;
|
|
35
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "window_semantics" varchar(40);
|
|
36
|
+
ALTER TABLE "webhook_sources" ADD COLUMN IF NOT EXISTS "mutation_policy" varchar(20);
|
|
37
|
+
|
|
38
|
+
-- The liveness sweeper's scan: enabled sources with a declared cadence.
|
|
39
|
+
CREATE INDEX IF NOT EXISTS "webhook_sources_supervised_idx"
|
|
40
|
+
ON "webhook_sources" ("enabled") WHERE "expected_interval_seconds" IS NOT NULL;
|
|
@@ -400,6 +400,13 @@
|
|
|
400
400
|
"when": 1785600000000,
|
|
401
401
|
"tag": "0056_event_schema_registry",
|
|
402
402
|
"breakpoints": true
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"idx": 57,
|
|
406
|
+
"version": "7",
|
|
407
|
+
"when": 1785700000000,
|
|
408
|
+
"tag": "0057_connector_lifecycle_contract",
|
|
409
|
+
"breakpoints": true
|
|
403
410
|
}
|
|
404
411
|
]
|
|
405
412
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/commands/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,UAAU,sBAAsB;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAcD;;;;;GAKG;AACH,iBAAe,sBAAsB,CACnC,OAAO,EAAE,sBAAsB,EAC/B,SAAS,GAAE,MAAM,OAAO,CAAC,MAAM,CAAuB,GACrD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqC7B;AAED,6FAA6F;AAC7F,iBAAS,oBAAoB,CAAC,OAAO,EAAE;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,0BAA0B,GAAG,SAAS,CAczC;AAED;;;;;GAKG;AACH,iBAAS,6BAA6B,CACpC,eAAe,EAAE,0BAA0B,GAAG,SAAS,EACvD,eAAe,EAAE,MAAM,GAAG,SAAS,GAClC,IAAI,CAQN;
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/commands/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,UAAU,sBAAsB;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAcD;;;;;GAKG;AACH,iBAAe,sBAAsB,CACnC,OAAO,EAAE,sBAAsB,EAC/B,SAAS,GAAE,MAAM,OAAO,CAAC,MAAM,CAAuB,GACrD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqC7B;AAED,6FAA6F;AAC7F,iBAAS,oBAAoB,CAAC,OAAO,EAAE;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,0BAA0B,GAAG,SAAS,CAczC;AAED;;;;;GAKG;AACH,iBAAS,6BAA6B,CACpC,eAAe,EAAE,0BAA0B,GAAG,SAAS,EACvD,eAAe,EAAE,MAAM,GAAG,SAAS,GAClC,IAAI,CAQN;AA4DD,wBAAgB,qBAAqB,IAAI,OAAO,CA6S/C;AAED,wDAAwD;AACxD,eAAO,MAAM,WAAW;;;;CAAkF,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -30739,7 +30739,24 @@ var init_registry = __esm(() => {
|
|
|
30739
30739
|
component: exports_external.string(),
|
|
30740
30740
|
reason: exports_external.string(),
|
|
30741
30741
|
severity: exports_external.enum(["warning", "critical"])
|
|
30742
|
-
}), { description: "System health degradation detected" })
|
|
30742
|
+
}), { description: "System health degradation detected" }),
|
|
30743
|
+
connectorStalled: createEventSchema("system.connector.stalled", exports_external.object({
|
|
30744
|
+
sourceId: exports_external.string(),
|
|
30745
|
+
sourceName: exports_external.string(),
|
|
30746
|
+
expectedIntervalSeconds: exports_external.number(),
|
|
30747
|
+
lastReceivedAt: exports_external.number().nullable(),
|
|
30748
|
+
lastHeartbeatAt: exports_external.number().nullable(),
|
|
30749
|
+
silentForSeconds: exports_external.number(),
|
|
30750
|
+
stalledAt: exports_external.number()
|
|
30751
|
+
}), { description: "A supervised connector went silent beyond its declared cadence" }),
|
|
30752
|
+
connectorRecovered: createEventSchema("system.connector.recovered", exports_external.object({
|
|
30753
|
+
sourceId: exports_external.string(),
|
|
30754
|
+
sourceName: exports_external.string(),
|
|
30755
|
+
expectedIntervalSeconds: exports_external.number(),
|
|
30756
|
+
stalledForSeconds: exports_external.number(),
|
|
30757
|
+
recoveredBy: exports_external.enum(["event", "heartbeat", "rearmed"]),
|
|
30758
|
+
recoveredAt: exports_external.number()
|
|
30759
|
+
}), { description: "A stalled connector signalled again (event, heartbeat, or re-declared cadence)" })
|
|
30743
30760
|
};
|
|
30744
30761
|
CustomEventSchemas = {
|
|
30745
30762
|
chatUnreadUpdated: createEventSchema("custom.chat.unread-updated", exports_external.object({
|
|
@@ -47118,6 +47135,113 @@ var init_automations = __esm(() => {
|
|
|
47118
47135
|
init_sweeper();
|
|
47119
47136
|
});
|
|
47120
47137
|
|
|
47138
|
+
// ../core/src/connectors/liveness.ts
|
|
47139
|
+
function latestConnectorSignal(row) {
|
|
47140
|
+
let best = { at: row.createdAt, kind: "rearmed" };
|
|
47141
|
+
const candidates = [
|
|
47142
|
+
{ at: row.livenessArmedAt, kind: "rearmed" },
|
|
47143
|
+
{ at: row.lastReceivedAt, kind: "event" },
|
|
47144
|
+
{ at: row.lastHeartbeatAt, kind: "heartbeat" }
|
|
47145
|
+
];
|
|
47146
|
+
for (const candidate of candidates) {
|
|
47147
|
+
if (candidate.at && candidate.at.getTime() >= best.at.getTime()) {
|
|
47148
|
+
best = { at: candidate.at, kind: candidate.kind };
|
|
47149
|
+
}
|
|
47150
|
+
}
|
|
47151
|
+
return best;
|
|
47152
|
+
}
|
|
47153
|
+
async function sweepConnectorLiveness(deps) {
|
|
47154
|
+
const clock = deps.now ?? (() => new Date);
|
|
47155
|
+
const now = clock();
|
|
47156
|
+
const rows = await deps.repo.findSupervised();
|
|
47157
|
+
const stats = { scanned: rows.length, stalled: 0, recovered: 0, errors: 0 };
|
|
47158
|
+
for (const row of rows) {
|
|
47159
|
+
try {
|
|
47160
|
+
await processRow2(row, now, deps, stats);
|
|
47161
|
+
} catch (err2) {
|
|
47162
|
+
stats.errors += 1;
|
|
47163
|
+
deps.logger.error("connector liveness: failed to process source", {
|
|
47164
|
+
sourceId: row.id,
|
|
47165
|
+
sourceName: row.name,
|
|
47166
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
47167
|
+
});
|
|
47168
|
+
}
|
|
47169
|
+
}
|
|
47170
|
+
if (stats.stalled > 0 || stats.recovered > 0) {
|
|
47171
|
+
deps.logger.info("connector liveness sweep tick", { ...stats });
|
|
47172
|
+
}
|
|
47173
|
+
return stats;
|
|
47174
|
+
}
|
|
47175
|
+
async function processRow2(row, now, deps, stats) {
|
|
47176
|
+
const signal = latestConnectorSignal(row);
|
|
47177
|
+
const silentMs = Math.max(0, now.getTime() - signal.at.getTime());
|
|
47178
|
+
const overdue = silentMs > row.expectedIntervalSeconds * 1000;
|
|
47179
|
+
const silentForSeconds = Math.round(silentMs / 1000);
|
|
47180
|
+
if (overdue) {
|
|
47181
|
+
if (row.livenessStatus === "stalled")
|
|
47182
|
+
return;
|
|
47183
|
+
if (!await deps.repo.markStalled(row.id, now))
|
|
47184
|
+
return;
|
|
47185
|
+
const payload = {
|
|
47186
|
+
sourceId: row.id,
|
|
47187
|
+
sourceName: row.name,
|
|
47188
|
+
expectedIntervalSeconds: row.expectedIntervalSeconds,
|
|
47189
|
+
lastReceivedAt: row.lastReceivedAt?.getTime() ?? null,
|
|
47190
|
+
lastHeartbeatAt: row.lastHeartbeatAt?.getTime() ?? null,
|
|
47191
|
+
silentForSeconds,
|
|
47192
|
+
stalledAt: now.getTime()
|
|
47193
|
+
};
|
|
47194
|
+
const eventId = await publishTransition(deps, "system.connector.stalled", { ...payload }, row);
|
|
47195
|
+
stats.stalled += 1;
|
|
47196
|
+
await runHook(deps.logger, "onStalled", row, () => deps.onStalled?.(row, payload, eventId));
|
|
47197
|
+
return;
|
|
47198
|
+
}
|
|
47199
|
+
if (row.livenessStatus === "stalled") {
|
|
47200
|
+
if (!await deps.repo.markRecovered(row.id, now))
|
|
47201
|
+
return;
|
|
47202
|
+
const stalledSince = row.stalledAt ?? signal.at;
|
|
47203
|
+
const payload = {
|
|
47204
|
+
sourceId: row.id,
|
|
47205
|
+
sourceName: row.name,
|
|
47206
|
+
expectedIntervalSeconds: row.expectedIntervalSeconds,
|
|
47207
|
+
stalledForSeconds: Math.max(0, Math.round((now.getTime() - stalledSince.getTime()) / 1000)),
|
|
47208
|
+
recoveredBy: signal.kind,
|
|
47209
|
+
recoveredAt: now.getTime()
|
|
47210
|
+
};
|
|
47211
|
+
await publishTransition(deps, "system.connector.recovered", { ...payload }, row);
|
|
47212
|
+
stats.recovered += 1;
|
|
47213
|
+
await runHook(deps.logger, "onRecovered", row, () => deps.onRecovered?.(row, payload));
|
|
47214
|
+
}
|
|
47215
|
+
}
|
|
47216
|
+
async function publishTransition(deps, type, payload, row) {
|
|
47217
|
+
if (!deps.eventBus) {
|
|
47218
|
+
deps.logger.warn("connector liveness: no event bus, transition persisted without event", {
|
|
47219
|
+
type,
|
|
47220
|
+
sourceName: row.name
|
|
47221
|
+
});
|
|
47222
|
+
return null;
|
|
47223
|
+
}
|
|
47224
|
+
const result = await deps.eventBus.publishGeneric(type, payload, {
|
|
47225
|
+
source: "connector-liveness",
|
|
47226
|
+
...row.tenantId ? { tenantId: row.tenantId } : {}
|
|
47227
|
+
});
|
|
47228
|
+
return result.id;
|
|
47229
|
+
}
|
|
47230
|
+
async function runHook(logger5, name, row, hook) {
|
|
47231
|
+
try {
|
|
47232
|
+
await hook();
|
|
47233
|
+
} catch (err2) {
|
|
47234
|
+
logger5.error(`connector liveness: ${name} hook failed`, {
|
|
47235
|
+
sourceId: row.id,
|
|
47236
|
+
sourceName: row.name,
|
|
47237
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
47238
|
+
});
|
|
47239
|
+
}
|
|
47240
|
+
}
|
|
47241
|
+
|
|
47242
|
+
// ../core/src/connectors/index.ts
|
|
47243
|
+
var init_connectors = () => {};
|
|
47244
|
+
|
|
47121
47245
|
// ../core/src/sessions/reset.ts
|
|
47122
47246
|
class InMemorySessionActivityStore {
|
|
47123
47247
|
activities = new Map;
|
|
@@ -75220,6 +75344,7 @@ __export(exports_src, {
|
|
|
75220
75344
|
tenantLabelBucketCount: () => tenantLabelBucketCount,
|
|
75221
75345
|
tenantEventsProcessed: () => tenantEventsProcessed,
|
|
75222
75346
|
sweepFollowUps: () => sweepFollowUps,
|
|
75347
|
+
sweepConnectorLiveness: () => sweepConnectorLiveness,
|
|
75223
75348
|
substituteTemplateObject: () => substituteTemplateObject,
|
|
75224
75349
|
substituteTemplate: () => substituteTemplate,
|
|
75225
75350
|
stampsEqual: () => stampsEqual,
|
|
@@ -75282,6 +75407,7 @@ __export(exports_src, {
|
|
|
75282
75407
|
natsConnectionStatus: () => natsConnectionStatus,
|
|
75283
75408
|
matchesPattern: () => matchesPattern,
|
|
75284
75409
|
markdownToWhatsApp: () => markdownToWhatsApp,
|
|
75410
|
+
latestConnectorSignal: () => latestConnectorSignal,
|
|
75285
75411
|
jsonEquals: () => jsonEquals,
|
|
75286
75412
|
isValidUuid: () => isValidUuid,
|
|
75287
75413
|
isValidJsonSchema: () => isValidJsonSchema,
|
|
@@ -75607,6 +75733,7 @@ var init_src2 = __esm(() => {
|
|
|
75607
75733
|
init_observability();
|
|
75608
75734
|
init_secrets();
|
|
75609
75735
|
init_automations();
|
|
75736
|
+
init_connectors();
|
|
75610
75737
|
init_reset();
|
|
75611
75738
|
init_providers();
|
|
75612
75739
|
init_cache();
|
|
@@ -80968,6 +81095,9 @@ __export(exports_schema, {
|
|
|
80968
81095
|
conversations: () => conversations,
|
|
80969
81096
|
contentTypes: () => contentTypes,
|
|
80970
81097
|
consumerOffsets: () => consumerOffsets,
|
|
81098
|
+
connectorWindowSemanticsValues: () => connectorWindowSemanticsValues,
|
|
81099
|
+
connectorMutationPolicies: () => connectorMutationPolicies,
|
|
81100
|
+
connectorLivenessStatuses: () => connectorLivenessStatuses,
|
|
80971
81101
|
conditionOperators: () => conditionOperators,
|
|
80972
81102
|
closeContactOutcomes: () => closeContactOutcomes,
|
|
80973
81103
|
closeContactLogs: () => closeContactLogs,
|
|
@@ -81016,7 +81146,7 @@ __export(exports_schema, {
|
|
|
81016
81146
|
accessRules: () => accessRules,
|
|
81017
81147
|
accessModes: () => accessModes
|
|
81018
81148
|
});
|
|
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;
|
|
81149
|
+
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
81150
|
var init_schema2 = __esm(() => {
|
|
81021
81151
|
init_events();
|
|
81022
81152
|
init_types5();
|
|
@@ -82229,6 +82359,9 @@ var init_schema2 = __esm(() => {
|
|
|
82229
82359
|
eventStageIdx: index("event_payloads_event_stage_idx").on(table3.eventId, table3.stage)
|
|
82230
82360
|
}));
|
|
82231
82361
|
webhookSignatureAlgorithms = ["hmac-sha256", "hmac-sha1", "token-match"];
|
|
82362
|
+
connectorLivenessStatuses = ["healthy", "stalled"];
|
|
82363
|
+
connectorWindowSemanticsValues = ["future_only", "includes_in_progress"];
|
|
82364
|
+
connectorMutationPolicies = ["same_id", "new_id"];
|
|
82232
82365
|
webhookSources = pgTable("webhook_sources", {
|
|
82233
82366
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
82234
82367
|
name: varchar("name", { length: 100 }).notNull().unique(),
|
|
@@ -82240,6 +82373,14 @@ var init_schema2 = __esm(() => {
|
|
|
82240
82373
|
enabled: boolean2("enabled").notNull().default(true),
|
|
82241
82374
|
lastReceivedAt: timestamp("last_received_at", { withTimezone: true }),
|
|
82242
82375
|
totalReceived: integer("total_received").notNull().default(0),
|
|
82376
|
+
expectedIntervalSeconds: integer("expected_interval_seconds"),
|
|
82377
|
+
lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),
|
|
82378
|
+
heartbeatCount: integer("heartbeat_count").notNull().default(0),
|
|
82379
|
+
livenessStatus: varchar("liveness_status", { length: 20 }).$type(),
|
|
82380
|
+
livenessArmedAt: timestamp("liveness_armed_at", { withTimezone: true }),
|
|
82381
|
+
stalledAt: timestamp("stalled_at", { withTimezone: true }),
|
|
82382
|
+
windowSemantics: varchar("window_semantics", { length: 40 }).$type(),
|
|
82383
|
+
mutationPolicy: varchar("mutation_policy", { length: 20 }).$type(),
|
|
82243
82384
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
82244
82385
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
82245
82386
|
tenantId: uuid("tenant_id").references(() => tenants.id, { onDelete: "restrict" })
|
|
@@ -82247,7 +82388,8 @@ var init_schema2 = __esm(() => {
|
|
|
82247
82388
|
tenantIdx: index("webhook_sources_tenant_idx").on(table3.tenantId),
|
|
82248
82389
|
tenantNameUq: uniqueIndex("webhook_sources_tenant_name_uq").on(table3.tenantId, table3.name).where(sql`${table3.tenantId} IS NOT NULL`),
|
|
82249
82390
|
nameIdx: uniqueIndex("webhook_sources_name_idx").on(table3.name),
|
|
82250
|
-
enabledIdx: index("webhook_sources_enabled_idx").on(table3.enabled)
|
|
82391
|
+
enabledIdx: index("webhook_sources_enabled_idx").on(table3.enabled),
|
|
82392
|
+
supervisedIdx: index("webhook_sources_supervised_idx").on(table3.enabled).where(sql`${table3.expectedIntervalSeconds} IS NOT NULL`)
|
|
82251
82393
|
}));
|
|
82252
82394
|
eventSchemas = pgTable("event_schemas", {
|
|
82253
82395
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
@@ -135302,6 +135444,13 @@ function createOmniClient(config) {
|
|
|
135302
135444
|
const { data, error, response } = await client.POST("/events/trigger", { body });
|
|
135303
135445
|
throwIfError(response, error);
|
|
135304
135446
|
return data ?? { eventId: "", eventType: body.eventType };
|
|
135447
|
+
},
|
|
135448
|
+
async heartbeat(source) {
|
|
135449
|
+
const { data, error, response } = await client.POST("/webhooks/{source}/heartbeat", {
|
|
135450
|
+
params: { path: { source } }
|
|
135451
|
+
});
|
|
135452
|
+
throwIfError(response, error);
|
|
135453
|
+
return data;
|
|
135305
135454
|
}
|
|
135306
135455
|
},
|
|
135307
135456
|
payloads: {
|
|
@@ -136109,7 +136258,7 @@ import { fileURLToPath } from "url";
|
|
|
136109
136258
|
// package.json
|
|
136110
136259
|
var package_default = {
|
|
136111
136260
|
name: "@automagik/omni",
|
|
136112
|
-
version: "2.260906.
|
|
136261
|
+
version: "2.260906.3",
|
|
136113
136262
|
repository: {
|
|
136114
136263
|
type: "git",
|
|
136115
136264
|
url: "git+https://github.com/automagik-dev/omni.git",
|
|
@@ -150609,6 +150758,53 @@ function assertPairedSignatureOnCreate(signatureConfig, signatureSecret) {
|
|
|
150609
150758
|
}
|
|
150610
150759
|
throw new Error(`--signature-algorithm and --signature-header require a signature secret (${SECRET_SOURCE_FLAGS})`);
|
|
150611
150760
|
}
|
|
150761
|
+
var WINDOW_SEMANTICS = ["future_only", "includes_in_progress"];
|
|
150762
|
+
var MUTATION_POLICIES = ["same_id", "new_id"];
|
|
150763
|
+
function parseExpectedInterval(raw2) {
|
|
150764
|
+
if (raw2 === undefined)
|
|
150765
|
+
return;
|
|
150766
|
+
const seconds = Number(raw2);
|
|
150767
|
+
if (!Number.isInteger(seconds) || seconds < 1) {
|
|
150768
|
+
error("--expected-interval must be a positive integer number of seconds");
|
|
150769
|
+
}
|
|
150770
|
+
return seconds;
|
|
150771
|
+
}
|
|
150772
|
+
function parseWindowSemantics(raw2) {
|
|
150773
|
+
if (raw2 === undefined)
|
|
150774
|
+
return;
|
|
150775
|
+
const value = WINDOW_SEMANTICS.find((v4) => v4 === raw2);
|
|
150776
|
+
if (!value) {
|
|
150777
|
+
error(`Invalid --window-semantics; expected one of: ${WINDOW_SEMANTICS.join(", ")}`);
|
|
150778
|
+
}
|
|
150779
|
+
return value;
|
|
150780
|
+
}
|
|
150781
|
+
function parseMutationPolicy(raw2) {
|
|
150782
|
+
if (raw2 === undefined)
|
|
150783
|
+
return;
|
|
150784
|
+
const value = MUTATION_POLICIES.find((v4) => v4 === raw2);
|
|
150785
|
+
if (!value) {
|
|
150786
|
+
error(`Invalid --mutation-policy; expected one of: ${MUTATION_POLICIES.join(", ")}`);
|
|
150787
|
+
}
|
|
150788
|
+
return value;
|
|
150789
|
+
}
|
|
150790
|
+
function buildLifecycleUpdates(options3) {
|
|
150791
|
+
if (options3.clearCadence && options3.expectedInterval !== undefined) {
|
|
150792
|
+
error("Use only one of --expected-interval and --clear-cadence");
|
|
150793
|
+
}
|
|
150794
|
+
const updates = {};
|
|
150795
|
+
if (options3.clearCadence)
|
|
150796
|
+
updates.expectedIntervalSeconds = null;
|
|
150797
|
+
const expectedIntervalSeconds = parseExpectedInterval(options3.expectedInterval);
|
|
150798
|
+
if (expectedIntervalSeconds !== undefined)
|
|
150799
|
+
updates.expectedIntervalSeconds = expectedIntervalSeconds;
|
|
150800
|
+
const windowSemantics = parseWindowSemantics(options3.windowSemantics);
|
|
150801
|
+
if (windowSemantics !== undefined)
|
|
150802
|
+
updates.windowSemantics = windowSemantics;
|
|
150803
|
+
const mutationPolicy = parseMutationPolicy(options3.mutationPolicy);
|
|
150804
|
+
if (mutationPolicy !== undefined)
|
|
150805
|
+
updates.mutationPolicy = mutationPolicy;
|
|
150806
|
+
return updates;
|
|
150807
|
+
}
|
|
150612
150808
|
function createWebhooksCommand() {
|
|
150613
150809
|
const webhooks = new Command("webhooks").description("Manage webhook sources");
|
|
150614
150810
|
webhooks.command("list").description("List webhook sources").option("--enabled", "Show only enabled sources").option("--disabled", "Show only disabled sources").action(async (options3) => {
|
|
@@ -150626,6 +150822,8 @@ function createWebhooksCommand() {
|
|
|
150626
150822
|
id: w.id,
|
|
150627
150823
|
name: w.name,
|
|
150628
150824
|
enabled: w.enabled ? "yes" : "no",
|
|
150825
|
+
health: w.livenessStatus ?? "-",
|
|
150826
|
+
cadence: w.expectedIntervalSeconds != null ? `${w.expectedIntervalSeconds}s` : "-",
|
|
150629
150827
|
createdAt: w.createdAt
|
|
150630
150828
|
}));
|
|
150631
150829
|
list(items, { emptyMessage: "No webhook sources found." });
|
|
@@ -150645,7 +150843,7 @@ function createWebhooksCommand() {
|
|
|
150645
150843
|
error(`Failed to get webhook source: ${message2}`);
|
|
150646
150844
|
}
|
|
150647
150845
|
});
|
|
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) => {
|
|
150846
|
+
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
150847
|
const client = getClient();
|
|
150650
150848
|
try {
|
|
150651
150849
|
let expectedHeaders;
|
|
@@ -150665,7 +150863,10 @@ function createWebhooksCommand() {
|
|
|
150665
150863
|
enabled: !options3.disabled,
|
|
150666
150864
|
expectedHeaders,
|
|
150667
150865
|
signatureConfig,
|
|
150668
|
-
signatureSecret
|
|
150866
|
+
signatureSecret,
|
|
150867
|
+
expectedIntervalSeconds: parseExpectedInterval(options3.expectedInterval),
|
|
150868
|
+
windowSemantics: parseWindowSemantics(options3.windowSemantics),
|
|
150869
|
+
mutationPolicy: parseMutationPolicy(options3.mutationPolicy)
|
|
150669
150870
|
});
|
|
150670
150871
|
const details = {
|
|
150671
150872
|
id: source.id,
|
|
@@ -150681,11 +150882,11 @@ function createWebhooksCommand() {
|
|
|
150681
150882
|
error(`Failed to create webhook source: ${message2}`);
|
|
150682
150883
|
}
|
|
150683
150884
|
});
|
|
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) => {
|
|
150885
|
+
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
150886
|
const resolvedId = await resolveWebhookId(id);
|
|
150686
150887
|
const client = getClient();
|
|
150687
150888
|
try {
|
|
150688
|
-
const updates =
|
|
150889
|
+
const updates = buildLifecycleUpdates(options3);
|
|
150689
150890
|
if (options3.name)
|
|
150690
150891
|
updates.name = options3.name;
|
|
150691
150892
|
if (options3.description)
|
|
@@ -150751,6 +150952,20 @@ function createWebhooksCommand() {
|
|
|
150751
150952
|
error(`Failed to trigger event: ${message2}`);
|
|
150752
150953
|
}
|
|
150753
150954
|
});
|
|
150955
|
+
webhooks.command("heartbeat <source>").description('Record a connector heartbeat ("ran, zero events found") \u2014 resets the liveness window (#961)').action(async (source) => {
|
|
150956
|
+
const client = getClient();
|
|
150957
|
+
try {
|
|
150958
|
+
const result = await client.webhooks.heartbeat(source);
|
|
150959
|
+
success(`Heartbeat recorded for '${result.source}'`, {
|
|
150960
|
+
heartbeatAt: result.heartbeatAt,
|
|
150961
|
+
livenessStatus: result.livenessStatus ?? "unsupervised",
|
|
150962
|
+
expectedIntervalSeconds: result.expectedIntervalSeconds
|
|
150963
|
+
});
|
|
150964
|
+
} catch (err2) {
|
|
150965
|
+
const message2 = err2 instanceof Error ? err2.message : "Unknown error";
|
|
150966
|
+
error(`Failed to record heartbeat: ${message2}`);
|
|
150967
|
+
}
|
|
150968
|
+
});
|
|
150754
150969
|
return webhooks;
|
|
150755
150970
|
}
|
|
150756
150971
|
|
package/dist/sdk/client.d.ts
CHANGED
|
@@ -663,7 +663,22 @@ export interface CreateWebhookSourceBody {
|
|
|
663
663
|
signatureSecret?: string | null;
|
|
664
664
|
/** Defaults to true server-side */
|
|
665
665
|
enabled?: boolean;
|
|
666
|
+
/**
|
|
667
|
+
* Connector lifecycle contract (#961): declared cadence — the connector
|
|
668
|
+
* promises >=1 event or heartbeat per N seconds (1s-30d). Declaring it arms
|
|
669
|
+
* liveness supervision; null disarms it.
|
|
670
|
+
*/
|
|
671
|
+
expectedIntervalSeconds?: number | null;
|
|
672
|
+
/** Declared window semantics; null = undeclared. */
|
|
673
|
+
windowSemantics?: 'future_only' | 'includes_in_progress' | null;
|
|
674
|
+
/** Declared upstream-mutation re-emit policy; null = undeclared. */
|
|
675
|
+
mutationPolicy?: 'same_id' | 'new_id' | null;
|
|
666
676
|
}
|
|
677
|
+
/**
|
|
678
|
+
* Response of the connector heartbeat verb (#961).
|
|
679
|
+
* Derived from the generated OpenAPI component so it cannot drift from the API.
|
|
680
|
+
*/
|
|
681
|
+
export type WebhookHeartbeatResponse = components['schemas']['WebhookHeartbeatResponse'];
|
|
667
682
|
/**
|
|
668
683
|
* Body for triggering a custom event
|
|
669
684
|
*/
|
|
@@ -1869,6 +1884,12 @@ export declare function createOmniClient(config: OmniClientConfig): {
|
|
|
1869
1884
|
eventId: string;
|
|
1870
1885
|
eventType: string;
|
|
1871
1886
|
}>;
|
|
1887
|
+
/**
|
|
1888
|
+
* Record a connector heartbeat: "I ran, zero events found" (#961).
|
|
1889
|
+
* Resets the source's liveness window without creating a journal event.
|
|
1890
|
+
* Keyed on the source NAME (like the receiver), not the id.
|
|
1891
|
+
*/
|
|
1892
|
+
heartbeat(source: string): Promise<WebhookHeartbeatResponse>;
|
|
1872
1893
|
};
|
|
1873
1894
|
/**
|
|
1874
1895
|
* Payload management
|