@automagik/omni 2.260409.3 → 2.260409.4
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 +256 -16
- 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.4",
|
|
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.4",
|
|
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(() => {
|
|
@@ -363490,16 +363552,51 @@ var init_turn_events = __esm(() => {
|
|
|
363490
363552
|
});
|
|
363491
363553
|
|
|
363492
363554
|
// ../api/src/routes/v2/turns.ts
|
|
363493
|
-
|
|
363555
|
+
function requireAdmin(c) {
|
|
363556
|
+
const keyData = c.get("apiKey");
|
|
363557
|
+
if (!keyData) {
|
|
363558
|
+
return c.json({ error: { code: "UNAUTHORIZED", message: "API key required" } }, 401);
|
|
363559
|
+
}
|
|
363560
|
+
if (!ApiKeyService.scopeAllows(keyData.scopes, "turns:admin")) {
|
|
363561
|
+
return c.json({ error: { code: "FORBIDDEN", message: "Master key or turns:admin scope required" } }, 403);
|
|
363562
|
+
}
|
|
363563
|
+
return null;
|
|
363564
|
+
}
|
|
363565
|
+
async function resolveOpenTurn(services, keyId, turnId, headerInstanceId, headerChatId) {
|
|
363566
|
+
const byKey = await services.turns.getOpenByApiKey(keyId);
|
|
363567
|
+
if (byKey)
|
|
363568
|
+
return byKey;
|
|
363569
|
+
if (turnId) {
|
|
363570
|
+
const candidate = await services.turns.getById(turnId);
|
|
363571
|
+
if (candidate && candidate.status === "open")
|
|
363572
|
+
return candidate;
|
|
363573
|
+
}
|
|
363574
|
+
let instanceId = headerInstanceId;
|
|
363575
|
+
let chatId = headerChatId;
|
|
363576
|
+
if (!instanceId || !chatId) {
|
|
363577
|
+
const fullKey = await services.apiKeys.getById(keyId);
|
|
363578
|
+
if (fullKey) {
|
|
363579
|
+
instanceId = instanceId ?? fullKey.contextInstanceId ?? undefined;
|
|
363580
|
+
chatId = chatId ?? fullKey.contextChatId ?? undefined;
|
|
363581
|
+
}
|
|
363582
|
+
}
|
|
363583
|
+
if (instanceId && chatId) {
|
|
363584
|
+
return services.turns.getOpen(instanceId, chatId);
|
|
363585
|
+
}
|
|
363586
|
+
return null;
|
|
363587
|
+
}
|
|
363588
|
+
var turnsRoutes, closeTurnSchema, listQuerySchema16, adminForceCloseSchema, bulkCloseSchema;
|
|
363494
363589
|
var init_turns2 = __esm(() => {
|
|
363495
363590
|
init_dist6();
|
|
363496
363591
|
init_dist2();
|
|
363497
363592
|
init_zod();
|
|
363593
|
+
init_api_keys();
|
|
363498
363594
|
init_turn_events();
|
|
363499
363595
|
turnsRoutes = new Hono2;
|
|
363500
363596
|
closeTurnSchema = exports_external.object({
|
|
363501
363597
|
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)")
|
|
363598
|
+
reason: exports_external.string().optional().describe("Close reason (for skip action)"),
|
|
363599
|
+
turnId: exports_external.string().uuid().optional().describe("Explicit turn ID to close (fallback when API key has no associated turn)")
|
|
363503
363600
|
});
|
|
363504
363601
|
turnsRoutes.post("/close", zValidator("json", closeTurnSchema), async (c) => {
|
|
363505
363602
|
const keyData = c.get("apiKey");
|
|
@@ -363508,12 +363605,12 @@ var init_turns2 = __esm(() => {
|
|
|
363508
363605
|
}
|
|
363509
363606
|
const body = c.req.valid("json");
|
|
363510
363607
|
const services = c.get("services");
|
|
363511
|
-
const openTurn = await services.
|
|
363608
|
+
const openTurn = await resolveOpenTurn(services, keyData.id, body.turnId, c.req.header("x-omni-instance"), c.req.header("x-omni-chat"));
|
|
363512
363609
|
if (!openTurn) {
|
|
363513
363610
|
return c.json({
|
|
363514
363611
|
data: {
|
|
363515
363612
|
alreadyClosed: true,
|
|
363516
|
-
message: "No open turn
|
|
363613
|
+
message: "No open turn found"
|
|
363517
363614
|
}
|
|
363518
363615
|
});
|
|
363519
363616
|
}
|
|
@@ -363551,10 +363648,145 @@ var init_turns2 = __esm(() => {
|
|
|
363551
363648
|
}
|
|
363552
363649
|
});
|
|
363553
363650
|
});
|
|
363651
|
+
listQuerySchema16 = exports_external.object({
|
|
363652
|
+
status: exports_external.enum(["open", "done", "timeout"]).optional(),
|
|
363653
|
+
instanceId: exports_external.string().uuid().optional(),
|
|
363654
|
+
chatId: exports_external.string().optional(),
|
|
363655
|
+
agentId: exports_external.string().uuid().optional(),
|
|
363656
|
+
limit: exports_external.coerce.number().int().min(1).max(200).default(50),
|
|
363657
|
+
offset: exports_external.coerce.number().int().min(0).default(0)
|
|
363658
|
+
});
|
|
363659
|
+
adminForceCloseSchema = exports_external.object({
|
|
363660
|
+
reason: exports_external.string().optional()
|
|
363661
|
+
});
|
|
363662
|
+
bulkCloseSchema = exports_external.object({
|
|
363663
|
+
confirm: exports_external.boolean(),
|
|
363664
|
+
reason: exports_external.string().optional()
|
|
363665
|
+
});
|
|
363666
|
+
turnsRoutes.get("/", zValidator("query", listQuerySchema16), async (c) => {
|
|
363667
|
+
const denied = requireAdmin(c);
|
|
363668
|
+
if (denied)
|
|
363669
|
+
return denied;
|
|
363670
|
+
const query = c.req.valid("query");
|
|
363671
|
+
const services = c.get("services");
|
|
363672
|
+
const { items, total } = await services.turns.list({
|
|
363673
|
+
status: query.status,
|
|
363674
|
+
instanceId: query.instanceId,
|
|
363675
|
+
chatId: query.chatId,
|
|
363676
|
+
agentId: query.agentId,
|
|
363677
|
+
limit: query.limit,
|
|
363678
|
+
offset: query.offset
|
|
363679
|
+
});
|
|
363680
|
+
return c.json({
|
|
363681
|
+
data: {
|
|
363682
|
+
items: items.map((t) => ({
|
|
363683
|
+
id: t.id,
|
|
363684
|
+
instanceId: t.instanceId,
|
|
363685
|
+
chatId: t.chatId,
|
|
363686
|
+
messageId: t.messageId,
|
|
363687
|
+
agentId: t.agentId,
|
|
363688
|
+
apiKeyId: t.apiKeyId,
|
|
363689
|
+
status: t.status,
|
|
363690
|
+
action: t.action,
|
|
363691
|
+
nudgeCount: t.nudgeCount,
|
|
363692
|
+
messagesSent: t.messagesSent,
|
|
363693
|
+
startedAt: t.startedAt.toISOString(),
|
|
363694
|
+
lastActivityAt: t.lastActivityAt.toISOString(),
|
|
363695
|
+
closedAt: t.closedAt?.toISOString() ?? null,
|
|
363696
|
+
closedReason: t.closedReason
|
|
363697
|
+
})),
|
|
363698
|
+
total,
|
|
363699
|
+
limit: query.limit,
|
|
363700
|
+
offset: query.offset
|
|
363701
|
+
}
|
|
363702
|
+
});
|
|
363703
|
+
});
|
|
363704
|
+
turnsRoutes.get("/stats", async (c) => {
|
|
363705
|
+
const denied = requireAdmin(c);
|
|
363706
|
+
if (denied)
|
|
363707
|
+
return denied;
|
|
363708
|
+
const services = c.get("services");
|
|
363709
|
+
const stats = await services.turns.stats();
|
|
363710
|
+
return c.json({ data: stats });
|
|
363711
|
+
});
|
|
363712
|
+
turnsRoutes.get("/:id", async (c) => {
|
|
363713
|
+
const denied = requireAdmin(c);
|
|
363714
|
+
if (denied)
|
|
363715
|
+
return denied;
|
|
363716
|
+
const services = c.get("services");
|
|
363717
|
+
const turn = await services.turns.getById(c.req.param("id"));
|
|
363718
|
+
if (!turn) {
|
|
363719
|
+
return c.json({ error: { code: "NOT_FOUND", message: "Turn not found" } }, 404);
|
|
363720
|
+
}
|
|
363721
|
+
return c.json({
|
|
363722
|
+
data: {
|
|
363723
|
+
id: turn.id,
|
|
363724
|
+
instanceId: turn.instanceId,
|
|
363725
|
+
chatId: turn.chatId,
|
|
363726
|
+
messageId: turn.messageId,
|
|
363727
|
+
agentId: turn.agentId,
|
|
363728
|
+
apiKeyId: turn.apiKeyId,
|
|
363729
|
+
status: turn.status,
|
|
363730
|
+
action: turn.action,
|
|
363731
|
+
nudgeCount: turn.nudgeCount,
|
|
363732
|
+
messagesSent: turn.messagesSent,
|
|
363733
|
+
startedAt: turn.startedAt.toISOString(),
|
|
363734
|
+
lastActivityAt: turn.lastActivityAt.toISOString(),
|
|
363735
|
+
closedAt: turn.closedAt?.toISOString() ?? null,
|
|
363736
|
+
closedReason: turn.closedReason,
|
|
363737
|
+
metadata: turn.metadata
|
|
363738
|
+
}
|
|
363739
|
+
});
|
|
363740
|
+
});
|
|
363741
|
+
turnsRoutes.post("/:id/close", zValidator("json", adminForceCloseSchema), async (c) => {
|
|
363742
|
+
const denied = requireAdmin(c);
|
|
363743
|
+
if (denied)
|
|
363744
|
+
return denied;
|
|
363745
|
+
const services = c.get("services");
|
|
363746
|
+
const body = c.req.valid("json");
|
|
363747
|
+
const turnId = c.req.param("id");
|
|
363748
|
+
const closed = await services.turns.forceClose(turnId, body.reason);
|
|
363749
|
+
if (!closed) {
|
|
363750
|
+
return c.json({ error: { code: "NOT_FOUND", message: "Turn not found or already closed" } }, 404);
|
|
363751
|
+
}
|
|
363752
|
+
const durationMs = closed.closedAt ? closed.closedAt.getTime() - closed.startedAt.getTime() : Date.now() - closed.startedAt.getTime();
|
|
363753
|
+
publishTurnDone(closed.instanceId, closed.chatId, {
|
|
363754
|
+
turnId: closed.id,
|
|
363755
|
+
action: "skip",
|
|
363756
|
+
reason: body.reason ?? "admin force-close",
|
|
363757
|
+
duration: durationMs,
|
|
363758
|
+
nudgeCount: closed.nudgeCount,
|
|
363759
|
+
messagesSent: closed.messagesSent
|
|
363760
|
+
});
|
|
363761
|
+
return c.json({
|
|
363762
|
+
data: {
|
|
363763
|
+
turnId: closed.id,
|
|
363764
|
+
status: closed.status,
|
|
363765
|
+
closedAt: closed.closedAt?.toISOString() ?? null
|
|
363766
|
+
}
|
|
363767
|
+
});
|
|
363768
|
+
});
|
|
363769
|
+
turnsRoutes.post("/close-all", zValidator("json", bulkCloseSchema), async (c) => {
|
|
363770
|
+
const denied = requireAdmin(c);
|
|
363771
|
+
if (denied)
|
|
363772
|
+
return denied;
|
|
363773
|
+
const body = c.req.valid("json");
|
|
363774
|
+
if (!body.confirm) {
|
|
363775
|
+
return c.json({ error: { code: "CONFIRMATION_REQUIRED", message: "Set confirm: true to bulk-close all open turns" } }, 400);
|
|
363776
|
+
}
|
|
363777
|
+
const services = c.get("services");
|
|
363778
|
+
const closedCount = await services.turns.bulkClose(body.reason);
|
|
363779
|
+
return c.json({
|
|
363780
|
+
data: {
|
|
363781
|
+
closedCount,
|
|
363782
|
+
message: `Closed ${closedCount} open turn(s)`
|
|
363783
|
+
}
|
|
363784
|
+
});
|
|
363785
|
+
});
|
|
363554
363786
|
});
|
|
363555
363787
|
|
|
363556
363788
|
// ../api/src/routes/v2/webhooks.ts
|
|
363557
|
-
var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema,
|
|
363789
|
+
var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema, listQuerySchema17, triggerEventSchema;
|
|
363558
363790
|
var init_webhooks3 = __esm(() => {
|
|
363559
363791
|
init_dist6();
|
|
363560
363792
|
init_dist2();
|
|
@@ -363568,10 +363800,10 @@ var init_webhooks3 = __esm(() => {
|
|
|
363568
363800
|
enabled: exports_external.boolean().default(true).describe("Whether source is enabled")
|
|
363569
363801
|
});
|
|
363570
363802
|
updateWebhookSourceSchema = createWebhookSourceSchema.partial();
|
|
363571
|
-
|
|
363803
|
+
listQuerySchema17 = exports_external.object({
|
|
363572
363804
|
enabled: exports_external.coerce.boolean().optional()
|
|
363573
363805
|
});
|
|
363574
|
-
webhooksRoutes.get("/webhook-sources", zValidator("query",
|
|
363806
|
+
webhooksRoutes.get("/webhook-sources", zValidator("query", listQuerySchema17), async (c) => {
|
|
363575
363807
|
const { enabled } = c.req.valid("query");
|
|
363576
363808
|
const services = c.get("services");
|
|
363577
363809
|
const sources = await services.webhooks.list({ enabled });
|
|
@@ -366652,10 +366884,13 @@ async function dispatchViaTurnBasedProvider(services, instance4, provider, trigg
|
|
|
366652
366884
|
agentId: agentRecord.id,
|
|
366653
366885
|
apiKeyId: scopedKey.id
|
|
366654
366886
|
});
|
|
366887
|
+
const contextChat = await services.chats.findByExternalIdSmart(instance4.id, chatId);
|
|
366888
|
+
const contextChatId = contextChat?.id ?? null;
|
|
366889
|
+
const contextMsg = contextChat && messageId ? await services.messages.getByExternalId(contextChat.id, messageId) : null;
|
|
366655
366890
|
await services.apiKeys.update(scopedKey.id, {
|
|
366656
366891
|
contextInstanceId: instance4.id,
|
|
366657
|
-
contextChatId
|
|
366658
|
-
contextMessageId:
|
|
366892
|
+
contextChatId,
|
|
366893
|
+
contextMessageId: contextMsg?.id ?? null
|
|
366659
366894
|
});
|
|
366660
366895
|
publishTurnOpen(instance4.id, chatId, {
|
|
366661
366896
|
turnId: turn.id,
|
|
@@ -367432,7 +367667,8 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
|
|
|
367432
367667
|
const externalChatId = payload.chatId;
|
|
367433
367668
|
const chat2 = await services.chats.findByExternalIdSmart(baseInstance.id, externalChatId);
|
|
367434
367669
|
const internalChatId = chat2?.id;
|
|
367435
|
-
const
|
|
367670
|
+
const reactionPersonId = await resolvePersonId(services, channel4, baseInstance.id, payload.from, metadata.personId);
|
|
367671
|
+
const { instance: instance4, routeId: _routeId } = await resolveEffectiveInstance(services, db2, baseInstance, internalChatId, reactionPersonId);
|
|
367436
367672
|
log98.info("Dispatching reaction trigger", {
|
|
367437
367673
|
instanceId: instance4.id,
|
|
367438
367674
|
chatId: externalChatId,
|
|
@@ -367445,7 +367681,8 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
|
|
|
367445
367681
|
try {
|
|
367446
367682
|
const provider = await getAgentProvider(services, instance4, db2);
|
|
367447
367683
|
if (provider) {
|
|
367448
|
-
const
|
|
367684
|
+
const effectivePersonId = reactionPersonId ?? metadata.personId;
|
|
367685
|
+
const senderName2 = await services.agentRunner.getSenderName(effectivePersonId, undefined);
|
|
367449
367686
|
const sessionId = computeSessionId(instance4.agentSessionStrategy ?? "per_chat", payload.from, externalChatId);
|
|
367450
367687
|
const trigger = {
|
|
367451
367688
|
traceId: metadata.traceId,
|
|
@@ -367459,7 +367696,7 @@ async function processReactionTrigger(services, baseInstance, payload, metadata,
|
|
|
367459
367696
|
},
|
|
367460
367697
|
sender: {
|
|
367461
367698
|
platformUserId: payload.from,
|
|
367462
|
-
personId:
|
|
367699
|
+
personId: effectivePersonId,
|
|
367463
367700
|
displayName: senderName2
|
|
367464
367701
|
},
|
|
367465
367702
|
content: {
|
|
@@ -367902,7 +368139,9 @@ async function setupAgentDispatcher(eventBus, services, db2) {
|
|
|
367902
368139
|
return;
|
|
367903
368140
|
const traceId = metadata.traceId ?? generateCorrelationId("trc");
|
|
367904
368141
|
const chat2 = await services.chats.findByExternalIdSmart(instance4.id, payload.chatId);
|
|
367905
|
-
const
|
|
368142
|
+
const channel4 = metadata.channelType ?? "whatsapp";
|
|
368143
|
+
const earlyPersonId = await resolvePersonId(services, channel4, instance4.id, payload.from, metadata.personId);
|
|
368144
|
+
const { instance: resolved, routeId } = await resolveEffectiveInstance(services, db2, instance4, chat2?.id, earlyPersonId);
|
|
367906
368145
|
const debounceConfig = getDebounceConfig(resolved);
|
|
367907
368146
|
const isGroupChat2 = payload.chatId.includes("@g.us");
|
|
367908
368147
|
const effectiveDebounceConfig = isGroupChat2 && debounceConfig.groupMs != null ? {
|
|
@@ -368007,7 +368246,8 @@ async function setupAgentDispatcher(eventBus, services, db2) {
|
|
|
368007
368246
|
if (!baseInstance?.agentId)
|
|
368008
368247
|
return;
|
|
368009
368248
|
const chat2 = await services.chats.findByExternalIdSmart(metadata.instanceId, payload.chatId);
|
|
368010
|
-
const
|
|
368249
|
+
const typingPersonId = metadata.personId;
|
|
368250
|
+
const { instance: resolved } = await resolveEffectiveInstance(services, db2, baseInstance, chat2?.id, typingPersonId);
|
|
368011
368251
|
const debounceConfig = getDebounceConfig(resolved);
|
|
368012
368252
|
if (debounceConfig.restartOnTyping) {
|
|
368013
368253
|
debouncer.onUserTyping(metadata.instanceId, payload.chatId, debounceConfig);
|
|
@@ -369609,7 +369849,7 @@ class TurnMonitor {
|
|
|
369609
369849
|
log102.info("Turn force-closed (timeout)", { turnId, duration, nudgeCount });
|
|
369610
369850
|
}
|
|
369611
369851
|
}
|
|
369612
|
-
var log102, NUDGE_THRESHOLD_MS =
|
|
369852
|
+
var log102, NUDGE_THRESHOLD_MS = 120000, FALLBACK_THRESHOLD_MS = 600000, TIMEOUT_THRESHOLD_MS = 1800000, POLL_INTERVAL_MS2 = 1e4;
|
|
369613
369853
|
var init_turn_monitor = __esm(() => {
|
|
369614
369854
|
init_src();
|
|
369615
369855
|
init_turn_events();
|