@automagik/omni 2.260911.1 → 2.260911.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/0067_automation_concurrency.sql +34 -0
- package/db/drizzle/meta/_journal.json +7 -0
- package/dist/commands/automations.d.ts +2 -0
- package/dist/commands/automations.d.ts.map +1 -1
- package/dist/index.js +43 -24
- package/dist/sdk/client.d.ts +12 -0
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/types.generated.d.ts +141 -2
- package/dist/sdk/types.generated.d.ts.map +1 -1
- package/dist/server/index.js +55 -24
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -38426,7 +38426,7 @@ var init_debounce = __esm(() => {
|
|
|
38426
38426
|
class AutomationEngine {
|
|
38427
38427
|
config;
|
|
38428
38428
|
subscriptions = new Map;
|
|
38429
|
-
|
|
38429
|
+
queues = new Map;
|
|
38430
38430
|
debounceManagers = new Map;
|
|
38431
38431
|
automations = [];
|
|
38432
38432
|
eventBus = null;
|
|
@@ -38751,29 +38751,38 @@ class AutomationEngine {
|
|
|
38751
38751
|
const manager = new DebounceManager(automation.debounce, callback);
|
|
38752
38752
|
this.debounceManagers.set(automation.id, manager);
|
|
38753
38753
|
}
|
|
38754
|
+
resolveQueueKey(automation, context2, instanceId) {
|
|
38755
|
+
if (automation.maxConcurrency == null && automation.concurrencyKey == null)
|
|
38756
|
+
return instanceId;
|
|
38757
|
+
const base = `${instanceId}:${automation.id}`;
|
|
38758
|
+
if (!automation.concurrencyKey)
|
|
38759
|
+
return base;
|
|
38760
|
+
const partition = substituteTemplate(automation.concurrencyKey, context2).trim();
|
|
38761
|
+
return partition ? `${base}:${partition}` : base;
|
|
38762
|
+
}
|
|
38754
38763
|
async queueExecution(automation, event, context2, instanceId) {
|
|
38755
|
-
|
|
38756
|
-
|
|
38757
|
-
|
|
38758
|
-
|
|
38759
|
-
|
|
38760
|
-
|
|
38761
|
-
|
|
38762
|
-
|
|
38763
|
-
|
|
38764
|
-
|
|
38765
|
-
|
|
38764
|
+
const key = this.resolveQueueKey(automation, context2, instanceId);
|
|
38765
|
+
const maxConcurrency = automation.maxConcurrency ?? this.config.instanceConcurrencyOverrides?.[instanceId] ?? this.config.defaultConcurrency;
|
|
38766
|
+
const queue = this.queues.get(key) ?? {
|
|
38767
|
+
key,
|
|
38768
|
+
evictWhenIdle: key !== instanceId,
|
|
38769
|
+
activeCount: 0,
|
|
38770
|
+
maxConcurrency,
|
|
38771
|
+
pending: []
|
|
38772
|
+
};
|
|
38773
|
+
queue.maxConcurrency = maxConcurrency;
|
|
38774
|
+
this.queues.set(key, queue);
|
|
38766
38775
|
if (queue.activeCount < queue.maxConcurrency) {
|
|
38767
38776
|
return this.executeAutomation(automation, event, context2, queue);
|
|
38768
38777
|
}
|
|
38769
38778
|
const maxQueueDepth = this.config.maxQueueDepth ?? 100;
|
|
38770
38779
|
if (queue.pending.length >= maxQueueDepth) {
|
|
38771
38780
|
logger4.warn("Queue full, applying backpressure", {
|
|
38772
|
-
|
|
38781
|
+
queueKey: key,
|
|
38773
38782
|
queueDepth: queue.pending.length,
|
|
38774
38783
|
maxDepth: maxQueueDepth
|
|
38775
38784
|
});
|
|
38776
|
-
throw new QueueFullError(
|
|
38785
|
+
throw new QueueFullError(key, queue.pending.length, maxQueueDepth);
|
|
38777
38786
|
}
|
|
38778
38787
|
const queueRef = queue;
|
|
38779
38788
|
return new Promise((resolve) => {
|
|
@@ -38785,7 +38794,7 @@ class AutomationEngine {
|
|
|
38785
38794
|
});
|
|
38786
38795
|
logger4.debug("Execution queued", {
|
|
38787
38796
|
automationId: automation.id,
|
|
38788
|
-
|
|
38797
|
+
queueKey: key,
|
|
38789
38798
|
queueLength: queueRef.pending.length
|
|
38790
38799
|
});
|
|
38791
38800
|
});
|
|
@@ -38892,6 +38901,10 @@ class AutomationEngine {
|
|
|
38892
38901
|
next.resolve(result);
|
|
38893
38902
|
}
|
|
38894
38903
|
}
|
|
38904
|
+
if (queue.evictWhenIdle && queue.activeCount === 0 && queue.pending.length === 0) {
|
|
38905
|
+
if (this.queues.get(queue.key) === queue)
|
|
38906
|
+
this.queues.delete(queue.key);
|
|
38907
|
+
}
|
|
38895
38908
|
}
|
|
38896
38909
|
}
|
|
38897
38910
|
async logExecution(result, trustedTenantId = null) {
|
|
@@ -38917,8 +38930,8 @@ class AutomationEngine {
|
|
|
38917
38930
|
});
|
|
38918
38931
|
}
|
|
38919
38932
|
getMetrics() {
|
|
38920
|
-
const instanceQueues = Array.from(this.
|
|
38921
|
-
instanceId: q.
|
|
38933
|
+
const instanceQueues = Array.from(this.queues.values()).map((q) => ({
|
|
38934
|
+
instanceId: q.key,
|
|
38922
38935
|
activeCount: q.activeCount,
|
|
38923
38936
|
pendingCount: q.pending.length
|
|
38924
38937
|
}));
|
|
@@ -38961,12 +38974,12 @@ var init_engine = __esm(() => {
|
|
|
38961
38974
|
init_templates();
|
|
38962
38975
|
logger4 = createLogger("automations:engine");
|
|
38963
38976
|
QueueFullError = class QueueFullError extends Error {
|
|
38964
|
-
|
|
38977
|
+
queueKey;
|
|
38965
38978
|
queueDepth;
|
|
38966
38979
|
maxDepth;
|
|
38967
|
-
constructor(
|
|
38968
|
-
super(`Queue full for
|
|
38969
|
-
this.
|
|
38980
|
+
constructor(queueKey, queueDepth, maxDepth) {
|
|
38981
|
+
super(`Queue full for ${queueKey}: ${queueDepth}/${maxDepth} pending`);
|
|
38982
|
+
this.queueKey = queueKey;
|
|
38970
38983
|
this.queueDepth = queueDepth;
|
|
38971
38984
|
this.maxDepth = maxDepth;
|
|
38972
38985
|
this.name = "QueueFullError";
|
|
@@ -174411,6 +174424,8 @@ var init_schema2 = __esm(() => {
|
|
|
174411
174424
|
enabled: boolean2("enabled").notNull().default(true),
|
|
174412
174425
|
priority: integer("priority").notNull().default(0),
|
|
174413
174426
|
transactionalEmissions: boolean2("transactional_emissions").notNull().default(false),
|
|
174427
|
+
maxConcurrency: integer("max_concurrency"),
|
|
174428
|
+
concurrencyKey: text("concurrency_key"),
|
|
174414
174429
|
managedByAgentId: uuid("managed_by_agent_id").references(() => agents.id, { onDelete: "cascade" }),
|
|
174415
174430
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
174416
174431
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
@@ -251657,7 +251672,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
251657
251672
|
var require_package7 = __commonJS((exports, module) => {
|
|
251658
251673
|
module.exports = {
|
|
251659
251674
|
name: "@omni/api",
|
|
251660
|
-
version: "2.260911.
|
|
251675
|
+
version: "2.260911.3",
|
|
251661
251676
|
private: true,
|
|
251662
251677
|
type: "module",
|
|
251663
251678
|
exports: {
|
|
@@ -379382,6 +379397,12 @@ var init_automations3 = __esm(() => {
|
|
|
379382
379397
|
transactionalEmissions: exports_external.boolean().openapi({
|
|
379383
379398
|
description: "Transactional publication (G5, #988): buffer the run's emit_event publishes and flush them in order " + "only when every action succeeded; a failed run publishes zero"
|
|
379384
379399
|
}),
|
|
379400
|
+
maxConcurrency: exports_external.number().int().nullable().openapi({
|
|
379401
|
+
description: "Per-automation concurrency limit (#1108). null = queued per instance with the engine default; " + "set = a queue private to this automation, 1 being strict single-flight"
|
|
379402
|
+
}),
|
|
379403
|
+
concurrencyKey: exports_external.string().nullable().openapi({
|
|
379404
|
+
description: "Template over the event payload partitioning this automation\u2019s queue (#1108), e.g. " + '"{{payload.from.id}}" to serialize per chat; null = one queue for the whole automation'
|
|
379405
|
+
}),
|
|
379385
379406
|
managedByAgentId: exports_external.string().uuid().nullable().openapi({
|
|
379386
379407
|
description: "Set when this automation was compiled from an agent event manifest (RFC #925 G4b, #986); " + "null = hand-made. Managed automations reject manual mutation \u2014 edit the owning agent\u2019s manifest instead"
|
|
379387
379408
|
}),
|
|
@@ -379400,6 +379421,12 @@ var init_automations3 = __esm(() => {
|
|
|
379400
379421
|
priority: exports_external.number().int().default(0).openapi({ description: "Priority" }),
|
|
379401
379422
|
transactionalEmissions: exports_external.boolean().default(false).openapi({
|
|
379402
379423
|
description: "Transactional publication (G5, #988): buffer the run's emit_event publishes and flush them in order " + "only when every action succeeded; a failed run publishes zero. Default false = immediate publishing"
|
|
379424
|
+
}),
|
|
379425
|
+
maxConcurrency: exports_external.number().int().min(1).nullable().optional().openapi({
|
|
379426
|
+
description: "Per-automation concurrency limit (#1108). Omit/null = today\u2019s per-instance queueing with the engine " + "default; 1 = strict single-flight, which is what a read-before-write action needs"
|
|
379427
|
+
}),
|
|
379428
|
+
concurrencyKey: exports_external.string().min(1).nullable().optional().openapi({
|
|
379429
|
+
description: "Template over the event payload partitioning this automation\u2019s queue (#1108), e.g. " + '"{{payload.from.id}}" to serialize per chat; omit/null = one queue for the whole automation'
|
|
379403
379430
|
})
|
|
379404
379431
|
});
|
|
379405
379432
|
AutomationLogSchema = exports_external.object({
|
|
@@ -379445,7 +379472,9 @@ var init_automations3 = __esm(() => {
|
|
|
379445
379472
|
AutomationMetricsSchema = exports_external.object({
|
|
379446
379473
|
running: exports_external.boolean().openapi({ description: "Engine running" }),
|
|
379447
379474
|
instanceQueues: exports_external.array(exports_external.object({
|
|
379448
|
-
instanceId: exports_external.string().openapi({
|
|
379475
|
+
instanceId: exports_external.string().openapi({
|
|
379476
|
+
description: 'Queue key: the instance ID, or "<instanceId>:<automationId>[:<partition>]" for an automation ' + "running on its own queue (#1108)"
|
|
379477
|
+
}),
|
|
379449
379478
|
activeCount: exports_external.number().int().openapi({ description: "Active jobs" }),
|
|
379450
379479
|
pendingCount: exports_external.number().int().openapi({ description: "Pending jobs" })
|
|
379451
379480
|
})).optional().openapi({ description: "Instance queue stats" }),
|
|
@@ -385197,7 +385226,9 @@ var init_automations4 = __esm(() => {
|
|
|
385197
385226
|
debounce: debounceSchema.optional().describe("Message debounce configuration"),
|
|
385198
385227
|
enabled: exports_external.boolean().default(true).describe("Whether automation is enabled"),
|
|
385199
385228
|
priority: exports_external.number().int().default(0).describe("Priority (higher runs first)"),
|
|
385200
|
-
transactionalEmissions: exports_external.boolean().default(false).describe("Transactional publication (G5, #988): buffer the run's emit_event publishes and flush them in order " + "only when every action succeeded; a failed run publishes zero. Default false = immediate publishing")
|
|
385229
|
+
transactionalEmissions: exports_external.boolean().default(false).describe("Transactional publication (G5, #988): buffer the run's emit_event publishes and flush them in order " + "only when every action succeeded; a failed run publishes zero. Default false = immediate publishing"),
|
|
385230
|
+
maxConcurrency: exports_external.number().int().min(1).nullable().optional().describe("Per-automation concurrency limit (#1108). Omit/null = today\u2019s per-instance queueing with the engine " + "default; 1 = strict single-flight, which is what a read-before-write action needs"),
|
|
385231
|
+
concurrencyKey: exports_external.string().min(1).nullable().optional().describe("Template over the event payload partitioning this automation\u2019s queue (#1108), e.g. " + '"{{payload.from.id}}" to serialize per chat; omit/null = one queue for the whole automation')
|
|
385201
385232
|
});
|
|
385202
385233
|
updateAutomationSchema = createAutomationSchema.partial();
|
|
385203
385234
|
listQuerySchema5 = exports_external.object({
|