@automagik/omni 2.260430.12 → 2.260430.13
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.
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- Backfill: chats that auto-promoted to a hard terminal under the old
|
|
2
|
+
-- `redirected_sac` escalation rule (escalationThreshold=2 over 7 days) get
|
|
3
|
+
-- `chats.settings.closed` flipped back to false. Companion to the config
|
|
4
|
+
-- fix in `packages/api/src/routes/v2/_close-contact-config.ts` which drops
|
|
5
|
+
-- the escalation going forward — an active customer ending every
|
|
6
|
+
-- conversation with a support-channel redirect is normal account activity,
|
|
7
|
+
-- not a signal worth silencing the reactive agent over.
|
|
8
|
+
--
|
|
9
|
+
-- `closeUntil`, `closeOutcome`, and `close_contact_logs` are deliberately
|
|
10
|
+
-- left intact so reporting and downstream BI consumers see no change.
|
|
11
|
+
-- Hard terminals from `won`/`lost` outcomes are not touched.
|
|
12
|
+
--
|
|
13
|
+
-- Hand-written (not `drizzle-kit generate`) because this is a JSONB merge
|
|
14
|
+
-- with a filter predicate that `drizzle-kit` cannot emit directly.
|
|
15
|
+
--
|
|
16
|
+
-- Performance note: there is no index on `chats.settings->>'closed'` or
|
|
17
|
+
-- `closeOutcome`, so the predicate is evaluated via a sequential scan.
|
|
18
|
+
-- The expected blast radius is small (only chats that previously hit
|
|
19
|
+
-- `redirected_sac` twice within a 7-day window), but on very large
|
|
20
|
+
-- `chats` tables a single UPDATE would still hold row locks for the
|
|
21
|
+
-- entire matched set in one transaction. Batched into 1000-row chunks so
|
|
22
|
+
-- each iteration acquires a bounded number of row locks; once a chat is
|
|
23
|
+
-- flipped to `closed: false` it stops matching the predicate, so
|
|
24
|
+
-- subsequent iterations naturally narrow.
|
|
25
|
+
|
|
26
|
+
DO $$
|
|
27
|
+
DECLARE
|
|
28
|
+
rows_updated INT;
|
|
29
|
+
BEGIN
|
|
30
|
+
LOOP
|
|
31
|
+
WITH candidates AS (
|
|
32
|
+
SELECT "id"
|
|
33
|
+
FROM "chats"
|
|
34
|
+
WHERE ("settings"->>'closed')::boolean IS TRUE
|
|
35
|
+
AND "settings"->>'closeOutcome' = 'redirected_sac'
|
|
36
|
+
LIMIT 1000
|
|
37
|
+
)
|
|
38
|
+
UPDATE "chats" AS c
|
|
39
|
+
SET "settings" = c."settings" - 'closed' || jsonb_build_object('closed', false)
|
|
40
|
+
FROM candidates
|
|
41
|
+
WHERE c."id" = candidates."id";
|
|
42
|
+
|
|
43
|
+
GET DIAGNOSTICS rows_updated = ROW_COUNT;
|
|
44
|
+
EXIT WHEN rows_updated = 0;
|
|
45
|
+
END LOOP;
|
|
46
|
+
END
|
|
47
|
+
$$;
|
|
@@ -246,6 +246,13 @@
|
|
|
246
246
|
"when": 1777680000000,
|
|
247
247
|
"tag": "0034_instances_require_genie_signature",
|
|
248
248
|
"breakpoints": true
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"idx": 35,
|
|
252
|
+
"version": "7",
|
|
253
|
+
"when": 1777770000000,
|
|
254
|
+
"tag": "0035_unstick_redirected_sac_terminals",
|
|
255
|
+
"breakpoints": true
|
|
249
256
|
}
|
|
250
257
|
]
|
|
251
258
|
}
|
package/dist/index.js
CHANGED
|
@@ -114079,7 +114079,7 @@ import { fileURLToPath } from "url";
|
|
|
114079
114079
|
// package.json
|
|
114080
114080
|
var package_default = {
|
|
114081
114081
|
name: "@automagik/omni",
|
|
114082
|
-
version: "2.260430.
|
|
114082
|
+
version: "2.260430.13",
|
|
114083
114083
|
description: "LLM-optimized CLI for Omni",
|
|
114084
114084
|
type: "module",
|
|
114085
114085
|
bin: {
|
package/dist/server/index.js
CHANGED
|
@@ -224556,7 +224556,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
224556
224556
|
var require_package8 = __commonJS((exports, module) => {
|
|
224557
224557
|
module.exports = {
|
|
224558
224558
|
name: "@omni/api",
|
|
224559
|
-
version: "2.260430.
|
|
224559
|
+
version: "2.260430.13",
|
|
224560
224560
|
type: "module",
|
|
224561
224561
|
exports: {
|
|
224562
224562
|
".": {
|
|
@@ -280868,6 +280868,21 @@ var init_events2 = __esm(() => {
|
|
|
280868
280868
|
init_drizzle_orm();
|
|
280869
280869
|
});
|
|
280870
280870
|
|
|
280871
|
+
// ../api/src/lib/close-contact-state.ts
|
|
280872
|
+
function isChatInActiveCloseState(settings) {
|
|
280873
|
+
if (!settings)
|
|
280874
|
+
return false;
|
|
280875
|
+
if (settings.closed === true)
|
|
280876
|
+
return true;
|
|
280877
|
+
const closeUntil = settings.closeUntil;
|
|
280878
|
+
if (typeof closeUntil === "string" && closeUntil.length > 0) {
|
|
280879
|
+
const ms = new Date(closeUntil).getTime();
|
|
280880
|
+
if (Number.isFinite(ms) && Date.now() < ms)
|
|
280881
|
+
return true;
|
|
280882
|
+
}
|
|
280883
|
+
return false;
|
|
280884
|
+
}
|
|
280885
|
+
|
|
280871
280886
|
// ../api/src/services/follow-up-lifecycle.ts
|
|
280872
280887
|
function readAgentFollowUpConfig(row) {
|
|
280873
280888
|
return row?.followUpConfig;
|
|
@@ -280907,6 +280922,8 @@ class FollowUpLifecycleService {
|
|
|
280907
280922
|
async armForOutbound(input) {
|
|
280908
280923
|
if (!this.eventBus)
|
|
280909
280924
|
return;
|
|
280925
|
+
if (await this.isInActiveCloseState(input.chatId, input.instanceId))
|
|
280926
|
+
return;
|
|
280910
280927
|
const config4 = input.config ?? await this.resolveConfig(input.chatId, input.instanceId, input.agentId ?? null);
|
|
280911
280928
|
if (!config4 || config4.enabled === false)
|
|
280912
280929
|
return;
|
|
@@ -280925,22 +280942,8 @@ class FollowUpLifecycleService {
|
|
|
280925
280942
|
return;
|
|
280926
280943
|
}
|
|
280927
280944
|
}
|
|
280928
|
-
|
|
280929
|
-
|
|
280930
|
-
const lastInbound = existing.lastInboundCustomerMessageAt?.getTime() ?? 0;
|
|
280931
|
-
const disarmedAt = existing.disarmedAt.getTime();
|
|
280932
|
-
if (lastInbound <= disarmedAt) {
|
|
280933
|
-
this.logger.info("follow-up lifecycle: refusing to arm \u2014 terminal disarm awaiting customer return", {
|
|
280934
|
-
chatId: input.chatId,
|
|
280935
|
-
instanceId: input.instanceId,
|
|
280936
|
-
disarmReason: existing.disarmReason,
|
|
280937
|
-
disarmedAt: existing.disarmedAt.toISOString(),
|
|
280938
|
-
lastAgentMessageAt: input.lastAgentMessageAt.toISOString(),
|
|
280939
|
-
lastInboundCustomerMessageAt: existing.lastInboundCustomerMessageAt?.toISOString() ?? null
|
|
280940
|
-
});
|
|
280941
|
-
return;
|
|
280942
|
-
}
|
|
280943
|
-
}
|
|
280945
|
+
if (await this.shouldRefuseForTerminalDisarm(input))
|
|
280946
|
+
return;
|
|
280944
280947
|
try {
|
|
280945
280948
|
await armSequence({ repo: this.repo, eventBus: this.eventBus, logger: this.logger }, {
|
|
280946
280949
|
chatId: input.chatId,
|
|
@@ -280982,6 +280985,40 @@ class FollowUpLifecycleService {
|
|
|
280982
280985
|
});
|
|
280983
280986
|
}
|
|
280984
280987
|
}
|
|
280988
|
+
async shouldRefuseForTerminalDisarm(input) {
|
|
280989
|
+
const existing = await this.readExistingRow(input.chatId, input.instanceId);
|
|
280990
|
+
if (!existing?.disarmReason || !existing.disarmedAt)
|
|
280991
|
+
return false;
|
|
280992
|
+
if (!TERMINAL_DISARM_REASONS.has(existing.disarmReason))
|
|
280993
|
+
return false;
|
|
280994
|
+
const lastInbound = existing.lastInboundCustomerMessageAt?.getTime() ?? 0;
|
|
280995
|
+
if (lastInbound > existing.disarmedAt.getTime())
|
|
280996
|
+
return false;
|
|
280997
|
+
this.logger.info("follow-up lifecycle: refusing to arm \u2014 terminal disarm awaiting customer return", {
|
|
280998
|
+
chatId: input.chatId,
|
|
280999
|
+
instanceId: input.instanceId,
|
|
281000
|
+
disarmReason: existing.disarmReason,
|
|
281001
|
+
disarmedAt: existing.disarmedAt.toISOString(),
|
|
281002
|
+
lastAgentMessageAt: input.lastAgentMessageAt.toISOString(),
|
|
281003
|
+
lastInboundCustomerMessageAt: existing.lastInboundCustomerMessageAt?.toISOString() ?? null
|
|
281004
|
+
});
|
|
281005
|
+
return true;
|
|
281006
|
+
}
|
|
281007
|
+
async isInActiveCloseState(chatId, instanceId) {
|
|
281008
|
+
const [chatRow] = await this.db.select({ settings: chats.settings }).from(chats).where(eq(chats.id, chatId)).limit(1);
|
|
281009
|
+
const settings = chatRow?.settings;
|
|
281010
|
+
if (!isChatInActiveCloseState(settings))
|
|
281011
|
+
return false;
|
|
281012
|
+
const closeOutcome = settings?.closeOutcome;
|
|
281013
|
+
this.logger.info("follow-up lifecycle: refusing to arm \u2014 chat in active close-contact state", {
|
|
281014
|
+
chatId,
|
|
281015
|
+
instanceId,
|
|
281016
|
+
closed: settings?.closed === true,
|
|
281017
|
+
closeUntil: settings?.closeUntil ?? null,
|
|
281018
|
+
closeOutcome: closeOutcome ?? null
|
|
281019
|
+
});
|
|
281020
|
+
return true;
|
|
281021
|
+
}
|
|
280985
281022
|
async upsertArmed(input) {
|
|
280986
281023
|
const values2 = {
|
|
280987
281024
|
chatId: input.chatId,
|
|
@@ -300283,7 +300320,7 @@ var init__close_contact_config = __esm(() => {
|
|
|
300283
300320
|
DEFAULT_CLOSE_CONTACT_CONFIG = {
|
|
300284
300321
|
won: { cooldownMs: null, escalationThreshold: null, escalationWindowMs: null },
|
|
300285
300322
|
lost: { cooldownMs: null, escalationThreshold: null, escalationWindowMs: null },
|
|
300286
|
-
redirected_sac: { cooldownMs: 24 * HOUR, escalationThreshold:
|
|
300323
|
+
redirected_sac: { cooldownMs: 24 * HOUR, escalationThreshold: null, escalationWindowMs: null },
|
|
300287
300324
|
unqualified: { cooldownMs: 7 * DAY, escalationThreshold: 3, escalationWindowMs: 30 * DAY },
|
|
300288
300325
|
no_response: { cooldownMs: 48 * HOUR, escalationThreshold: 3, escalationWindowMs: 30 * DAY },
|
|
300289
300326
|
other: { cooldownMs: 24 * HOUR, escalationThreshold: 2, escalationWindowMs: 14 * DAY }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automagik/omni",
|
|
3
|
-
"version": "2.260430.
|
|
3
|
+
"version": "2.260430.13",
|
|
4
4
|
"description": "LLM-optimized CLI for Omni",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -51,15 +51,15 @@
|
|
|
51
51
|
"qrcode-terminal": "^0.12.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@omni/api": "2.260430.
|
|
55
|
-
"@omni/channel-discord": "2.260430.
|
|
56
|
-
"@omni/channel-gupshup": "2.260430.
|
|
57
|
-
"@omni/channel-sdk": "2.260430.
|
|
58
|
-
"@omni/channel-slack": "2.260430.
|
|
59
|
-
"@omni/channel-telegram": "2.260430.
|
|
60
|
-
"@omni/channel-whatsapp": "2.260430.
|
|
61
|
-
"@omni/core": "2.260430.
|
|
62
|
-
"@omni/sdk": "2.260430.
|
|
54
|
+
"@omni/api": "2.260430.12",
|
|
55
|
+
"@omni/channel-discord": "2.260430.12",
|
|
56
|
+
"@omni/channel-gupshup": "2.260430.12",
|
|
57
|
+
"@omni/channel-sdk": "2.260430.12",
|
|
58
|
+
"@omni/channel-slack": "2.260430.12",
|
|
59
|
+
"@omni/channel-telegram": "2.260430.12",
|
|
60
|
+
"@omni/channel-whatsapp": "2.260430.12",
|
|
61
|
+
"@omni/core": "2.260430.12",
|
|
62
|
+
"@omni/sdk": "2.260430.12",
|
|
63
63
|
"@types/node": "^22.10.3",
|
|
64
64
|
"@types/qrcode-terminal": "^0.12.2",
|
|
65
65
|
"typescript": "^5.7.3"
|