@automagik/omni 2.260911.3 → 2.260911.5
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/commands/webhooks.d.ts +1 -1
- package/dist/commands/webhooks.d.ts.map +1 -1
- package/dist/index.js +29 -7
- package/dist/sdk/client.d.ts +9 -1
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/types.generated.d.ts +24 -0
- package/dist/sdk/types.generated.d.ts.map +1 -1
- package/dist/server/index.js +118 -26
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -38333,6 +38333,8 @@ class DebounceManager {
|
|
|
38333
38333
|
this.windows.set(key, window2);
|
|
38334
38334
|
}
|
|
38335
38335
|
window2.messages.push(message2);
|
|
38336
|
+
window2.from = from;
|
|
38337
|
+
window2.instanceId = instanceId;
|
|
38336
38338
|
window2.lastActivityAt = Date.now();
|
|
38337
38339
|
this.resetTimer(key, window2);
|
|
38338
38340
|
}
|
|
@@ -38667,6 +38669,23 @@ class AutomationEngine {
|
|
|
38667
38669
|
throw err2;
|
|
38668
38670
|
}
|
|
38669
38671
|
}
|
|
38672
|
+
resolveDebounceKey(automation, event, instanceId) {
|
|
38673
|
+
const payload = event.payload;
|
|
38674
|
+
const template = automation.debounce?.key;
|
|
38675
|
+
if (!template) {
|
|
38676
|
+
const from = payload.from;
|
|
38677
|
+
return from?.id ? buildConversationKey(instanceId, from.id) : null;
|
|
38678
|
+
}
|
|
38679
|
+
const rendered = substituteTemplate(template, createTemplateContext(payload)).trim();
|
|
38680
|
+
if (!rendered) {
|
|
38681
|
+
logger4.debug("Debounce key template rendered empty \u2014 processing immediately", {
|
|
38682
|
+
automationId: automation.id,
|
|
38683
|
+
eventId: event.id
|
|
38684
|
+
});
|
|
38685
|
+
return null;
|
|
38686
|
+
}
|
|
38687
|
+
return `${instanceId}:${rendered}`;
|
|
38688
|
+
}
|
|
38670
38689
|
async handleDebounced(automation, event) {
|
|
38671
38690
|
const manager = this.debounceManagers.get(automation.id);
|
|
38672
38691
|
if (!manager) {
|
|
@@ -38676,11 +38695,11 @@ class AutomationEngine {
|
|
|
38676
38695
|
const payload = event.payload;
|
|
38677
38696
|
const instanceId = event.metadata.instanceId ?? "global";
|
|
38678
38697
|
const from = payload.from;
|
|
38679
|
-
|
|
38698
|
+
const key = this.resolveDebounceKey(automation, event, instanceId);
|
|
38699
|
+
if (!key) {
|
|
38680
38700
|
await this.handleImmediate(automation, event);
|
|
38681
38701
|
return;
|
|
38682
38702
|
}
|
|
38683
|
-
const key = buildConversationKey(instanceId, from.id);
|
|
38684
38703
|
if (event.type.startsWith("presence.")) {
|
|
38685
38704
|
manager.handlePresenceEvent(key, event.type);
|
|
38686
38705
|
return;
|
|
@@ -251672,7 +251691,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
251672
251691
|
var require_package7 = __commonJS((exports, module) => {
|
|
251673
251692
|
module.exports = {
|
|
251674
251693
|
name: "@omni/api",
|
|
251675
|
-
version: "2.260911.
|
|
251694
|
+
version: "2.260911.5",
|
|
251676
251695
|
private: true,
|
|
251677
251696
|
type: "module",
|
|
251678
251697
|
exports: {
|
|
@@ -375856,6 +375875,9 @@ var init_ingress_idempotency = __esm(() => {
|
|
|
375856
375875
|
|
|
375857
375876
|
// ../api/src/services/webhooks.ts
|
|
375858
375877
|
import { createHmac as createHmac4, timingSafeEqual } from "crypto";
|
|
375878
|
+
function scopeTriggerKey(key, instanceId, tenantId) {
|
|
375879
|
+
return `manual:${instanceId ?? tenantId ?? "global"}:${key}`;
|
|
375880
|
+
}
|
|
375859
375881
|
function timingSafeEqualStrings(a, b3) {
|
|
375860
375882
|
const bufA = Buffer.from(a);
|
|
375861
375883
|
const bufB = Buffer.from(b3);
|
|
@@ -376026,7 +376048,7 @@ class WebhookService {
|
|
|
376026
376048
|
payload,
|
|
376027
376049
|
headers
|
|
376028
376050
|
});
|
|
376029
|
-
const
|
|
376051
|
+
const claim = await this.claimJournalRow({
|
|
376030
376052
|
id: eventId,
|
|
376031
376053
|
channel: "internal",
|
|
376032
376054
|
eventType,
|
|
@@ -376036,18 +376058,17 @@ class WebhookService {
|
|
|
376036
376058
|
idempotencyKey,
|
|
376037
376059
|
receivedAt: new Date,
|
|
376038
376060
|
metadata: { correlationId: eventId, source: "webhook", fullEventType: eventType, webhookSource: sourceName }
|
|
376039
|
-
})
|
|
376040
|
-
if (
|
|
376061
|
+
});
|
|
376062
|
+
if (claim.duplicate) {
|
|
376041
376063
|
await this.db.update(webhookSources).set({
|
|
376042
376064
|
totalDuplicates: sql`${webhookSources.totalDuplicates} + 1`,
|
|
376043
376065
|
updatedAt: new Date
|
|
376044
376066
|
}).where(eq(webhookSources.id, source.id));
|
|
376045
|
-
const [original] = await this.db.select({ id: omniEvents.id }).from(omniEvents).where(eq(omniEvents.idempotencyKey, idempotencyKey)).limit(1);
|
|
376046
376067
|
log114.info("Webhook redelivery acked without a second event", { sourceName, idempotencyKey });
|
|
376047
376068
|
return {
|
|
376048
376069
|
received: true,
|
|
376049
376070
|
duplicate: true,
|
|
376050
|
-
eventId:
|
|
376071
|
+
eventId: claim.eventId,
|
|
376051
376072
|
source: sourceName,
|
|
376052
376073
|
eventType
|
|
376053
376074
|
};
|
|
@@ -376082,6 +376103,14 @@ class WebhookService {
|
|
|
376082
376103
|
eventType
|
|
376083
376104
|
};
|
|
376084
376105
|
}
|
|
376106
|
+
async claimJournalRow(row) {
|
|
376107
|
+
const claimed = await this.db.insert(omniEvents).values(row).onConflictDoNothing({ target: omniEvents.idempotencyKey }).returning({ id: omniEvents.id });
|
|
376108
|
+
if (claimed.length > 0) {
|
|
376109
|
+
return { eventId: row.id, duplicate: false };
|
|
376110
|
+
}
|
|
376111
|
+
const [original] = await this.db.select({ id: omniEvents.id }).from(omniEvents).where(eq(omniEvents.idempotencyKey, row.idempotencyKey)).limit(1);
|
|
376112
|
+
return { eventId: original?.id ?? row.id, duplicate: true };
|
|
376113
|
+
}
|
|
376085
376114
|
verifySignature(source, rawBody, headers) {
|
|
376086
376115
|
const config4 = source.signatureConfig;
|
|
376087
376116
|
if (!config4)
|
|
@@ -376145,16 +376174,66 @@ class WebhookService {
|
|
|
376145
376174
|
async trigger(eventType, payload, metadata) {
|
|
376146
376175
|
const provisionalId = metadata?.correlationId ?? generateId();
|
|
376147
376176
|
await this.enforceRegisteredSchema(eventType, payload, provisionalId, "manual trigger");
|
|
376148
|
-
|
|
376149
|
-
|
|
376150
|
-
|
|
376151
|
-
|
|
376152
|
-
|
|
376177
|
+
const bus2 = this.eventBus;
|
|
376178
|
+
if (!bus2) {
|
|
376179
|
+
return { eventId: provisionalId, published: false };
|
|
376180
|
+
}
|
|
376181
|
+
const key = metadata?.idempotencyKey;
|
|
376182
|
+
if (key) {
|
|
376183
|
+
return this.triggerClaimed(bus2, eventType, payload, metadata ?? {}, key);
|
|
376184
|
+
}
|
|
376185
|
+
const result = await bus2.publishGeneric(eventType, payload, {
|
|
376186
|
+
correlationId: metadata?.correlationId,
|
|
376187
|
+
causationId: metadata?.causationId,
|
|
376188
|
+
instanceId: metadata?.instanceId,
|
|
376189
|
+
source: "manual-trigger"
|
|
376190
|
+
});
|
|
376191
|
+
return { eventId: result.id, published: true };
|
|
376192
|
+
}
|
|
376193
|
+
async triggerClaimed(bus2, eventType, payload, metadata, key) {
|
|
376194
|
+
const idempotencyKey = scopeTriggerKey(key, metadata.instanceId, this.tenantId);
|
|
376195
|
+
const eventId = generateId();
|
|
376196
|
+
const claim = await this.claimJournalRow({
|
|
376197
|
+
id: eventId,
|
|
376198
|
+
channel: "internal",
|
|
376199
|
+
eventType,
|
|
376200
|
+
direction: "internal",
|
|
376201
|
+
status: "completed",
|
|
376202
|
+
rawPayload: payload,
|
|
376203
|
+
idempotencyKey,
|
|
376204
|
+
causationId: metadata.causationId ?? null,
|
|
376205
|
+
receivedAt: new Date,
|
|
376206
|
+
metadata: {
|
|
376207
|
+
correlationId: metadata.correlationId ?? eventId,
|
|
376208
|
+
source: "manual-trigger",
|
|
376209
|
+
fullEventType: eventType,
|
|
376210
|
+
idempotencyKey
|
|
376211
|
+
}
|
|
376212
|
+
});
|
|
376213
|
+
if (claim.duplicate) {
|
|
376214
|
+
log114.info("Manual trigger deduped: idempotency key already journaled", { eventType, idempotencyKey });
|
|
376215
|
+
return { eventId: claim.eventId, published: false, duplicate: true };
|
|
376216
|
+
}
|
|
376217
|
+
try {
|
|
376218
|
+
await bus2.publishGeneric(eventType, payload, {
|
|
376219
|
+
correlationId: metadata.correlationId,
|
|
376220
|
+
causationId: metadata.causationId,
|
|
376221
|
+
instanceId: metadata.instanceId,
|
|
376222
|
+
publishEventId: eventId,
|
|
376153
376223
|
source: "manual-trigger"
|
|
376154
376224
|
});
|
|
376155
|
-
|
|
376225
|
+
} catch (error3) {
|
|
376226
|
+
await this.db.delete(omniEvents).where(eq(omniEvents.id, eventId)).catch((releaseError) => {
|
|
376227
|
+
log114.error("Failed to release trigger idempotency claim after publish failure", {
|
|
376228
|
+
eventType,
|
|
376229
|
+
eventId,
|
|
376230
|
+
idempotencyKey,
|
|
376231
|
+
error: String(releaseError)
|
|
376232
|
+
});
|
|
376233
|
+
});
|
|
376234
|
+
throw error3;
|
|
376156
376235
|
}
|
|
376157
|
-
return { eventId:
|
|
376236
|
+
return { eventId, published: true, duplicate: false };
|
|
376158
376237
|
}
|
|
376159
376238
|
async heartbeat(sourceName) {
|
|
376160
376239
|
const source = await this.getByName(sourceName);
|
|
@@ -379381,7 +379460,8 @@ var init_automations3 = __esm(() => {
|
|
|
379381
379460
|
maxMs: exports_external.number().int().optional(),
|
|
379382
379461
|
baseDelayMs: exports_external.number().int().optional(),
|
|
379383
379462
|
maxWaitMs: exports_external.number().int().optional(),
|
|
379384
|
-
extendOnEvents: exports_external.array(exports_external.string()).optional()
|
|
379463
|
+
extendOnEvents: exports_external.array(exports_external.string()).optional(),
|
|
379464
|
+
key: exports_external.string().optional().describe('Template over the event payload naming the debounce window (#1110), e.g. "{{payload.pull_request.id}}"; ' + 'absent = grouped by conversation (instance + sender). Not allowed with mode "presence"')
|
|
379385
379465
|
});
|
|
379386
379466
|
AutomationSchema = exports_external.object({
|
|
379387
379467
|
id: exports_external.string().uuid().openapi({ description: "Automation UUID" }),
|
|
@@ -383545,7 +383625,10 @@ var init_webhooks2 = __esm(() => {
|
|
|
383545
383625
|
payload: exports_external.record(exports_external.string(), exports_external.unknown()).openapi({ description: "Event payload" }),
|
|
383546
383626
|
correlationId: exports_external.string().optional().openapi({ description: "Correlation ID" }),
|
|
383547
383627
|
causationId: exports_external.string().uuid().optional().openapi({ description: "Parent event ID; stamps causationId so the emission is parented in the causality tree" }),
|
|
383548
|
-
instanceId: exports_external.string().uuid().optional().openapi({ description: "Instance ID for context" })
|
|
383628
|
+
instanceId: exports_external.string().uuid().optional().openapi({ description: "Instance ID for context" }),
|
|
383629
|
+
idempotencyKey: exports_external.string().min(1).max(255).optional().openapi({
|
|
383630
|
+
description: "Producer-supplied ingress key (#1109). Unique per instance (else per tenant), not global. A key already journaled publishes nothing and returns the ORIGINAL event id with duplicate: true. Omit for undeduped publishing."
|
|
383631
|
+
})
|
|
383549
383632
|
});
|
|
383550
383633
|
WebhookReceiveResponseSchema = exports_external.object({
|
|
383551
383634
|
eventId: exports_external.string().uuid().openapi({ description: "Created event ID (the ORIGINAL event on a duplicate)" }),
|
|
@@ -385132,7 +385215,7 @@ async function resolveTestEvent(services, body) {
|
|
|
385132
385215
|
timestamp: row.receivedAt.getTime()
|
|
385133
385216
|
};
|
|
385134
385217
|
}
|
|
385135
|
-
var automationsRoutes, conditionSchema, webhookActionSchema, sendMessageActionSchema, emitEventActionSchema, logActionSchema, callAgentActionSchema, actionSchema, debounceSchema, createAutomationSchema, updateAutomationSchema, listQuerySchema5, testAutomationSchema, logsQuerySchema;
|
|
385218
|
+
var automationsRoutes, conditionSchema, webhookActionSchema, sendMessageActionSchema, emitEventActionSchema, logActionSchema, callAgentActionSchema, actionSchema, debounceKeySchema, debounceSchema, createAutomationSchema, updateAutomationSchema, listQuerySchema5, testAutomationSchema, logsQuerySchema;
|
|
385136
385219
|
var init_automations4 = __esm(() => {
|
|
385137
385220
|
init_dist6();
|
|
385138
385221
|
init_dist2();
|
|
@@ -385198,24 +385281,31 @@ var init_automations4 = __esm(() => {
|
|
|
385198
385281
|
logActionSchema,
|
|
385199
385282
|
callAgentActionSchema
|
|
385200
385283
|
]);
|
|
385284
|
+
debounceKeySchema = exports_external.string().min(1).optional().describe('Template over the event payload naming the debounce window (#1110), e.g. "{{payload.pull_request.id}}"; ' + 'omit to group by conversation (instance + sender) as before. Not allowed with mode "presence"');
|
|
385201
385285
|
debounceSchema = exports_external.discriminatedUnion("mode", [
|
|
385202
|
-
exports_external.object({ mode: exports_external.literal("none") }),
|
|
385286
|
+
exports_external.object({ mode: exports_external.literal("none"), key: debounceKeySchema }),
|
|
385203
385287
|
exports_external.object({
|
|
385204
385288
|
mode: exports_external.literal("fixed"),
|
|
385205
|
-
delayMs: exports_external.number().int().min(100).max(300000).describe("Fixed delay in milliseconds")
|
|
385289
|
+
delayMs: exports_external.number().int().min(100).max(300000).describe("Fixed delay in milliseconds"),
|
|
385290
|
+
key: debounceKeySchema
|
|
385206
385291
|
}),
|
|
385207
385292
|
exports_external.object({
|
|
385208
385293
|
mode: exports_external.literal("range"),
|
|
385209
385294
|
minMs: exports_external.number().int().min(100).describe("Minimum delay"),
|
|
385210
|
-
maxMs: exports_external.number().int().max(300000).describe("Maximum delay")
|
|
385295
|
+
maxMs: exports_external.number().int().max(300000).describe("Maximum delay"),
|
|
385296
|
+
key: debounceKeySchema
|
|
385211
385297
|
}),
|
|
385212
385298
|
exports_external.object({
|
|
385213
385299
|
mode: exports_external.literal("presence"),
|
|
385214
385300
|
baseDelayMs: exports_external.number().int().min(100).describe("Base delay"),
|
|
385215
385301
|
maxWaitMs: exports_external.number().int().max(300000).optional().describe("Maximum total wait"),
|
|
385216
|
-
extendOnEvents: exports_external.array(exports_external.string()).describe("Events that extend the timer")
|
|
385302
|
+
extendOnEvents: exports_external.array(exports_external.string()).describe("Events that extend the timer"),
|
|
385303
|
+
key: debounceKeySchema
|
|
385217
385304
|
})
|
|
385218
|
-
])
|
|
385305
|
+
]).refine((debounce3) => !(debounce3.mode === "presence" && debounce3.key), {
|
|
385306
|
+
message: 'debounce.key cannot be combined with mode "presence": presence extension only applies to conversations',
|
|
385307
|
+
path: ["key"]
|
|
385308
|
+
});
|
|
385219
385309
|
createAutomationSchema = exports_external.object({
|
|
385220
385310
|
name: exports_external.string().min(1).max(255).describe("Automation name"),
|
|
385221
385311
|
description: exports_external.string().optional().describe("Description"),
|
|
@@ -396356,10 +396446,11 @@ var init_webhooks3 = __esm(() => {
|
|
|
396356
396446
|
payload: exports_external.record(exports_external.string(), exports_external.unknown()).describe("Event payload"),
|
|
396357
396447
|
correlationId: exports_external.string().optional().describe("Optional correlation ID"),
|
|
396358
396448
|
causationId: exports_external.string().uuid().optional().describe("Optional parent event ID (causality tree, #1072)"),
|
|
396359
|
-
instanceId: exports_external.string().uuid().optional().describe("Optional instance ID for context")
|
|
396449
|
+
instanceId: exports_external.string().uuid().optional().describe("Optional instance ID for context"),
|
|
396450
|
+
idempotencyKey: exports_external.string().min(1).max(255).optional().describe("Optional producer-supplied ingress key; a key already journaled returns the original event (#1109)")
|
|
396360
396451
|
});
|
|
396361
396452
|
webhooksRoutes.post("/events/trigger", zValidator("json", triggerEventSchema), async (c) => {
|
|
396362
|
-
const { eventType, payload, correlationId, causationId, instanceId } = c.req.valid("json");
|
|
396453
|
+
const { eventType, payload, correlationId, causationId, instanceId, idempotencyKey } = c.req.valid("json");
|
|
396363
396454
|
const services = c.get("services");
|
|
396364
396455
|
const apiKey = c.get("apiKey");
|
|
396365
396456
|
if (instanceId && apiKey && !ApiKeyService.instanceAllowed(apiKey.instanceIds, instanceId)) {
|
|
@@ -396368,7 +396459,8 @@ var init_webhooks3 = __esm(() => {
|
|
|
396368
396459
|
const result = await services.webhooks.trigger(eventType, payload, {
|
|
396369
396460
|
correlationId,
|
|
396370
396461
|
causationId,
|
|
396371
|
-
instanceId
|
|
396462
|
+
instanceId,
|
|
396463
|
+
idempotencyKey
|
|
396372
396464
|
});
|
|
396373
396465
|
return c.json(result, 201);
|
|
396374
396466
|
});
|