@automagik/omni 2.260409.3 → 2.260409.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/done.d.ts.map +1 -1
- package/dist/commands/turns.d.ts +12 -0
- package/dist/commands/turns.d.ts.map +1 -0
- package/dist/index.js +155 -5
- package/dist/server/index.js +261 -17
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"done.d.ts","sourceRoot":"","sources":["../../src/commands/done.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"done.d.ts","sourceRoot":"","sources":["../../src/commands/done.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmIpC,wBAAgB,iBAAiB,IAAI,OAAO,CA0C3C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns Commands — Admin turn management
|
|
3
|
+
*
|
|
4
|
+
* omni turns list [--status open|done|timeout] [--instance <id>] [--chat <id>] [--agent <id>] [--limit N]
|
|
5
|
+
* omni turns get <id>
|
|
6
|
+
* omni turns close <id> [--reason <text>]
|
|
7
|
+
* omni turns close-all --confirm [--reason <text>]
|
|
8
|
+
* omni turns stats
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
export declare function createTurnsCommand(): Command;
|
|
12
|
+
//# sourceMappingURL=turns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turns.d.ts","sourceRoot":"","sources":["../../src/commands/turns.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,kBAAkB,IAAI,OAAO,CAiG5C"}
|
package/dist/index.js
CHANGED
|
@@ -76815,16 +76815,82 @@ function createOmniClient(config) {
|
|
|
76815
76815
|
}
|
|
76816
76816
|
},
|
|
76817
76817
|
turns: {
|
|
76818
|
-
async close(body) {
|
|
76818
|
+
async close(body, extraHeaders) {
|
|
76819
76819
|
const resp = await apiFetch(`${baseUrl}/api/v2/turns/close`, {
|
|
76820
76820
|
method: "POST",
|
|
76821
|
-
headers: { "Content-Type": "application/json" },
|
|
76821
|
+
headers: { "Content-Type": "application/json", ...extraHeaders },
|
|
76822
76822
|
body: JSON.stringify(body)
|
|
76823
76823
|
});
|
|
76824
76824
|
const json = await resp.json();
|
|
76825
76825
|
if (!resp.ok)
|
|
76826
76826
|
throw OmniApiError.from(json, resp.status);
|
|
76827
76827
|
return json?.data ?? {};
|
|
76828
|
+
},
|
|
76829
|
+
async list(params) {
|
|
76830
|
+
const qs = new URLSearchParams;
|
|
76831
|
+
if (params?.status)
|
|
76832
|
+
qs.set("status", params.status);
|
|
76833
|
+
if (params?.instanceId)
|
|
76834
|
+
qs.set("instanceId", params.instanceId);
|
|
76835
|
+
if (params?.chatId)
|
|
76836
|
+
qs.set("chatId", params.chatId);
|
|
76837
|
+
if (params?.agentId)
|
|
76838
|
+
qs.set("agentId", params.agentId);
|
|
76839
|
+
if (params?.limit !== undefined)
|
|
76840
|
+
qs.set("limit", String(params.limit));
|
|
76841
|
+
if (params?.offset !== undefined)
|
|
76842
|
+
qs.set("offset", String(params.offset));
|
|
76843
|
+
const query = qs.toString();
|
|
76844
|
+
const url = `${baseUrl}/api/v2/turns${query ? `?${query}` : ""}`;
|
|
76845
|
+
const resp = await apiFetch(url);
|
|
76846
|
+
const json = await resp.json();
|
|
76847
|
+
if (!resp.ok)
|
|
76848
|
+
throw OmniApiError.from(json, resp.status);
|
|
76849
|
+
return json?.data ?? { items: [], total: 0, limit: 50, offset: 0 };
|
|
76850
|
+
},
|
|
76851
|
+
async get(id) {
|
|
76852
|
+
const resp = await apiFetch(`${baseUrl}/api/v2/turns/${id}`);
|
|
76853
|
+
const json = await resp.json();
|
|
76854
|
+
if (!resp.ok)
|
|
76855
|
+
throw OmniApiError.from(json, resp.status);
|
|
76856
|
+
if (!json?.data)
|
|
76857
|
+
throw new OmniApiError("Turn not found", "NOT_FOUND", undefined, 404);
|
|
76858
|
+
return json.data;
|
|
76859
|
+
},
|
|
76860
|
+
async forceClose(id, reason) {
|
|
76861
|
+
const resp = await apiFetch(`${baseUrl}/api/v2/turns/${id}/close`, {
|
|
76862
|
+
method: "POST",
|
|
76863
|
+
headers: { "Content-Type": "application/json" },
|
|
76864
|
+
body: JSON.stringify({ reason })
|
|
76865
|
+
});
|
|
76866
|
+
const json = await resp.json();
|
|
76867
|
+
if (!resp.ok)
|
|
76868
|
+
throw OmniApiError.from(json, resp.status);
|
|
76869
|
+
if (!json?.data)
|
|
76870
|
+
throw new OmniApiError("Turn not found or already closed", "NOT_FOUND", undefined, 404);
|
|
76871
|
+
return json.data;
|
|
76872
|
+
},
|
|
76873
|
+
async bulkClose(reason) {
|
|
76874
|
+
const resp = await apiFetch(`${baseUrl}/api/v2/turns/close-all`, {
|
|
76875
|
+
method: "POST",
|
|
76876
|
+
headers: { "Content-Type": "application/json" },
|
|
76877
|
+
body: JSON.stringify({ confirm: true, reason })
|
|
76878
|
+
});
|
|
76879
|
+
const json = await resp.json();
|
|
76880
|
+
if (!resp.ok)
|
|
76881
|
+
throw OmniApiError.from(json, resp.status);
|
|
76882
|
+
if (!json?.data)
|
|
76883
|
+
throw new OmniApiError("Bulk close failed", "INTERNAL_ERROR", undefined, 500);
|
|
76884
|
+
return json.data;
|
|
76885
|
+
},
|
|
76886
|
+
async stats() {
|
|
76887
|
+
const resp = await apiFetch(`${baseUrl}/api/v2/turns/stats`);
|
|
76888
|
+
const json = await resp.json();
|
|
76889
|
+
if (!resp.ok)
|
|
76890
|
+
throw OmniApiError.from(json, resp.status);
|
|
76891
|
+
if (!json?.data)
|
|
76892
|
+
throw new OmniApiError("Stats unavailable", "INTERNAL_ERROR", undefined, 500);
|
|
76893
|
+
return json.data;
|
|
76828
76894
|
}
|
|
76829
76895
|
},
|
|
76830
76896
|
agents: {
|
|
@@ -76906,7 +76972,7 @@ import { fileURLToPath } from "url";
|
|
|
76906
76972
|
// package.json
|
|
76907
76973
|
var package_default = {
|
|
76908
76974
|
name: "@automagik/omni",
|
|
76909
|
-
version: "2.260409.
|
|
76975
|
+
version: "2.260409.5",
|
|
76910
76976
|
description: "LLM-optimized CLI for Omni",
|
|
76911
76977
|
type: "module",
|
|
76912
76978
|
bin: {
|
|
@@ -80700,7 +80766,13 @@ function getMediaType(path) {
|
|
|
80700
80766
|
}
|
|
80701
80767
|
async function closeTurn(client, action, successMsg, reason) {
|
|
80702
80768
|
try {
|
|
80703
|
-
const
|
|
80769
|
+
const turnId = process.env.OMNI_TURN_ID;
|
|
80770
|
+
const extraHeaders = {};
|
|
80771
|
+
if (process.env.OMNI_INSTANCE)
|
|
80772
|
+
extraHeaders["x-omni-instance"] = process.env.OMNI_INSTANCE;
|
|
80773
|
+
if (process.env.OMNI_CHAT)
|
|
80774
|
+
extraHeaders["x-omni-chat"] = process.env.OMNI_CHAT;
|
|
80775
|
+
const result = await client.turns.close({ action, reason, turnId }, extraHeaders);
|
|
80704
80776
|
if (result.alreadyClosed) {
|
|
80705
80777
|
success(`${successMsg} Turn already closed (idempotent).`);
|
|
80706
80778
|
} else {
|
|
@@ -80790,7 +80862,7 @@ function createDoneCommand() {
|
|
|
80790
80862
|
return handleMedia(client, ctxWithMessage, options.media, options.caption);
|
|
80791
80863
|
if (text)
|
|
80792
80864
|
return handleText(client, ctxWithMessage, text);
|
|
80793
|
-
|
|
80865
|
+
return handleSkip(client, options.reason);
|
|
80794
80866
|
});
|
|
80795
80867
|
}
|
|
80796
80868
|
|
|
@@ -95594,6 +95666,78 @@ function createTtsCommand() {
|
|
|
95594
95666
|
return tts;
|
|
95595
95667
|
}
|
|
95596
95668
|
|
|
95669
|
+
// src/commands/turns.ts
|
|
95670
|
+
init_output();
|
|
95671
|
+
function formatDuration2(startedAt, closedAt) {
|
|
95672
|
+
const start = new Date(startedAt).getTime();
|
|
95673
|
+
const end = closedAt ? new Date(closedAt).getTime() : Date.now();
|
|
95674
|
+
const ms = end - start;
|
|
95675
|
+
if (ms < 1000)
|
|
95676
|
+
return `${ms}ms`;
|
|
95677
|
+
if (ms < 60000)
|
|
95678
|
+
return `${Math.round(ms / 1000)}s`;
|
|
95679
|
+
return `${Math.round(ms / 60000)}m`;
|
|
95680
|
+
}
|
|
95681
|
+
function createTurnsCommand() {
|
|
95682
|
+
const cmd = new Command("turns").description("Admin turn management");
|
|
95683
|
+
cmd.command("list").description("List turns with optional filters").option("--status <status>", "Filter by status (open, done, timeout)").option("--instance <id>", "Filter by instance ID").option("--chat <id>", "Filter by chat ID").option("--agent <id>", "Filter by agent ID").option("--limit <n>", "Max results (default 50)", "50").option("--offset <n>", "Offset for pagination (default 0)", "0").action(async (opts) => {
|
|
95684
|
+
const client = getClient();
|
|
95685
|
+
const result = await client.turns.list({
|
|
95686
|
+
status: opts.status,
|
|
95687
|
+
instanceId: opts.instance,
|
|
95688
|
+
chatId: opts.chat,
|
|
95689
|
+
agentId: opts.agent,
|
|
95690
|
+
limit: Number(opts.limit),
|
|
95691
|
+
offset: Number(opts.offset)
|
|
95692
|
+
});
|
|
95693
|
+
const rows = result.items.map((t) => ({
|
|
95694
|
+
id: t.id.slice(0, 8),
|
|
95695
|
+
status: t.status,
|
|
95696
|
+
chat: t.chatId.length > 20 ? `${t.chatId.slice(0, 20)}\u2026` : t.chatId,
|
|
95697
|
+
agent: t.agentId.slice(0, 8),
|
|
95698
|
+
duration: formatDuration2(t.startedAt, t.closedAt),
|
|
95699
|
+
nudges: t.nudgeCount,
|
|
95700
|
+
msgs: t.messagesSent
|
|
95701
|
+
}));
|
|
95702
|
+
list(rows, {
|
|
95703
|
+
emptyMessage: "No turns found.",
|
|
95704
|
+
rawData: result.items
|
|
95705
|
+
});
|
|
95706
|
+
if (getCurrentFormat() !== "json") {
|
|
95707
|
+
dim(`Total: ${result.total} | Showing ${result.offset + 1}\u2013${result.offset + result.items.length}`);
|
|
95708
|
+
}
|
|
95709
|
+
});
|
|
95710
|
+
cmd.command("get").description("Get a single turn by ID").argument("<id>", "Turn ID").action(async (id) => {
|
|
95711
|
+
const client = getClient();
|
|
95712
|
+
const turn = await client.turns.get(id);
|
|
95713
|
+
data(turn);
|
|
95714
|
+
});
|
|
95715
|
+
cmd.command("close").description("Admin force-close a turn").argument("<id>", "Turn ID to close").option("--reason <text>", "Close reason").action(async (id, opts) => {
|
|
95716
|
+
const client = getClient();
|
|
95717
|
+
const result = await client.turns.forceClose(id, opts.reason);
|
|
95718
|
+
success(`Turn ${result.turnId} closed`, result);
|
|
95719
|
+
});
|
|
95720
|
+
cmd.command("close-all").description("Bulk close all open turns (requires --confirm)").option("--confirm", "Confirm bulk close").option("--reason <text>", "Close reason").action(async (opts) => {
|
|
95721
|
+
if (!opts.confirm) {
|
|
95722
|
+
error("Bulk close requires --confirm flag to proceed.");
|
|
95723
|
+
}
|
|
95724
|
+
const client = getClient();
|
|
95725
|
+
const result = await client.turns.bulkClose(opts.reason);
|
|
95726
|
+
success(result.message, result);
|
|
95727
|
+
});
|
|
95728
|
+
cmd.command("stats").description("Show aggregate turn metrics").action(async () => {
|
|
95729
|
+
const client = getClient();
|
|
95730
|
+
const stats = await client.turns.stats();
|
|
95731
|
+
data({
|
|
95732
|
+
openCount: stats.openCount,
|
|
95733
|
+
totalCount: stats.totalCount,
|
|
95734
|
+
avgDurationMs: stats.avgDurationMs,
|
|
95735
|
+
timeoutRate: `${(stats.timeoutRate * 100).toFixed(1)}%`
|
|
95736
|
+
});
|
|
95737
|
+
});
|
|
95738
|
+
return cmd;
|
|
95739
|
+
}
|
|
95740
|
+
|
|
95597
95741
|
// src/commands/update.ts
|
|
95598
95742
|
import { createInterface as createInterface4 } from "readline";
|
|
95599
95743
|
init_source();
|
|
@@ -96538,6 +96682,12 @@ var COMMANDS = [
|
|
|
96538
96682
|
helpGroup: "Management",
|
|
96539
96683
|
helpDescription: "Webhook management"
|
|
96540
96684
|
},
|
|
96685
|
+
{
|
|
96686
|
+
create: createTurnsCommand,
|
|
96687
|
+
category: "advanced",
|
|
96688
|
+
helpGroup: "Management",
|
|
96689
|
+
helpDescription: "Admin turn management (list, close, stats)"
|
|
96690
|
+
},
|
|
96541
96691
|
{
|
|
96542
96692
|
create: createStatusCommand,
|
|
96543
96693
|
category: "core",
|
package/dist/server/index.js
CHANGED
|
@@ -222829,7 +222829,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
222829
222829
|
var require_package8 = __commonJS((exports, module) => {
|
|
222830
222830
|
module.exports = {
|
|
222831
222831
|
name: "@omni/api",
|
|
222832
|
-
version: "2.260409.
|
|
222832
|
+
version: "2.260409.5",
|
|
222833
222833
|
type: "module",
|
|
222834
222834
|
exports: {
|
|
222835
222835
|
".": {
|
|
@@ -238212,6 +238212,11 @@ var init_scopes = __esm(() => {
|
|
|
238212
238212
|
"PATCH /settings": "settings:write",
|
|
238213
238213
|
"DELETE /settings/:key": "settings:write",
|
|
238214
238214
|
"GET /settings/:key/history": "settings:read",
|
|
238215
|
+
"GET /turns": "turns:admin",
|
|
238216
|
+
"GET /turns/stats": "turns:admin",
|
|
238217
|
+
"GET /turns/:id": "turns:admin",
|
|
238218
|
+
"POST /turns/:id/close": "turns:admin",
|
|
238219
|
+
"POST /turns/close-all": "turns:admin",
|
|
238215
238220
|
"POST /turns/close": "turns:close",
|
|
238216
238221
|
"GET /webhook-sources": "webhooks:read",
|
|
238217
238222
|
"GET /webhook-sources/:id": "webhooks:read",
|
|
@@ -349377,6 +349382,63 @@ class TurnService {
|
|
|
349377
349382
|
async incrementMessages(turnId) {
|
|
349378
349383
|
await this.db.update(turns).set({ messagesSent: sql`${turns.messagesSent} + 1` }).where(eq(turns.id, turnId));
|
|
349379
349384
|
}
|
|
349385
|
+
async list(options) {
|
|
349386
|
+
const conditions3 = [];
|
|
349387
|
+
if (options.status)
|
|
349388
|
+
conditions3.push(eq(turns.status, options.status));
|
|
349389
|
+
if (options.instanceId)
|
|
349390
|
+
conditions3.push(eq(turns.instanceId, options.instanceId));
|
|
349391
|
+
if (options.chatId)
|
|
349392
|
+
conditions3.push(eq(turns.chatId, options.chatId));
|
|
349393
|
+
if (options.agentId)
|
|
349394
|
+
conditions3.push(eq(turns.agentId, options.agentId));
|
|
349395
|
+
const where = conditions3.length > 0 ? and2(...conditions3) : undefined;
|
|
349396
|
+
const [items, [totalRow]] = await Promise.all([
|
|
349397
|
+
this.db.select().from(turns).where(where).orderBy(sql`${turns.startedAt} DESC`).limit(options.limit).offset(options.offset),
|
|
349398
|
+
this.db.select({ count: count2() }).from(turns).where(where)
|
|
349399
|
+
]);
|
|
349400
|
+
return { items, total: totalRow?.count ?? 0 };
|
|
349401
|
+
}
|
|
349402
|
+
async stats() {
|
|
349403
|
+
const [row] = await this.db.select({
|
|
349404
|
+
totalCount: count2(),
|
|
349405
|
+
openCount: sql`count(*) filter (where ${turns.status} = 'open')`,
|
|
349406
|
+
timeoutCount: sql`count(*) filter (where ${turns.status} = 'timeout')`,
|
|
349407
|
+
avgDurationMs: sql`coalesce(avg(extract(epoch from (${turns.closedAt} - ${turns.startedAt})) * 1000) filter (where ${turns.closedAt} is not null), 0)`
|
|
349408
|
+
}).from(turns);
|
|
349409
|
+
const total = row?.totalCount ?? 0;
|
|
349410
|
+
const timeoutCount = Number(row?.timeoutCount ?? 0);
|
|
349411
|
+
return {
|
|
349412
|
+
openCount: Number(row?.openCount ?? 0),
|
|
349413
|
+
totalCount: total,
|
|
349414
|
+
avgDurationMs: Math.round(Number(row?.avgDurationMs ?? 0)),
|
|
349415
|
+
timeoutRate: total > 0 ? timeoutCount / total : 0
|
|
349416
|
+
};
|
|
349417
|
+
}
|
|
349418
|
+
async forceClose(turnId, reason) {
|
|
349419
|
+
const now = new Date;
|
|
349420
|
+
const [closed] = await this.db.update(turns).set({
|
|
349421
|
+
status: "done",
|
|
349422
|
+
action: "skip",
|
|
349423
|
+
closedAt: now,
|
|
349424
|
+
closedReason: reason ?? "admin force-close"
|
|
349425
|
+
}).where(and2(eq(turns.id, turnId), eq(turns.status, "open"))).returning();
|
|
349426
|
+
if (closed) {
|
|
349427
|
+
log83.info("Turn force-closed by admin", { turnId });
|
|
349428
|
+
}
|
|
349429
|
+
return closed ?? null;
|
|
349430
|
+
}
|
|
349431
|
+
async bulkClose(reason) {
|
|
349432
|
+
const now = new Date;
|
|
349433
|
+
const closed = await this.db.update(turns).set({
|
|
349434
|
+
status: "done",
|
|
349435
|
+
action: "skip",
|
|
349436
|
+
closedAt: now,
|
|
349437
|
+
closedReason: reason ?? "admin bulk close"
|
|
349438
|
+
}).where(eq(turns.status, "open")).returning({ id: turns.id });
|
|
349439
|
+
log83.info("Bulk close completed", { closedCount: closed.length });
|
|
349440
|
+
return closed.length;
|
|
349441
|
+
}
|
|
349380
349442
|
}
|
|
349381
349443
|
var log83;
|
|
349382
349444
|
var init_turns = __esm(() => {
|
|
@@ -362013,11 +362075,14 @@ var init_messages5 = __esm(() => {
|
|
|
362013
362075
|
});
|
|
362014
362076
|
}
|
|
362015
362077
|
const resolvedTo = await resolveRecipient(to, instance4.channel, services);
|
|
362078
|
+
let fromMe = false;
|
|
362016
362079
|
const chat2 = await services.chats.findByExternalIdSmart(instanceId, resolvedTo);
|
|
362017
362080
|
if (chat2) {
|
|
362018
362081
|
const target = await services.messages.getByExternalId(chat2.id, messageId);
|
|
362019
362082
|
if (!target) {
|
|
362020
362083
|
log90.warn("Target message not found in DB, sending reaction anyway", { messageId, chatId: chat2.id });
|
|
362084
|
+
} else {
|
|
362085
|
+
fromMe = target.isFromMe === true;
|
|
362021
362086
|
}
|
|
362022
362087
|
}
|
|
362023
362088
|
const outgoingMessage = {
|
|
@@ -362026,7 +362091,8 @@ var init_messages5 = __esm(() => {
|
|
|
362026
362091
|
type: "reaction",
|
|
362027
362092
|
emoji,
|
|
362028
362093
|
targetMessageId: messageId
|
|
362029
|
-
}
|
|
362094
|
+
},
|
|
362095
|
+
metadata: { fromMe }
|
|
362030
362096
|
};
|
|
362031
362097
|
const result = await plugin6.sendMessage(instanceId, outgoingMessage);
|
|
362032
362098
|
if (!result.success) {
|
|
@@ -363490,16 +363556,51 @@ var init_turn_events = __esm(() => {
|
|
|
363490
363556
|
});
|
|
363491
363557
|
|
|
363492
363558
|
// ../api/src/routes/v2/turns.ts
|
|
363493
|
-
|
|
363559
|
+
function requireAdmin(c) {
|
|
363560
|
+
const keyData = c.get("apiKey");
|
|
363561
|
+
if (!keyData) {
|
|
363562
|
+
return c.json({ error: { code: "UNAUTHORIZED", message: "API key required" } }, 401);
|
|
363563
|
+
}
|
|
363564
|
+
if (!ApiKeyService.scopeAllows(keyData.scopes, "turns:admin")) {
|
|
363565
|
+
return c.json({ error: { code: "FORBIDDEN", message: "Master key or turns:admin scope required" } }, 403);
|
|
363566
|
+
}
|
|
363567
|
+
return null;
|
|
363568
|
+
}
|
|
363569
|
+
async function resolveOpenTurn(services, keyId, turnId, headerInstanceId, headerChatId) {
|
|
363570
|
+
const byKey = await services.turns.getOpenByApiKey(keyId);
|
|
363571
|
+
if (byKey)
|
|
363572
|
+
return byKey;
|
|
363573
|
+
if (turnId) {
|
|
363574
|
+
const candidate = await services.turns.getById(turnId);
|
|
363575
|
+
if (candidate && candidate.status === "open")
|
|
363576
|
+
return candidate;
|
|
363577
|
+
}
|
|
363578
|
+
let instanceId = headerInstanceId;
|
|
363579
|
+
let chatId = headerChatId;
|
|
363580
|
+
if (!instanceId || !chatId) {
|
|
363581
|
+
const fullKey = await services.apiKeys.getById(keyId);
|
|
363582
|
+
if (fullKey) {
|
|
363583
|
+
instanceId = instanceId ?? fullKey.contextInstanceId ?? undefined;
|
|
363584
|
+
chatId = chatId ?? fullKey.contextChatId ?? undefined;
|
|
363585
|
+
}
|
|
363586
|
+
}
|
|
363587
|
+
if (instanceId && chatId) {
|
|
363588
|
+
return services.turns.getOpen(instanceId, chatId);
|
|
363589
|
+
}
|
|
363590
|
+
return null;
|
|
363591
|
+
}
|
|
363592
|
+
var turnsRoutes, closeTurnSchema, listQuerySchema16, adminForceCloseSchema, bulkCloseSchema;
|
|
363494
363593
|
var init_turns2 = __esm(() => {
|
|
363495
363594
|
init_dist6();
|
|
363496
363595
|
init_dist2();
|
|
363497
363596
|
init_zod();
|
|
363597
|
+
init_api_keys();
|
|
363498
363598
|
init_turn_events();
|
|
363499
363599
|
turnsRoutes = new Hono2;
|
|
363500
363600
|
closeTurnSchema = exports_external.object({
|
|
363501
363601
|
action: exports_external.enum(["message", "react", "skip"]).describe("How the turn was closed"),
|
|
363502
|
-
reason: exports_external.string().optional().describe("Close reason (for skip action)")
|
|
363602
|
+
reason: exports_external.string().optional().describe("Close reason (for skip action)"),
|
|
363603
|
+
turnId: exports_external.string().uuid().optional().describe("Explicit turn ID to close (fallback when API key has no associated turn)")
|
|
363503
363604
|
});
|
|
363504
363605
|
turnsRoutes.post("/close", zValidator("json", closeTurnSchema), async (c) => {
|
|
363505
363606
|
const keyData = c.get("apiKey");
|
|
@@ -363508,12 +363609,12 @@ var init_turns2 = __esm(() => {
|
|
|
363508
363609
|
}
|
|
363509
363610
|
const body = c.req.valid("json");
|
|
363510
363611
|
const services = c.get("services");
|
|
363511
|
-
const openTurn = await services.
|
|
363612
|
+
const openTurn = await resolveOpenTurn(services, keyData.id, body.turnId, c.req.header("x-omni-instance"), c.req.header("x-omni-chat"));
|
|
363512
363613
|
if (!openTurn) {
|
|
363513
363614
|
return c.json({
|
|
363514
363615
|
data: {
|
|
363515
363616
|
alreadyClosed: true,
|
|
363516
|
-
message: "No open turn
|
|
363617
|
+
message: "No open turn found"
|
|
363517
363618
|
}
|
|
363518
363619
|
});
|
|
363519
363620
|
}
|
|
@@ -363551,10 +363652,145 @@ var init_turns2 = __esm(() => {
|
|
|
363551
363652
|
}
|
|
363552
363653
|
});
|
|
363553
363654
|
});
|
|
363655
|
+
listQuerySchema16 = exports_external.object({
|
|
363656
|
+
status: exports_external.enum(["open", "done", "timeout"]).optional(),
|
|
363657
|
+
instanceId: exports_external.string().uuid().optional(),
|
|
363658
|
+
chatId: exports_external.string().optional(),
|
|
363659
|
+
agentId: exports_external.string().uuid().optional(),
|
|
363660
|
+
limit: exports_external.coerce.number().int().min(1).max(200).default(50),
|
|
363661
|
+
offset: exports_external.coerce.number().int().min(0).default(0)
|
|
363662
|
+
});
|
|
363663
|
+
adminForceCloseSchema = exports_external.object({
|
|
363664
|
+
reason: exports_external.string().optional()
|
|
363665
|
+
});
|
|
363666
|
+
bulkCloseSchema = exports_external.object({
|
|
363667
|
+
confirm: exports_external.boolean(),
|
|
363668
|
+
reason: exports_external.string().optional()
|
|
363669
|
+
});
|
|
363670
|
+
turnsRoutes.get("/", zValidator("query", listQuerySchema16), async (c) => {
|
|
363671
|
+
const denied = requireAdmin(c);
|
|
363672
|
+
if (denied)
|
|
363673
|
+
return denied;
|
|
363674
|
+
const query = c.req.valid("query");
|
|
363675
|
+
const services = c.get("services");
|
|
363676
|
+
const { items, total } = await services.turns.list({
|
|
363677
|
+
status: query.status,
|
|
363678
|
+
instanceId: query.instanceId,
|
|
363679
|
+
chatId: query.chatId,
|
|
363680
|
+
agentId: query.agentId,
|
|
363681
|
+
limit: query.limit,
|
|
363682
|
+
offset: query.offset
|
|
363683
|
+
});
|
|
363684
|
+
return c.json({
|
|
363685
|
+
data: {
|
|
363686
|
+
items: items.map((t) => ({
|
|
363687
|
+
id: t.id,
|
|
363688
|
+
instanceId: t.instanceId,
|
|
363689
|
+
chatId: t.chatId,
|
|
363690
|
+
messageId: t.messageId,
|
|
363691
|
+
agentId: t.agentId,
|
|
363692
|
+
apiKeyId: t.apiKeyId,
|
|
363693
|
+
status: t.status,
|
|
363694
|
+
action: t.action,
|
|
363695
|
+
nudgeCount: t.nudgeCount,
|
|
363696
|
+
messagesSent: t.messagesSent,
|
|
363697
|
+
startedAt: t.startedAt.toISOString(),
|
|
363698
|
+
lastActivityAt: t.lastActivityAt.toISOString(),
|
|
363699
|
+
closedAt: t.closedAt?.toISOString() ?? null,
|
|
363700
|
+
closedReason: t.closedReason
|
|
363701
|
+
})),
|
|
363702
|
+
total,
|
|
363703
|
+
limit: query.limit,
|
|
363704
|
+
offset: query.offset
|
|
363705
|
+
}
|
|
363706
|
+
});
|
|
363707
|
+
});
|
|
363708
|
+
turnsRoutes.get("/stats", async (c) => {
|
|
363709
|
+
const denied = requireAdmin(c);
|
|
363710
|
+
if (denied)
|
|
363711
|
+
return denied;
|
|
363712
|
+
const services = c.get("services");
|
|
363713
|
+
const stats = await services.turns.stats();
|
|
363714
|
+
return c.json({ data: stats });
|
|
363715
|
+
});
|
|
363716
|
+
turnsRoutes.get("/:id", async (c) => {
|
|
363717
|
+
const denied = requireAdmin(c);
|
|
363718
|
+
if (denied)
|
|
363719
|
+
return denied;
|
|
363720
|
+
const services = c.get("services");
|
|
363721
|
+
const turn = await services.turns.getById(c.req.param("id"));
|
|
363722
|
+
if (!turn) {
|
|
363723
|
+
return c.json({ error: { code: "NOT_FOUND", message: "Turn not found" } }, 404);
|
|
363724
|
+
}
|
|
363725
|
+
return c.json({
|
|
363726
|
+
data: {
|
|
363727
|
+
id: turn.id,
|
|
363728
|
+
instanceId: turn.instanceId,
|
|
363729
|
+
chatId: turn.chatId,
|
|
363730
|
+
messageId: turn.messageId,
|
|
363731
|
+
agentId: turn.agentId,
|
|
363732
|
+
apiKeyId: turn.apiKeyId,
|
|
363733
|
+
status: turn.status,
|
|
363734
|
+
action: turn.action,
|
|
363735
|
+
nudgeCount: turn.nudgeCount,
|
|
363736
|
+
messagesSent: turn.messagesSent,
|
|
363737
|
+
startedAt: turn.startedAt.toISOString(),
|
|
363738
|
+
lastActivityAt: turn.lastActivityAt.toISOString(),
|
|
363739
|
+
closedAt: turn.closedAt?.toISOString() ?? null,
|
|
363740
|
+
closedReason: turn.closedReason,
|
|
363741
|
+
metadata: turn.metadata
|
|
363742
|
+
}
|
|
363743
|
+
});
|
|
363744
|
+
});
|
|
363745
|
+
turnsRoutes.post("/:id/close", zValidator("json", adminForceCloseSchema), async (c) => {
|
|
363746
|
+
const denied = requireAdmin(c);
|
|
363747
|
+
if (denied)
|
|
363748
|
+
return denied;
|
|
363749
|
+
const services = c.get("services");
|
|
363750
|
+
const body = c.req.valid("json");
|
|
363751
|
+
const turnId = c.req.param("id");
|
|
363752
|
+
const closed = await services.turns.forceClose(turnId, body.reason);
|
|
363753
|
+
if (!closed) {
|
|
363754
|
+
return c.json({ error: { code: "NOT_FOUND", message: "Turn not found or already closed" } }, 404);
|
|
363755
|
+
}
|
|
363756
|
+
const durationMs = closed.closedAt ? closed.closedAt.getTime() - closed.startedAt.getTime() : Date.now() - closed.startedAt.getTime();
|
|
363757
|
+
publishTurnDone(closed.instanceId, closed.chatId, {
|
|
363758
|
+
turnId: closed.id,
|
|
363759
|
+
action: "skip",
|
|
363760
|
+
reason: body.reason ?? "admin force-close",
|
|
363761
|
+
duration: durationMs,
|
|
363762
|
+
nudgeCount: closed.nudgeCount,
|
|
363763
|
+
messagesSent: closed.messagesSent
|
|
363764
|
+
});
|
|
363765
|
+
return c.json({
|
|
363766
|
+
data: {
|
|
363767
|
+
turnId: closed.id,
|
|
363768
|
+
status: closed.status,
|
|
363769
|
+
closedAt: closed.closedAt?.toISOString() ?? null
|
|
363770
|
+
}
|
|
363771
|
+
});
|
|
363772
|
+
});
|
|
363773
|
+
turnsRoutes.post("/close-all", zValidator("json", bulkCloseSchema), async (c) => {
|
|
363774
|
+
const denied = requireAdmin(c);
|
|
363775
|
+
if (denied)
|
|
363776
|
+
return denied;
|
|
363777
|
+
const body = c.req.valid("json");
|
|
363778
|
+
if (!body.confirm) {
|
|
363779
|
+
return c.json({ error: { code: "CONFIRMATION_REQUIRED", message: "Set confirm: true to bulk-close all open turns" } }, 400);
|
|
363780
|
+
}
|
|
363781
|
+
const services = c.get("services");
|
|
363782
|
+
const closedCount = await services.turns.bulkClose(body.reason);
|
|
363783
|
+
return c.json({
|
|
363784
|
+
data: {
|
|
363785
|
+
closedCount,
|
|
363786
|
+
message: `Closed ${closedCount} open turn(s)`
|
|
363787
|
+
}
|
|
363788
|
+
});
|
|
363789
|
+
});
|
|
363554
363790
|
});
|
|
363555
363791
|
|
|
363556
363792
|
// ../api/src/routes/v2/webhooks.ts
|
|
363557
|
-
var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema,
|
|
363793
|
+
var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema, listQuerySchema17, triggerEventSchema;
|
|
363558
363794
|
var init_webhooks3 = __esm(() => {
|
|
363559
363795
|
init_dist6();
|
|
363560
363796
|
init_dist2();
|
|
@@ -363568,10 +363804,10 @@ var init_webhooks3 = __esm(() => {
|
|
|
363568
363804
|
enabled: exports_external.boolean().default(true).describe("Whether source is enabled")
|
|
363569
363805
|
});
|
|
363570
363806
|
updateWebhookSourceSchema = createWebhookSourceSchema.partial();
|
|
363571
|
-
|
|
363807
|
+
listQuerySchema17 = exports_external.object({
|
|
363572
363808
|
enabled: exports_external.coerce.boolean().optional()
|
|
363573
363809
|
});
|
|
363574
|
-
webhooksRoutes.get("/webhook-sources", zValidator("query",
|
|
363810
|
+
webhooksRoutes.get("/webhook-sources", zValidator("query", listQuerySchema17), async (c) => {
|
|
363575
363811
|
const { enabled } = c.req.valid("query");
|
|
363576
363812
|
const services = c.get("services");
|
|
363577
363813
|
const sources = await services.webhooks.list({ enabled });
|
|
@@ -366652,10 +366888,13 @@ async function dispatchViaTurnBasedProvider(services, instance4, provider, trigg
|
|
|
366652
366888
|
agentId: agentRecord.id,
|
|
366653
366889
|
apiKeyId: scopedKey.id
|
|
366654
366890
|
});
|
|
366891
|
+
const contextChat = await services.chats.findByExternalIdSmart(instance4.id, chatId);
|
|
366892
|
+
const contextChatId = contextChat?.id ?? null;
|
|
366893
|
+
const contextMsg = contextChat && messageId ? await services.messages.getByExternalId(contextChat.id, messageId) : null;
|
|
366655
366894
|
await services.apiKeys.update(scopedKey.id, {
|
|
366656
366895
|
contextInstanceId: instance4.id,
|
|
366657
|
-
contextChatId
|
|
366658
|
-
contextMessageId:
|
|
366896
|
+
contextChatId,
|
|
366897
|
+
contextMessageId: contextMsg?.id ?? null
|
|
366659
366898
|
});
|
|
366660
366899
|
publishTurnOpen(instance4.id, chatId, {
|
|
366661
366900
|
turnId: turn.id,
|
|
@@ -367432,7 +367671,8 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
|
|
|
367432
367671
|
const externalChatId = payload.chatId;
|
|
367433
367672
|
const chat2 = await services.chats.findByExternalIdSmart(baseInstance.id, externalChatId);
|
|
367434
367673
|
const internalChatId = chat2?.id;
|
|
367435
|
-
const
|
|
367674
|
+
const reactionPersonId = await resolvePersonId(services, channel4, baseInstance.id, payload.from, metadata.personId);
|
|
367675
|
+
const { instance: instance4, routeId: _routeId } = await resolveEffectiveInstance(services, db2, baseInstance, internalChatId, reactionPersonId);
|
|
367436
367676
|
log98.info("Dispatching reaction trigger", {
|
|
367437
367677
|
instanceId: instance4.id,
|
|
367438
367678
|
chatId: externalChatId,
|
|
@@ -367445,7 +367685,8 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
|
|
|
367445
367685
|
try {
|
|
367446
367686
|
const provider = await getAgentProvider(services, instance4, db2);
|
|
367447
367687
|
if (provider) {
|
|
367448
|
-
const
|
|
367688
|
+
const effectivePersonId = reactionPersonId ?? metadata.personId;
|
|
367689
|
+
const senderName2 = await services.agentRunner.getSenderName(effectivePersonId, undefined);
|
|
367449
367690
|
const sessionId = computeSessionId(instance4.agentSessionStrategy ?? "per_chat", payload.from, externalChatId);
|
|
367450
367691
|
const trigger = {
|
|
367451
367692
|
traceId: metadata.traceId,
|
|
@@ -367459,7 +367700,7 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
|
|
|
367459
367700
|
},
|
|
367460
367701
|
sender: {
|
|
367461
367702
|
platformUserId: payload.from,
|
|
367462
|
-
personId:
|
|
367703
|
+
personId: effectivePersonId,
|
|
367463
367704
|
displayName: senderName2
|
|
367464
367705
|
},
|
|
367465
367706
|
content: {
|
|
@@ -367902,7 +368143,9 @@ async function setupAgentDispatcher(eventBus, services, db2) {
|
|
|
367902
368143
|
return;
|
|
367903
368144
|
const traceId = metadata.traceId ?? generateCorrelationId("trc");
|
|
367904
368145
|
const chat2 = await services.chats.findByExternalIdSmart(instance4.id, payload.chatId);
|
|
367905
|
-
const
|
|
368146
|
+
const channel4 = metadata.channelType ?? "whatsapp";
|
|
368147
|
+
const earlyPersonId = await resolvePersonId(services, channel4, instance4.id, payload.from, metadata.personId);
|
|
368148
|
+
const { instance: resolved, routeId } = await resolveEffectiveInstance(services, db2, instance4, chat2?.id, earlyPersonId);
|
|
367906
368149
|
const debounceConfig = getDebounceConfig(resolved);
|
|
367907
368150
|
const isGroupChat2 = payload.chatId.includes("@g.us");
|
|
367908
368151
|
const effectiveDebounceConfig = isGroupChat2 && debounceConfig.groupMs != null ? {
|
|
@@ -368007,7 +368250,8 @@ async function setupAgentDispatcher(eventBus, services, db2) {
|
|
|
368007
368250
|
if (!baseInstance?.agentId)
|
|
368008
368251
|
return;
|
|
368009
368252
|
const chat2 = await services.chats.findByExternalIdSmart(metadata.instanceId, payload.chatId);
|
|
368010
|
-
const
|
|
368253
|
+
const typingPersonId = metadata.personId;
|
|
368254
|
+
const { instance: resolved } = await resolveEffectiveInstance(services, db2, baseInstance, chat2?.id, typingPersonId);
|
|
368011
368255
|
const debounceConfig = getDebounceConfig(resolved);
|
|
368012
368256
|
if (debounceConfig.restartOnTyping) {
|
|
368013
368257
|
debouncer.onUserTyping(metadata.instanceId, payload.chatId, debounceConfig);
|
|
@@ -369609,7 +369853,7 @@ class TurnMonitor {
|
|
|
369609
369853
|
log102.info("Turn force-closed (timeout)", { turnId, duration, nudgeCount });
|
|
369610
369854
|
}
|
|
369611
369855
|
}
|
|
369612
|
-
var log102, NUDGE_THRESHOLD_MS =
|
|
369856
|
+
var log102, NUDGE_THRESHOLD_MS = 120000, FALLBACK_THRESHOLD_MS = 600000, TIMEOUT_THRESHOLD_MS = 1800000, POLL_INTERVAL_MS2 = 1e4;
|
|
369613
369857
|
var init_turn_monitor = __esm(() => {
|
|
369614
369858
|
init_src();
|
|
369615
369859
|
init_turn_events();
|