@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.
@@ -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;AA2HpC,wBAAgB,iBAAiB,IAAI,OAAO,CA0C3C"}
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.3",
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 result = await client.turns.close({ action, reason });
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
- error('Specify what to do: omni done "text", omni done --media <file>, omni done --react <emoji>, or omni done --skip');
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",
@@ -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.3",
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
- var turnsRoutes, closeTurnSchema;
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.turns.getOpenByApiKey(keyData.id);
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 for this API key"
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, listQuerySchema16, triggerEventSchema;
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
- listQuerySchema16 = exports_external.object({
363807
+ listQuerySchema17 = exports_external.object({
363572
363808
  enabled: exports_external.coerce.boolean().optional()
363573
363809
  });
363574
- webhooksRoutes.get("/webhook-sources", zValidator("query", listQuerySchema16), async (c) => {
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: chatId,
366658
- contextMessageId: messageId || null
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 { instance: instance4, routeId: _routeId } = await resolveEffectiveInstance(services, db2, baseInstance, internalChatId, metadata.personId);
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 senderName2 = await services.agentRunner.getSenderName(metadata.personId, undefined);
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: metadata.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 { instance: resolved, routeId } = await resolveEffectiveInstance(services, db2, instance4, chat2?.id, metadata.personId);
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 { instance: resolved } = await resolveEffectiveInstance(services, db2, baseInstance, chat2?.id, metadata.personId);
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 = 60000, FALLBACK_THRESHOLD_MS = 300000, TIMEOUT_THRESHOLD_MS = 900000, POLL_INTERVAL_MS2 = 1e4;
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260409.3",
3
+ "version": "2.260409.5",
4
4
  "description": "LLM-optimized CLI for Omni",
5
5
  "type": "module",
6
6
  "bin": {